diff --git a/ArmVirtPkg/ArmVirt.dsc.inc b/ArmVirtPkg/ArmVirt.dsc.inc index 9ec92930472d..d9abadbe708c 100644 --- a/ArmVirtPkg/ArmVirt.dsc.inc +++ b/ArmVirtPkg/ArmVirt.dsc.inc @@ -290,7 +290,7 @@ [PcdsFixedAtBuild.common] gEfiMdePkgTokenSpaceGuid.PcdMaximumUnicodeStringLength|1000000 gEfiMdePkgTokenSpaceGuid.PcdMaximumAsciiStringLength|1000000 - gEfiMdePkgTokenSpaceGuid.PcdMaximumLinkedListLength|1000000 + gEfiMdePkgTokenSpaceGuid.PcdMaximumLinkedListLength|0 gEfiMdePkgTokenSpaceGuid.PcdSpinLockTimeout|10000000 gEfiMdePkgTokenSpaceGuid.PcdUefiLibMaxPrintBufferSize|320 diff --git a/CryptoPkg/Library/BaseCryptLib/SecCryptLib.inf b/CryptoPkg/Library/BaseCryptLib/SecCryptLib.inf new file mode 100644 index 000000000000..fb792616921e --- /dev/null +++ b/CryptoPkg/Library/BaseCryptLib/SecCryptLib.inf @@ -0,0 +1,68 @@ +## @file +# Cryptographic Library Instance for SEC. +# +# Caution: This module requires additional review when modified. +# This library will have external input - signature. +# This external input must be validated carefully to avoid security issues such as +# buffer overflow or integer overflow. +# +# Copyright (c) 2021, Intel Corporation. All rights reserved.
+# SPDX-License-Identifier: BSD-2-Clause-Patent +# +## + +[Defines] + INF_VERSION = 0x00010005 + BASE_NAME = SecCryptLib + MODULE_UNI_FILE = SecCryptLib.uni + FILE_GUID = 3689D343-0D32-4284-8053-BF10537990E8 + MODULE_TYPE = BASE + VERSION_STRING = 1.0 + LIBRARY_CLASS = BaseCryptLib|SEC + +# +# The following information is for reference only and not required by the build tools. +# +# VALID_ARCHITECTURES = X64 +# + +[Sources] + InternalCryptLib.h + Hash/CryptSha512.c + + SysCall/CrtWrapper.c + SysCall/ConstantTimeClock.c + SysCall/BaseMemAllocation.c + +[Packages] + MdePkg/MdePkg.dec + CryptoPkg/CryptoPkg.dec + +[LibraryClasses] + BaseLib + BaseMemoryLib + MemoryAllocationLib + DebugLib + OpensslLib + IntrinsicLib + +# +# Remove these [BuildOptions] after this library is cleaned up +# +[BuildOptions] + # + # suppress the following warnings so we do not break the build with warnings-as-errors: + # C4090: 'function' : different 'const' qualifiers + # C4718: 'function call' : recursive call has no side effects, deleting + # + MSFT:*_*_*_CC_FLAGS = /wd4090 /wd4718 + + # -JCryptoPkg/Include : To disable the use of the system includes provided by RVCT + # --diag_remark=1 : Reduce severity of "#1-D: last line of file ends without a newline" + RVCT:*_*_ARM_CC_FLAGS = -JCryptoPkg/Include --diag_remark=1 + + GCC:*_CLANG35_*_CC_FLAGS = -std=c99 + GCC:*_CLANG38_*_CC_FLAGS = -std=c99 + GCC:*_CLANGPDB_*_CC_FLAGS = -std=c99 -Wno-error=incompatible-pointer-types + + XCODE:*_*_*_CC_FLAGS = -std=c99 diff --git a/CryptoPkg/Library/BaseCryptLib/SecCryptLib.uni b/CryptoPkg/Library/BaseCryptLib/SecCryptLib.uni new file mode 100644 index 000000000000..aa25c8ce7027 --- /dev/null +++ b/CryptoPkg/Library/BaseCryptLib/SecCryptLib.uni @@ -0,0 +1,25 @@ +// /** @file +// Cryptographic Library Instance for PEIM. +// +// Caution: This module requires additional review when modified. +// This library will have external input - signature. +// This external input must be validated carefully to avoid security issues such as +// buffer overflow or integer overflow. +// +// Note: AES +// functions, RSA external functions, PKCS#7 SignedData sign functions, +// Diffie-Hellman functions, X.509 certificate handler functions, authenticode +// signature verification functions, PEM handler functions, and pseudorandom number +// generator functions are not supported in this instance. +// +// Copyright (c) 2010 - 2020, Intel Corporation. All rights reserved.
+// +// SPDX-License-Identifier: BSD-2-Clause-Patent +// +// **/ + + +#string STR_MODULE_ABSTRACT #language en-US "Cryptographic Library Instance for PEIM" + +#string STR_MODULE_DESCRIPTION #language en-US "Caution: This module requires additional review when modified. This library will have external input - signature. This external input must be validated carefully to avoid security issues such as buffer overflow or integer overflow. Note: AES functions, RSA external functions, PKCS#7 SignedData sign functions, Diffie-Hellman functions, X.509 certificate handler functions, authenticode signature verification functions, PEM handler functions, and pseudorandom number generator functions are not supported in this instance." + diff --git a/MdeModulePkg/Bus/Pci/PciBusDxe/PciBusDxe.inf b/MdeModulePkg/Bus/Pci/PciBusDxe/PciBusDxe.inf index e317169d9c57..91bfcdaa38bc 100644 --- a/MdeModulePkg/Bus/Pci/PciBusDxe/PciBusDxe.inf +++ b/MdeModulePkg/Bus/Pci/PciBusDxe/PciBusDxe.inf @@ -73,6 +73,7 @@ BaseLib UefiDriverEntryPoint DebugLib + TdxProbeLib [Protocols] gEfiPciHotPlugRequestProtocolGuid ## SOMETIMES_PRODUCES diff --git a/MdeModulePkg/Bus/Pci/PciBusDxe/PciEnumerator.c b/MdeModulePkg/Bus/Pci/PciBusDxe/PciEnumerator.c index 8db1ebf8ec41..a1e6db17b702 100644 --- a/MdeModulePkg/Bus/Pci/PciBusDxe/PciEnumerator.c +++ b/MdeModulePkg/Bus/Pci/PciBusDxe/PciEnumerator.c @@ -9,6 +9,8 @@ SPDX-License-Identifier: BSD-2-Clause-Patent #include "PciBus.h" +#include + /** This routine is used to enumerate entire pci bus system in a given platform. @@ -533,6 +535,9 @@ GetMaxOptionRomSize ( UINT32 TempOptionRomSize; MaxOptionRomSize = 0; + if(ProbeTdGuest()) { + return 0; + } // // Go through bridges to reach all devices diff --git a/MdeModulePkg/Core/Dxe/DxeMain.h b/MdeModulePkg/Core/Dxe/DxeMain.h index 9bd3c0d08411..3562d833e707 100644 --- a/MdeModulePkg/Core/Dxe/DxeMain.h +++ b/MdeModulePkg/Core/Dxe/DxeMain.h @@ -289,6 +289,8 @@ extern EFI_RUNTIME_ARCH_PROTOCOL gRuntimeTemplate; extern EFI_LOAD_FIXED_ADDRESS_CONFIGURATION_TABLE gLoadModuleAtFixAddressConfigurationTable; extern BOOLEAN gLoadFixedAddressCodeMemoryReady; + +extern BOOLEAN gTdGuest; // // Service Initialization Functions // diff --git a/MdeModulePkg/Core/Dxe/DxeMain.inf b/MdeModulePkg/Core/Dxe/DxeMain.inf index e4bca895773d..fb2218e062c4 100644 --- a/MdeModulePkg/Core/Dxe/DxeMain.inf +++ b/MdeModulePkg/Core/Dxe/DxeMain.inf @@ -94,6 +94,7 @@ DebugAgentLib CpuExceptionHandlerLib PcdLib + TdxProbeLib [Guids] gEfiEventMemoryMapChangeGuid ## PRODUCES ## Event diff --git a/MdeModulePkg/Core/Dxe/DxeMain/DxeMain.c b/MdeModulePkg/Core/Dxe/DxeMain/DxeMain.c index db21311f9352..e124a5d8428f 100644 --- a/MdeModulePkg/Core/Dxe/DxeMain/DxeMain.c +++ b/MdeModulePkg/Core/Dxe/DxeMain/DxeMain.c @@ -7,6 +7,9 @@ SPDX-License-Identifier: BSD-2-Clause-Patent **/ #include "DxeMain.h" +#include + +BOOLEAN gTdGuest = FALSE; // // DXE Core Global Variables for Protocols from PEI @@ -244,6 +247,11 @@ DxeMain ( EFI_VECTOR_HANDOFF_INFO *VectorInfo; VOID *EntryPoint; + // + // Check whether it is of Td guest + // + gTdGuest = ProbeTdGuest(); + // // Setup the default exception handlers // diff --git a/MdeModulePkg/Core/Dxe/FwVol/FwVolRead.c b/MdeModulePkg/Core/Dxe/FwVol/FwVolRead.c index 8dcbbeb5ee5a..09cf88c200b4 100644 --- a/MdeModulePkg/Core/Dxe/FwVol/FwVolRead.c +++ b/MdeModulePkg/Core/Dxe/FwVol/FwVolRead.c @@ -1,7 +1,7 @@ /** @file Implements functions to read firmware file -Copyright (c) 2006 - 2020, Intel Corporation. All rights reserved.
+Copyright (c) 2006 - 2021, Intel Corporation. All rights reserved.
SPDX-License-Identifier: BSD-2-Clause-Patent **/ @@ -128,6 +128,7 @@ FvGetNextFile ( UINTN *KeyValue; LIST_ENTRY *Link; FFS_FILE_LIST_ENTRY *FfsFileEntry; + UINTN MaxFileType; FvDevice = FV_DEVICE_FROM_THIS (This); @@ -143,7 +144,16 @@ FvGetNextFile ( return EFI_ACCESS_DENIED; } - if (*FileType > EFI_FV_FILETYPE_MM_CORE_STANDALONE) { + // + // Td guest doesn't support SMM + // + if(gTdGuest) { + MaxFileType = EFI_FV_FILETYPE_SMM_CORE; + } else { + MaxFileType = EFI_FV_FILETYPE_MM_CORE_STANDALONE; + } + + if (*FileType > MaxFileType) { // // File type needs to be in 0 - 0x0F // diff --git a/MdeModulePkg/Core/Dxe/Gcd/Gcd.c b/MdeModulePkg/Core/Dxe/Gcd/Gcd.c index 51b082b7e7eb..1b9d591754c7 100644 --- a/MdeModulePkg/Core/Dxe/Gcd/Gcd.c +++ b/MdeModulePkg/Core/Dxe/Gcd/Gcd.c @@ -35,6 +35,14 @@ SPDX-License-Identifier: BSD-2-Clause-Patent #define PRESENT_MEMORY_ATTRIBUTES (EFI_RESOURCE_ATTRIBUTE_PRESENT) +// TDX +#define EXCLUSIVE_MEMORY_ATTRIBUTES (EFI_MEMORY_UC | EFI_MEMORY_WC | \ + EFI_MEMORY_WT | EFI_MEMORY_WB | \ + EFI_MEMORY_WP | EFI_MEMORY_UCE) + +#define NONEXCLUSIVE_MEMORY_ATTRIBUTES (EFI_MEMORY_XP | EFI_MEMORY_RP | \ + EFI_MEMORY_RO) + // // Module Variables // @@ -74,6 +82,9 @@ EFI_GCD_MAP_ENTRY mGcdIoSpaceMapEntryTemplate = { NULL, NULL }; +// TD +#define EFI_RESOURCE_ATTRIBUTE_ENCRYPTED 0x04000000 + GCD_ATTRIBUTE_CONVERSION_ENTRY mAttributeConversionTable[] = { { EFI_RESOURCE_ATTRIBUTE_UNCACHEABLE, EFI_MEMORY_UC, TRUE }, @@ -90,6 +101,7 @@ GCD_ATTRIBUTE_CONVERSION_ENTRY mAttributeConversionTable[] = { { EFI_RESOURCE_ATTRIBUTE_TESTED, EFI_MEMORY_TESTED, FALSE }, { EFI_RESOURCE_ATTRIBUTE_PERSISTABLE, EFI_MEMORY_NV, TRUE }, { EFI_RESOURCE_ATTRIBUTE_MORE_RELIABLE, EFI_MEMORY_MORE_RELIABLE, TRUE }, + { EFI_RESOURCE_ATTRIBUTE_ENCRYPTED, EFI_MEMORY_CPU_CRYPTO, TRUE }, // TDX { 0, 0, FALSE } }; @@ -658,7 +670,11 @@ ConverToCpuArchAttributes ( { UINT64 CpuArchAttributes; - CpuArchAttributes = Attributes & EFI_MEMORY_ATTRIBUTE_MASK; + if(gTdGuest) { + CpuArchAttributes = Attributes & NONEXCLUSIVE_MEMORY_ATTRIBUTES; + } else { + CpuArchAttributes = Attributes & EFI_MEMORY_ATTRIBUTE_MASK; + } if ( (Attributes & EFI_MEMORY_UC) == EFI_MEMORY_UC) { CpuArchAttributes |= EFI_MEMORY_UC; @@ -944,7 +960,11 @@ CoreConvertSpace ( // Keep original CPU arch attributes when caller just calls // SetMemorySpaceAttributes() with none CPU arch attributes (for example, RUNTIME). // - Attributes |= (Entry->Attributes & (EFI_CACHE_ATTRIBUTE_MASK | EFI_MEMORY_ATTRIBUTE_MASK)); + if(gTdGuest) { + Attributes |= (Entry->Attributes & (EXCLUSIVE_MEMORY_ATTRIBUTES | NONEXCLUSIVE_MEMORY_ATTRIBUTES)); + } else { + Attributes |= (Entry->Attributes & (EFI_CACHE_ATTRIBUTE_MASK | EFI_MEMORY_ATTRIBUTE_MASK)); + } } Entry->Attributes = Attributes; break; @@ -2289,7 +2309,9 @@ CoreInitializeMemoryServices ( Attributes = PhitResourceHob->ResourceAttribute; BaseAddress = PageAlignAddress (PhitHob->EfiMemoryTop); Length = PageAlignLength (ResourceHob->PhysicalStart + ResourceHob->ResourceLength - BaseAddress); - FindLargestFreeRegion (&BaseAddress, &Length, (EFI_HOB_MEMORY_ALLOCATION *)GetFirstHob (EFI_HOB_TYPE_MEMORY_ALLOCATION)); + if(!gTdGuest) { + FindLargestFreeRegion (&BaseAddress, &Length, (EFI_HOB_MEMORY_ALLOCATION *)GetFirstHob (EFI_HOB_TYPE_MEMORY_ALLOCATION)); + } if (Length < MinimalMemorySizeNeeded) { // // If that range is not large enough to intialize the DXE Core, then @@ -2305,7 +2327,9 @@ CoreInitializeMemoryServices ( // BaseAddress = PageAlignAddress (ResourceHob->PhysicalStart); Length = PageAlignLength ((UINT64)((UINTN)*HobStart - BaseAddress)); - FindLargestFreeRegion (&BaseAddress, &Length, (EFI_HOB_MEMORY_ALLOCATION *)GetFirstHob (EFI_HOB_TYPE_MEMORY_ALLOCATION)); + if(!gTdGuest) { + FindLargestFreeRegion (&BaseAddress, &Length, (EFI_HOB_MEMORY_ALLOCATION *)GetFirstHob (EFI_HOB_TYPE_MEMORY_ALLOCATION)); + } } } break; @@ -2369,7 +2393,9 @@ CoreInitializeMemoryServices ( // TestedMemoryBaseAddress = PageAlignAddress (ResourceHob->PhysicalStart); TestedMemoryLength = PageAlignLength (ResourceHob->PhysicalStart + ResourceHob->ResourceLength - TestedMemoryBaseAddress); - FindLargestFreeRegion (&TestedMemoryBaseAddress, &TestedMemoryLength, (EFI_HOB_MEMORY_ALLOCATION *)GetFirstHob (EFI_HOB_TYPE_MEMORY_ALLOCATION)); + if(!gTdGuest) { + FindLargestFreeRegion (&TestedMemoryBaseAddress, &TestedMemoryLength, (EFI_HOB_MEMORY_ALLOCATION *)GetFirstHob (EFI_HOB_TYPE_MEMORY_ALLOCATION)); + } if (TestedMemoryLength < MinimalMemorySizeNeeded) { continue; } diff --git a/MdeModulePkg/Core/Dxe/Mem/Page.c b/MdeModulePkg/Core/Dxe/Mem/Page.c index 731bf08bc959..a749ffd6e32e 100644 --- a/MdeModulePkg/Core/Dxe/Mem/Page.c +++ b/MdeModulePkg/Core/Dxe/Mem/Page.c @@ -1856,8 +1856,14 @@ CoreGetMemoryMap ( MemoryMap->PhysicalStart = MergeGcdMapEntry.BaseAddress; MemoryMap->VirtualStart = 0; MemoryMap->NumberOfPages = RShiftU64 ((MergeGcdMapEntry.EndAddress - MergeGcdMapEntry.BaseAddress + 1), EFI_PAGE_SHIFT); - MemoryMap->Attribute = (MergeGcdMapEntry.Attributes & ~EFI_MEMORY_PORT_IO) | + if(gTdGuest) { + MemoryMap->Attribute = (MergeGcdMapEntry.Attributes & ~EFI_MEMORY_PORT_IO) | + (MergeGcdMapEntry.Capabilities & (EFI_MEMORY_RP | EFI_MEMORY_WP | EFI_MEMORY_XP | EFI_MEMORY_RO | + EFI_MEMORY_UC | EFI_MEMORY_UCE | EFI_MEMORY_WC | EFI_MEMORY_WT | EFI_MEMORY_WB)); + } else { + MemoryMap->Attribute = (MergeGcdMapEntry.Attributes & ~EFI_MEMORY_PORT_IO) | (MergeGcdMapEntry.Capabilities & (EFI_CACHE_ATTRIBUTE_MASK | EFI_MEMORY_ATTRIBUTE_MASK)); + } if (MergeGcdMapEntry.GcdMemoryType == EfiGcdMemoryTypeReserved) { MemoryMap->Type = EfiReservedMemoryType; @@ -1890,8 +1896,14 @@ CoreGetMemoryMap ( MemoryMap->PhysicalStart = MergeGcdMapEntry.BaseAddress; MemoryMap->VirtualStart = 0; MemoryMap->NumberOfPages = RShiftU64 ((MergeGcdMapEntry.EndAddress - MergeGcdMapEntry.BaseAddress + 1), EFI_PAGE_SHIFT); - MemoryMap->Attribute = MergeGcdMapEntry.Attributes | EFI_MEMORY_NV | + if(gTdGuest) { + MemoryMap->Attribute = MergeGcdMapEntry.Attributes | EFI_MEMORY_NV | + (MergeGcdMapEntry.Capabilities & (EFI_MEMORY_RP | EFI_MEMORY_WP | EFI_MEMORY_XP | EFI_MEMORY_RO | + EFI_MEMORY_UC | EFI_MEMORY_UCE | EFI_MEMORY_WC | EFI_MEMORY_WT | EFI_MEMORY_WB)); + } else { + MemoryMap->Attribute = MergeGcdMapEntry.Attributes | EFI_MEMORY_NV | (MergeGcdMapEntry.Capabilities & (EFI_CACHE_ATTRIBUTE_MASK | EFI_MEMORY_ATTRIBUTE_MASK)); + } MemoryMap->Type = EfiPersistentMemory; // @@ -1933,7 +1945,12 @@ CoreGetMemoryMap ( MemoryMapEnd = MemoryMap; MemoryMap = MemoryMapStart; while (MemoryMap < MemoryMapEnd) { - MemoryMap->Attribute &= ~(UINT64)EFI_MEMORY_ACCESS_MASK; + if(gTdGuest) { + MemoryMap->Attribute &= ~(UINT64)(EFI_MEMORY_RP | EFI_MEMORY_RO | + EFI_MEMORY_XP); + } else { + MemoryMap->Attribute &= ~(UINT64)EFI_MEMORY_ACCESS_MASK; + } MemoryMap = NEXT_MEMORY_DESCRIPTOR (MemoryMap, Size); } MergeMemoryMap (MemoryMapStart, &BufferSize, Size); diff --git a/MdeModulePkg/Core/Dxe/Misc/MemoryProtection.c b/MdeModulePkg/Core/Dxe/Misc/MemoryProtection.c index 7d1daf0b1938..dfa69cd6d3a1 100644 --- a/MdeModulePkg/Core/Dxe/Misc/MemoryProtection.c +++ b/MdeModulePkg/Core/Dxe/Misc/MemoryProtection.c @@ -42,6 +42,10 @@ SPDX-License-Identifier: BSD-2-Clause-Patent #include "DxeMain.h" #include "Mem/HeapGuard.h" +// TD +#define CACHE_ATTRIBUTE_MASK (EFI_MEMORY_UC | EFI_MEMORY_WC | EFI_MEMORY_WT | EFI_MEMORY_WB | EFI_MEMORY_UCE | EFI_MEMORY_WP) +#define MEMORY_ATTRIBUTE_MASK (EFI_MEMORY_RP | EFI_MEMORY_XP | EFI_MEMORY_RO) + // // Image type definitions // @@ -218,7 +222,11 @@ SetUefiImageMemoryAttributes ( Status = CoreGetMemorySpaceDescriptor(BaseAddress, &Descriptor); ASSERT_EFI_ERROR(Status); - FinalAttributes = (Descriptor.Attributes & EFI_CACHE_ATTRIBUTE_MASK) | (Attributes & EFI_MEMORY_ATTRIBUTE_MASK); + if(gTdGuest) { + FinalAttributes = (Descriptor.Attributes & CACHE_ATTRIBUTE_MASK) | (Attributes & MEMORY_ATTRIBUTE_MASK); + } else { + FinalAttributes = (Descriptor.Attributes & EFI_CACHE_ATTRIBUTE_MASK) | (Attributes & EFI_MEMORY_ATTRIBUTE_MASK); + } DEBUG ((DEBUG_INFO, "SetUefiImageMemoryAttributes - 0x%016lx - 0x%016lx (0x%016lx)\n", BaseAddress, Length, FinalAttributes)); @@ -920,8 +928,13 @@ InitializeDxeNxMemoryProtectionPolicy ( (Entry->Capabilities & (EFI_MEMORY_PRESENT | EFI_MEMORY_INITIALIZED | EFI_MEMORY_TESTED)) == (EFI_MEMORY_PRESENT | EFI_MEMORY_INITIALIZED)) { - Attributes = GetPermissionAttributeForMemoryType (EfiConventionalMemory) | + if(gTdGuest) { + Attributes = GetPermissionAttributeForMemoryType (EfiConventionalMemory) | + (Entry->Attributes & CACHE_ATTRIBUTE_MASK); + } else { + Attributes = GetPermissionAttributeForMemoryType (EfiConventionalMemory) | (Entry->Attributes & EFI_CACHE_ATTRIBUTE_MASK); + } DEBUG ((DEBUG_INFO, "Untested GCD memory space region: - 0x%016lx - 0x%016lx (0x%016lx)\n", diff --git a/MdeModulePkg/Universal/SmbiosDxe/SmbiosDxe.c b/MdeModulePkg/Universal/SmbiosDxe/SmbiosDxe.c index 3cdb0b1ed74f..0afa80823c00 100644 --- a/MdeModulePkg/Universal/SmbiosDxe/SmbiosDxe.c +++ b/MdeModulePkg/Universal/SmbiosDxe/SmbiosDxe.c @@ -8,7 +8,7 @@ SPDX-License-Identifier: BSD-2-Clause-Patent **/ #include "SmbiosDxe.h" - +#include // // Module Global: // Since this driver will only ever produce one instance of the @@ -1428,6 +1428,10 @@ SmbiosDriverEntryPoint ( { EFI_STATUS Status; + if(ProbeTdGuest()) { + return EFI_UNSUPPORTED; + } + mPrivateData.Signature = SMBIOS_INSTANCE_SIGNATURE; mPrivateData.Smbios.Add = SmbiosAdd; mPrivateData.Smbios.UpdateString = SmbiosUpdateString; diff --git a/MdeModulePkg/Universal/SmbiosDxe/SmbiosDxe.inf b/MdeModulePkg/Universal/SmbiosDxe/SmbiosDxe.inf index f6c036e1dcbc..91b9321d544b 100644 --- a/MdeModulePkg/Universal/SmbiosDxe/SmbiosDxe.inf +++ b/MdeModulePkg/Universal/SmbiosDxe/SmbiosDxe.inf @@ -41,6 +41,7 @@ UefiDriverEntryPoint DebugLib PcdLib + TdxProbeLib [Protocols] gEfiSmbiosProtocolGuid ## PRODUCES diff --git a/MdePkg/Include/IndustryStandard/Tdx.h b/MdePkg/Include/IndustryStandard/Tdx.h new file mode 100644 index 000000000000..106c4daef686 --- /dev/null +++ b/MdePkg/Include/IndustryStandard/Tdx.h @@ -0,0 +1,199 @@ +/** @file + Intel Trust Domain Extension definitions + + Copyright (c) 2020 - 2021, Intel Corporation. All rights reserved.
+ SPDX-License-Identifier: BSD-2-Clause-Patent + +**/ + +#ifndef _TDX_H_ +#define _TDX_H_ + +#define EXIT_REASON_EXTERNAL_INTERRUPT 1 +#define EXIT_REASON_TRIPLE_FAULT 2 + +#define EXIT_REASON_PENDING_INTERRUPT 7 +#define EXIT_REASON_NMI_WINDOW 8 +#define EXIT_REASON_TASK_SWITCH 9 +#define EXIT_REASON_CPUID 10 +#define EXIT_REASON_HLT 12 +#define EXIT_REASON_INVD 13 +#define EXIT_REASON_INVLPG 14 +#define EXIT_REASON_RDPMC 15 +#define EXIT_REASON_RDTSC 16 +#define EXIT_REASON_VMCALL 18 +#define EXIT_REASON_VMCLEAR 19 +#define EXIT_REASON_VMLAUNCH 20 +#define EXIT_REASON_VMPTRLD 21 +#define EXIT_REASON_VMPTRST 22 +#define EXIT_REASON_VMREAD 23 +#define EXIT_REASON_VMRESUME 24 +#define EXIT_REASON_VMWRITE 25 +#define EXIT_REASON_VMOFF 26 +#define EXIT_REASON_VMON 27 +#define EXIT_REASON_CR_ACCESS 28 +#define EXIT_REASON_DR_ACCESS 29 +#define EXIT_REASON_IO_INSTRUCTION 30 +#define EXIT_REASON_MSR_READ 31 +#define EXIT_REASON_MSR_WRITE 32 +#define EXIT_REASON_INVALID_STATE 33 +#define EXIT_REASON_MSR_LOAD_FAIL 34 +#define EXIT_REASON_MWAIT_INSTRUCTION 36 +#define EXIT_REASON_MONITOR_TRAP_FLAG 37 +#define EXIT_REASON_MONITOR_INSTRUCTION 39 +#define EXIT_REASON_PAUSE_INSTRUCTION 40 +#define EXIT_REASON_MCE_DURING_VMENTRY 41 +#define EXIT_REASON_TPR_BELOW_THRESHOLD 43 +#define EXIT_REASON_APIC_ACCESS 44 +#define EXIT_REASON_EOI_INDUCED 45 +#define EXIT_REASON_GDTR_IDTR 46 +#define EXIT_REASON_LDTR_TR 47 +#define EXIT_REASON_EPT_VIOLATION 48 +#define EXIT_REASON_EPT_MISCONFIG 49 +#define EXIT_REASON_INVEPT 50 +#define EXIT_REASON_RDTSCP 51 +#define EXIT_REASON_PREEMPTION_TIMER 52 +#define EXIT_REASON_INVVPID 53 +#define EXIT_REASON_WBINVD 54 +#define EXIT_REASON_XSETBV 55 +#define EXIT_REASON_APIC_WRITE 56 +#define EXIT_REASON_RDRAND 57 +#define EXIT_REASON_INVPCID 58 +#define EXIT_REASON_VMFUNC 59 +#define EXIT_REASON_ENCLS 60 +#define EXIT_REASON_RDSEED 61 +#define EXIT_REASON_PML_FULL 62 +#define EXIT_REASON_XSAVES 63 +#define EXIT_REASON_XRSTORS 64 + +// TDCALL API Function Completion Status Codes +#define TDX_EXIT_REASON_SUCCESS 0x0000000000000000 +#define TDX_EXIT_REASON_PAGE_ALREADY_ACCEPTED 0x00000B0A00000000 +#define TDX_EXIT_REASON_OPERAND_INVALID 0xC000010000000000 +#define TDX_EXIT_REASON_OPERAND_BUSY 0x8000020000000000 + +// TDCALL [TDG.MEM.PAGE.ACCEPT] page size +#define TDCALL_ACCEPT_PAGE_SIZE_4K 0 +#define TDCALL_ACCEPT_PAGE_SIZE_2M 1 +#define TDCALL_ACCEPT_PAGE_SIZE_1G 2 + +#define TDCALL_TDVMCALL 0 +#define TDCALL_TDINFO 1 +#define TDCALL_TDEXTENDRTMR 2 +#define TDCALL_TDGETVEINFO 3 +#define TDCALL_TDREPORT 4 +#define TDCALL_TDSETCPUIDVE 5 +#define TDCALL_TDACCEPTPAGE 6 + +#define TDVMCALL_CPUID 0x0000a +#define TDVMCALL_HALT 0x0000c +#define TDVMCALL_IO 0x0001e +#define TDVMCALL_RDMSR 0x0001f +#define TDVMCALL_WRMSR 0x00020 +#define TDVMCALL_MMIO 0x00030 +#define TDVMCALL_PCONFIG 0x00041 + +#define TDVMCALL_GET_TDVMCALL_INFO 0x10000 +#define TDVMCALL_MAPGPA 0x10001 +#define TDVMCALL_GET_QUOTE 0x10002 +#define TDVMCALL_REPORT_FATAL_ERR 0x10003 +#define TDVMCALL_SETUP_EVENT_NOTIFY 0x10004 + +#pragma pack(1) +typedef struct { + UINT64 Data[6]; +} TDCALL_GENERIC_RETURN_DATA; + +typedef struct { + UINT64 Gpaw; + UINT64 Attributes; + UINT32 MaxVcpus; + UINT32 NumVcpus; + UINT64 Resv[3]; +} TDCALL_INFO_RETURN_DATA; + +typedef union { + UINT64 Val; + struct { + UINT32 Size:3; + UINT32 Direction:1; + UINT32 String:1; + UINT32 Rep:1; + UINT32 Encoding:1; + UINT32 Resv:9; + UINT32 Port:16; + UINT32 Resv2; + } Io; +} VMX_EXIT_QUALIFICATION; + +typedef struct { + UINT32 ExitReason; + UINT32 Resv; + VMX_EXIT_QUALIFICATION ExitQualification; + UINT64 GuestLA; + UINT64 GuestPA; + UINT32 ExitInstructionLength; + UINT32 ExitInstructionInfo; + UINT32 Resv1; +} TDCALL_VEINFO_RETURN_DATA; + +typedef union { + TDCALL_GENERIC_RETURN_DATA Generic; + TDCALL_INFO_RETURN_DATA TdInfo; + TDCALL_VEINFO_RETURN_DATA VeInfo; +} TD_RETURN_DATA; + +/* data structure used in TDREPORT_STRUCT */ +typedef struct { + UINT8 Type; + UINT8 Subtype; + UINT8 Version; + UINT8 Rsvd; +} TD_REPORT_TYPE; + +typedef struct { + TD_REPORT_TYPE ReportType; + UINT8 Rsvd1[12]; + UINT8 CpuSvn[16]; + UINT8 TeeTcbInfoHash[48]; + UINT8 TeeInfoHash[48]; + UINT8 ReportData[64]; + UINT8 Rsvd2[32]; + UINT8 Mac[32]; +} REPORTMACSTRUCT; + +typedef struct { + UINT8 Seam[2]; + UINT8 Rsvd[14]; +} TEE_TCB_SVN; + +typedef struct { + UINT8 Valid[8]; + TEE_TCB_SVN TeeTcbSvn; + UINT8 Mrseam[48]; + UINT8 Mrsignerseam[48]; + UINT8 Attributes[8]; + UINT8 Rsvd[111]; +} TEE_TCB_INFO; + +typedef struct { + UINT8 Attributes[8]; + UINT8 Xfam[8]; + UINT8 Mrtd[48]; + UINT8 Mrconfigid[48]; + UINT8 Mrowner[48]; + UINT8 Mrownerconfig[48]; + UINT8 Rtmrs[4][48]; + UINT8 Rsvd[112]; +} TDINFO; + +typedef struct { + REPORTMACSTRUCT ReportMacStruct; + TEE_TCB_INFO TeeTcbInfo; + UINT8 Rsvd[17]; + TDINFO Tdinfo; +} TDREPORT_STRUCT; + +#pragma pack() + +#endif diff --git a/MdePkg/Include/Library/HobLib.h b/MdePkg/Include/Library/HobLib.h index 517365eae594..9e3801103ebb 100644 --- a/MdePkg/Include/Library/HobLib.h +++ b/MdePkg/Include/Library/HobLib.h @@ -473,6 +473,105 @@ BuildMemoryAllocationHob ( IN EFI_MEMORY_TYPE MemoryType ); + +VOID * +EFIAPI +PrePeiGetHobList( + VOID +); + +EFI_STATUS +EFIAPI +PrePeiSetHobList( + IN VOID *HobList + ); + +/** + Builds a Firmware Volume HOB and a resource descriptor hob + + This function builds a Firmware Volume HOB. + It can only be invoked during PEI phase; + for DXE phase, it will ASSERT() since PEI HOB is read-only for DXE phase. + If there is no additional space for HOB creation, then ASSERT(). + + @param BaseAddress The base address of the Firmware Volume. + @param Length The size of the Firmware Volume in bytes. + +**/ +VOID +EFIAPI +BuildFvHobs ( + IN EFI_PHYSICAL_ADDRESS PhysicalStart, + IN UINT64 NumberOfBytes, + IN EFI_RESOURCE_ATTRIBUTE_TYPE *ResourceAttribute OPTIONAL + ); + + +/** + Updates the pointer to the HOB list. + + @param HobList Hob list pointer to store + +**/ +EFI_STATUS +EFIAPI +SetHobList ( + IN VOID *HobList + ); + +EFI_HOB_HANDOFF_INFO_TABLE* +HobConstructor ( + IN VOID *EfiMemoryBegin, + IN UINTN EfiMemoryLength, + IN VOID *EfiFreeMemoryBottom, + IN VOID *EfiFreeMemoryTop + ); + +/** + This service enables PEIMs to create various types of HOBs. + + @param Type The type of HOB to be installed. + @param Length The length of the HOB to be added. + + @retval !NULL The HOB was successfully created. + @retval NULL There is no additional space for HOB creation. + +**/ +VOID * +CreateHob ( + IN UINT16 HobType, + IN UINT16 HobLenght + ); + + +/** + This service enables PEIMs to update the boot mode variable. + + @param BootMode The value of the boot mode to set. + + @retval EFI_SUCCESS The value was successfully updated + +**/ +EFI_STATUS +EFIAPI +SetBootMode ( + IN EFI_BOOT_MODE BootMode + ); + +/** + Update the Stack Hob if the stack has been moved + + @param BaseAddress The 64 bit physical address of the Stack. + @param Length The length of the stack in bytes. + +**/ +VOID +UpdateStackHob ( + IN EFI_PHYSICAL_ADDRESS BaseAddress, + IN UINT64 Length + ); + + /** Returns the type of a HOB. diff --git a/MdePkg/Include/Library/MemoryAllocationLib.h b/MdePkg/Include/Library/MemoryAllocationLib.h index 65a30cf146dd..8f0fae6c73e1 100644 --- a/MdePkg/Include/Library/MemoryAllocationLib.h +++ b/MdePkg/Include/Library/MemoryAllocationLib.h @@ -484,4 +484,26 @@ FreePool ( IN VOID *Buffer ); +/** + Allocates one or more 4KB pages of given type MemoryType. + + Allocates the number of 4KB pages of MemoryType and returns a pointer to the + allocated buffer. The buffer returned is aligned on a 4KB boundary. If Pages is 0, then NULL + is returned. If there is not enough memory remaining to satisfy the request, then NULL is + returned. + + @param MemoryType Type of memory to use for this allocation. + @param Pages The number of 4 KB pages to allocate. + + @return A pointer to the allocated buffer or NULL if allocation fails. + +**/ +VOID * +EFIAPI +AllocatePagesWithMemoryType ( + IN UINTN MemoryType, + IN UINTN Pages + ); + + #endif diff --git a/MdePkg/Include/Library/TdxLib.h b/MdePkg/Include/Library/TdxLib.h new file mode 100644 index 000000000000..5c089ed2262a --- /dev/null +++ b/MdePkg/Include/Library/TdxLib.h @@ -0,0 +1,145 @@ +/** @file + TdxLib definitions + + Copyright (c) 2020 - 2021, Intel Corporation. All rights reserved.
+ SPDX-License-Identifier: BSD-2-Clause-Patent + +**/ + +#ifndef _TDX_LIB_H_ +#define _TDX_LIB_H_ + +#include +#include +#include +#include + +/** + This function accepts a pending private page, and initialize the page to + all-0 using the TD ephemeral private key. + + @param[in] StartAddress Guest physical address of the private page + to accept. + @param[in] NumberOfPages Number of the pages to be accepted. + @param[in] PageSize GPA page size. Accept 1G/2M/4K page size. + + @return EFI_SUCCESS +**/ +EFI_STATUS +EFIAPI +TdAcceptPages ( + IN UINT64 StartAddress, + IN UINT64 NumberOfPages, + IN UINT64 PageSize + ); + +/** + This function extends one of the RTMR measurement register + in TDCS with the provided extension data in memory. + RTMR extending supports SHA384 which length is 48 bytes. + + @param[in] Data Point to the data to be extended + @param[in] DataLen Length of the data. Must be 48 + @param[in] Index RTMR index + + @return EFI_SUCCESS + @return EFI_INVALID_PARAMETER + @return EFI_DEVICE_ERROR + +**/ +EFI_STATUS +EFIAPI +TdExtendRtmr ( + IN UINT32 *Data, + IN UINT32 DataLen, + IN UINT8 Index + ); + + +/** + This function ges the Td guest shared page mask. + + The guest indicates if a page is shared using the Guest Physical Address + (GPA) Shared (S) bit. If the GPA Width(GPAW) is 48, the S-bit is bit-47. + If the GPAW is 52, the S-bit is bit-51. + + @return Shared page bit mask +**/ +UINT64 +EFIAPI +TdSharedPageMask ( + VOID + ); + + +/** + The TDCALL instruction causes a VM exit to the Intel TDX module. It is + used to call guest-side Intel TDX functions, either local or a TD exit + to the host VMM, as selected by Leaf. + Leaf functions are described at + + @param[in] Leaf Leaf number of TDCALL instruction + @param[in] Arg1 Arg1 + @param[in] Arg2 Arg2 + @param[in] Arg3 Arg3 + @param[in,out] Results Returned result of the Leaf function + + @return EFI_SUCCESS + @return Other See individual leaf functions +**/ +EFI_STATUS +EFIAPI +TdCall ( + IN UINT64 Leaf, + IN UINT64 Arg1, + IN UINT64 Arg2, + IN UINT64 Arg3, + IN OUT VOID *Results + ); + +/** + TDVMALL is a leaf function 0 for TDCALL. It helps invoke services from the + host VMM to pass/receive information. + + @param[in] Leaf Number of sub-functions + @param[in] Arg1 Arg1 + @param[in] Arg2 Arg2 + @param[in] Arg3 Arg3 + @param[in] Arg4 Arg4 + @param[in,out] Results Returned result of the sub-function + + @return EFI_SUCCESS + @return Other See individual sub-functions + +**/ +EFI_STATUS +EFIAPI +TdVmCall ( + IN UINT64 Leaf, + IN UINT64 Arg1, + IN UINT64 Arg2, + IN UINT64 Arg3, + IN UINT64 Arg4, + IN OUT VOID *Results + ); + +/** + This function enable the TD guest to request the VMM to emulate CPUID + operation, especially for non-architectural, CPUID leaves. + + @param[in] Eax Main leaf of the CPUID + @param[in] Ecx Sub-leaf of the CPUID + @param[out] Results Returned result of CPUID operation + + @return EFI_SUCCESS +**/ +EFI_STATUS +EFIAPI +TdVmCallCpuid ( + IN UINT64 Eax, + IN UINT64 Ecx, + OUT VOID *Results + ); + +#endif diff --git a/MdePkg/Include/Library/TdxProbeLib.h b/MdePkg/Include/Library/TdxProbeLib.h new file mode 100644 index 000000000000..177c684f5622 --- /dev/null +++ b/MdePkg/Include/Library/TdxProbeLib.h @@ -0,0 +1,25 @@ +/** @file + TdxProbeLib definitions + + Copyright (c) 2021, Intel Corporation. All rights reserved.
+ SPDX-License-Identifier: BSD-2-Clause-Patent + +**/ + +#ifndef _TDX_PROBE_LIB_H_ +#define _TDX_PROBE_LIB_H_ + +#include + +/** + Check whether it is TD guest or Non-TD guest + + @return TRUE TD guest + @return FALSE Non-TD guest +**/ +BOOLEAN +EFIAPI +ProbeTdGuest ( + VOID); + +#endif diff --git a/MdePkg/Include/Protocol/Tdx.h b/MdePkg/Include/Protocol/Tdx.h new file mode 100644 index 000000000000..0c09feb6fd4d --- /dev/null +++ b/MdePkg/Include/Protocol/Tdx.h @@ -0,0 +1,28 @@ +/** @file + If TD-Guest firmware supports measurement and an event is created, TD-Guest + firmware is designed to report the event log with the same data structure + in TCG-Platform-Firmware-Profile specification with + EFI_TCG2_EVENT_LOG_FORMAT_TCG_2 format. + + The TD-Guest firmware supports measurement, the TD Guest Firmware is designed + to produce EFI_TD_PROTOCOL with new GUID EFI_TD_PROTOCOL_GUID to report + event log and provides hash capability. + +Copyright (c) 2020 - 2021, Intel Corporation. All rights reserved.
+SPDX-License-Identifier: BSD-2-Clause-Patent + +**/ + + +#ifndef __EFI_TDX_PROTOCOL_H__ +#define __EFI_TDX_PROTOCOL_H__ + +#include + +#define EFI_TD_PROTOCOL_GUID \ + {0x96751a3d, 0x72f4, 0x41a6, { 0xa7, 0x94, 0xed, 0x5d, 0x0e, 0x67, 0xae, 0x6b }} + +extern EFI_GUID gTdTcg2ProtocolGuid; + + +#endif diff --git a/MdePkg/Include/Protocol/TdxAcpi.h b/MdePkg/Include/Protocol/TdxAcpi.h new file mode 100644 index 000000000000..f7cd2eac5d4f --- /dev/null +++ b/MdePkg/Include/Protocol/TdxAcpi.h @@ -0,0 +1,29 @@ +/** @file + TBD + + Copyright (c) 2020 - 2021, Intel Corporation. All rights reserved.
+ SPDX-License-Identifier: BSD-2-Clause-Patent + +**/ + + +#ifndef __TDX_ACPI_H__ +#define __TDX_ACPI_H__ + +#include + +#define EFI_TDX_EVENTLOG_ACPI_TABLE_SIGNATURE SIGNATURE_32('T', 'D', 'E', 'L') +#define EFI_TDX_EVENTLOG_ACPIT_TABLE_REVISION 1 + +#pragma pack(1) + +typedef struct { + EFI_ACPI_DESCRIPTION_HEADER Header; + UINT32 Rsvd; + UINT64 Laml; + UINT64 Lasa; +} EFI_TDX_EVENTLOG_ACPI_TABLE; + +#pragma pack() + +#endif \ No newline at end of file diff --git a/MdePkg/Library/BaseIoLibIntrinsic/BaseIoLibIntrinsicSev.inf b/MdePkg/Library/BaseIoLibIntrinsic/BaseIoLibIntrinsicSev.inf index 86a07e60f838..84fdf6adb79c 100644 --- a/MdePkg/Library/BaseIoLibIntrinsic/BaseIoLibIntrinsicSev.inf +++ b/MdePkg/Library/BaseIoLibIntrinsic/BaseIoLibIntrinsicSev.inf @@ -1,52 +1,52 @@ -## @file -# Instance of I/O Library using compiler intrinsics. -# -# I/O Library that uses compiler intrinsics to perform IN and OUT instructions -# for IA-32 and x64. -# -# Copyright (c) 2007 - 2018, Intel Corporation. All rights reserved.
-# Portions copyright (c) 2008 - 2009, Apple Inc. All rights reserved.
-# Copyright (c) 2017, AMD Incorporated. All rights reserved.
-# -# SPDX-License-Identifier: BSD-2-Clause-Patent -# -## - -[Defines] - INF_VERSION = 0x00010005 - BASE_NAME = BaseIoLibIntrinsicSev - MODULE_UNI_FILE = BaseIoLibIntrinsic.uni - FILE_GUID = 93742f95-6e71-4581-b600-8e1da443f95a - MODULE_TYPE = BASE - VERSION_STRING = 1.0 - LIBRARY_CLASS = IoLib - - -# -# VALID_ARCHITECTURES = IA32 X64 -# - -[Sources] - IoLibMmioBuffer.c - BaseIoLibIntrinsicInternal.h - IoHighLevel.c - -[Sources.IA32] - IoLibGcc.c | GCC - IoLibMsc.c | MSFT - IoLib.c - Ia32/IoFifoSev.nasm - -[Sources.X64] - IoLibGcc.c | GCC - IoLibMsc.c | MSFT - IoLib.c - X64/IoFifoSev.nasm - -[Packages] - MdePkg/MdePkg.dec - -[LibraryClasses] - DebugLib - BaseLib - +## @file +# Instance of I/O Library using compiler intrinsics. +# +# I/O Library that uses compiler intrinsics to perform IN and OUT instructions +# for IA-32 and x64. +# +# Copyright (c) 2007 - 2018, Intel Corporation. All rights reserved.
+# Portions copyright (c) 2008 - 2009, Apple Inc. All rights reserved.
+# Copyright (c) 2017, AMD Incorporated. All rights reserved.
+# +# SPDX-License-Identifier: BSD-2-Clause-Patent +# +## + +[Defines] + INF_VERSION = 0x00010005 + BASE_NAME = BaseIoLibIntrinsicSev + MODULE_UNI_FILE = BaseIoLibIntrinsic.uni + FILE_GUID = 93742f95-6e71-4581-b600-8e1da443f95a + MODULE_TYPE = BASE + VERSION_STRING = 1.0 + LIBRARY_CLASS = IoLib + + +# +# VALID_ARCHITECTURES = IA32 X64 +# + +[Sources] + IoLibMmioBuffer.c + BaseIoLibIntrinsicInternal.h + IoHighLevel.c + +[Sources.IA32] + IoLibGcc.c | GCC + IoLibMsc.c | MSFT + IoLib.c + Ia32/IoFifoSev.nasm + +[Sources.X64] + IoLibGcc.c | GCC + IoLibMsc.c | MSFT + IoLib.c + X64/IoFifoSev.nasm + +[Packages] + MdePkg/MdePkg.dec + +[LibraryClasses] + DebugLib + BaseLib + diff --git a/MdePkg/Library/BaseIoLibIntrinsic/BaseIoLibIntrinsicTdx.inf b/MdePkg/Library/BaseIoLibIntrinsic/BaseIoLibIntrinsicTdx.inf new file mode 100644 index 000000000000..3032460dfadc --- /dev/null +++ b/MdePkg/Library/BaseIoLibIntrinsic/BaseIoLibIntrinsicTdx.inf @@ -0,0 +1,53 @@ +## @file +# Instance of I/O Library compatible for both Td guest and Non-Td guest. +# +# For Non-Td guest this I/O Library uses compiler intrinsics to perform +# IN and OUT instructions for X64. +# +# For Td guest this I/O Library uses TDVMCALL to perform IN and OUT +# instruction for X64. +# +# Copyright (c) 2007 - 2021, Intel Corporation. All rights reserved.
+# Portions copyright (c) 2008 - 2009, Apple Inc. All rights reserved.
+# Copyright (c) 2017, AMD Incorporated. All rights reserved.
+# Portions Copyright (c) 2020, Hewlett Packard Enterprise Development LP. All rights reserved.
+# +# SPDX-License-Identifier: BSD-2-Clause-Patent +# +## + +[Defines] + INF_VERSION = 0x00010005 + BASE_NAME = BaseIoLibIntrinsicTdx + MODULE_UNI_FILE = BaseIoLibIntrinsic.uni + FILE_GUID = 681b9f30-fc2d-11ea-8b6e-0800200c9a66 + MODULE_TYPE = BASE + VERSION_STRING = 1.0 + LIBRARY_CLASS = IoLib + + +# +# VALID_ARCHITECTURES = X64 +# + +[Sources] + IoLibMmioBuffer.c + BaseIoLibIntrinsicInternal.h + IoHighLevel.c + +[Sources.X64] + IoLibGccTdx.c | GCC + IoLibMscTdx.c | MSFT + IoLibInternalTdx.c + IoLibTdx.c + X64/IoFifoSev.nasm + +[Packages] + MdePkg/MdePkg.dec + +[LibraryClasses] + DebugLib + BaseLib + TdxLib + TdxProbeLib + diff --git a/MdePkg/Library/BaseIoLibIntrinsic/IoLibGccTdx.c b/MdePkg/Library/BaseIoLibIntrinsic/IoLibGccTdx.c new file mode 100644 index 000000000000..a8e4295f8767 --- /dev/null +++ b/MdePkg/Library/BaseIoLibIntrinsic/IoLibGccTdx.c @@ -0,0 +1,231 @@ +/** @file + I/O Library. This file has compiler specifics for GCC as there is no + ANSI C standard for doing IO. + + GCC - uses EFIAPI assembler. __asm__ calls GAS. __volatile__ makes sure the + compiler puts the assembler in this exact location. The complex GNUC + operations are not optimzed. It would be possible to also write these + with EFIAPI assembler. + + We don't advocate putting compiler specifics in libraries or drivers but there + is no other way to make this work. + + Copyright (c) 2006 - 2021, Intel Corporation. All rights reserved.
+ SPDX-License-Identifier: BSD-2-Clause-Patent + +**/ + + +#include "BaseIoLibIntrinsicInternal.h" +#include +#include "IoLibTdx.h" + +/** + Reads an 8-bit I/O port. + + Reads the 8-bit I/O port specified by Port. The 8-bit read value is returned. + This function must guarantee that all I/O read and write operations are + serialized. + + If 8-bit I/O port operations are not supported, then ASSERT() + + For Td guest TDVMCALL_IO is invoked to read I/O port. + + @param Port The I/O port to read. + + @return The value read. + +**/ +UINT8 +EFIAPI +IoRead8 ( + IN UINTN Port + ) +{ + UINT8 Data; + + if (ProbeTdGuest ()) { + Data = TdIoRead8 (Port); + return Data; + } + + __asm__ __volatile__ ("inb %w1,%b0" : "=a" (Data) : "d" ((UINT16)Port)); + return Data; +} + +/** + Writes an 8-bit I/O port. + + Writes the 8-bit I/O port specified by Port with the value specified by Value + and returns Value. This function must guarantee that all I/O read and write + operations are serialized. + + If 8-bit I/O port operations are not supported, then ASSERT(). + + For Td guest TDVMCALL_IO is invoked to write I/O port. + + @param Port The I/O port to write. + @param Value The value to write to the I/O port. + + @return The value written the I/O port. + +**/ +UINT8 +EFIAPI +IoWrite8 ( + IN UINTN Port, + IN UINT8 Value + ) +{ + if (ProbeTdGuest ()) { + TdIoWrite8 (Port, Value); + return Value; + } + __asm__ __volatile__ ("outb %b0,%w1" : : "a" (Value), "d" ((UINT16)Port)); + return Value;; +} + +/** + Reads a 16-bit I/O port. + + Reads the 16-bit I/O port specified by Port. The 16-bit read value is returned. + This function must guarantee that all I/O read and write operations are + serialized. + + If 16-bit I/O port operations are not supported, then ASSERT(). + If Port is not aligned on a 16-bit boundary, then ASSERT(). + + For Td guest TDVMCALL_IO is invoked to read I/O port. + + @param Port The I/O port to read. + + @return The value read. + +**/ +UINT16 +EFIAPI +IoRead16 ( + IN UINTN Port + ) +{ + UINT16 Data; + + ASSERT ((Port & 1) == 0); + + if (ProbeTdGuest ()) { + Data = TdIoRead16 (Port); + return Data; + } + + __asm__ __volatile__ ("inw %w1,%w0" : "=a" (Data) : "d" ((UINT16)Port)); + return Data; +} + +/** + Writes a 16-bit I/O port. + + Writes the 16-bit I/O port specified by Port with the value specified by Value + and returns Value. This function must guarantee that all I/O read and write + operations are serialized. + + If 16-bit I/O port operations are not supported, then ASSERT(). + If Port is not aligned on a 16-bit boundary, then ASSERT(). + + For Td guest TDVMCALL_IO is invoked to write I/O port. + + @param Port The I/O port to write. + @param Value The value to write to the I/O port. + + @return The value written the I/O port. + +**/ +UINT16 +EFIAPI +IoWrite16 ( + IN UINTN Port, + IN UINT16 Value + ) +{ + ASSERT ((Port & 1) == 0); + + if (ProbeTdGuest ()) { + TdIoWrite16 (Port, Value); + return Value; + } + + __asm__ __volatile__ ("outw %w0,%w1" : : "a" (Value), "d" ((UINT16)Port)); + return Value;; +} + +/** + Reads a 32-bit I/O port. + + Reads the 32-bit I/O port specified by Port. The 32-bit read value is returned. + This function must guarantee that all I/O read and write operations are + serialized. + + If 32-bit I/O port operations are not supported, then ASSERT(). + If Port is not aligned on a 32-bit boundary, then ASSERT(). + + For Td guest TDVMCALL_IO is invoked to read I/O port. + + @param Port The I/O port to read. + + @return The value read. + +**/ +UINT32 +EFIAPI +IoRead32 ( + IN UINTN Port + ) +{ + UINT32 Data; + + ASSERT ((Port & 3) == 0); + + if (ProbeTdGuest ()) { + Data = TdIoRead32 (Port); + return Data; + } + + __asm__ __volatile__ ("inl %w1,%0" : "=a" (Data) : "d" ((UINT16)Port)); + return Data; +} + +/** + Writes a 32-bit I/O port. + + Writes the 32-bit I/O port specified by Port with the value specified by Value + and returns Value. This function must guarantee that all I/O read and write + operations are serialized. + + If 32-bit I/O port operations are not supported, then ASSERT(). + If Port is not aligned on a 32-bit boundary, then ASSERT(). + + For Td guest TDVMCALL_IO is invoked to write I/O port. + + @param Port The I/O port to write. + @param Value The value to write to the I/O port. + + @return The value written the I/O port. + +**/ +UINT32 +EFIAPI +IoWrite32 ( + IN UINTN Port, + IN UINT32 Value + ) +{ + ASSERT ((Port & 3) == 0); + + if (ProbeTdGuest ()) { + TdIoWrite32 (Port, Value); + return Value; + } + + __asm__ __volatile__ ("outl %0,%w1" : : "a" (Value), "d" ((UINT16)Port)); + return Value; +} + diff --git a/MdePkg/Library/BaseIoLibIntrinsic/IoLibInternalTdx.c b/MdePkg/Library/BaseIoLibIntrinsic/IoLibInternalTdx.c new file mode 100644 index 000000000000..eec490ac3816 --- /dev/null +++ b/MdePkg/Library/BaseIoLibIntrinsic/IoLibInternalTdx.c @@ -0,0 +1,456 @@ +/** @file + TDX I/O Library routines. + + Copyright (c) 2020-2021, Intel Corporation. All rights reserved.
+ SPDX-License-Identifier: BSD-2-Clause-Patent + +**/ + + +#include "IoLibTdx.h" +#include +#include + +// Size of TDVMCALL Access, including IO and MMIO +#define TDVMCALL_ACCESS_SIZE_1 1 +#define TDVMCALL_ACCESS_SIZE_2 2 +#define TDVMCALL_ACCESS_SIZE_4 4 +#define TDVMCALL_ACCESS_SIZE_8 8 + +// Direction of TDVMCALL Access, including IO and MMIO +#define TDVMCALL_ACCESS_READ 0 +#define TDVMCALL_ACCESS_WRITE 1 + +/** + Reads an 8-bit I/O port. + + TDVMCALL_IO is invoked to read I/O port. + + @param Port The I/O port to read. + + @return The value read. + +**/ +UINT8 +EFIAPI +TdIoRead8 ( + IN UINTN Port + ) +{ + UINT64 Status; + UINT64 Val; + + Status = TdVmCall (TDVMCALL_IO, TDVMCALL_ACCESS_SIZE_1, TDVMCALL_ACCESS_READ, Port, 0, &Val); + if (Status != 0) { + TdVmCall (TDVMCALL_HALT, 0, 0, 0, 0, 0); + } + return (UINT8)Val; +} + +/** + Reads a 16-bit I/O port. + + TDVMCALL_IO is invoked to write I/O port. + + @param Port The I/O port to read. + + @return The value read. + +**/ +UINT16 +EFIAPI +TdIoRead16 ( + IN UINTN Port + ) +{ + UINT64 Status; + UINT64 Val; + + ASSERT ((Port & 1) == 0); + + Status = TdVmCall (TDVMCALL_IO, TDVMCALL_ACCESS_SIZE_2, TDVMCALL_ACCESS_READ, Port, 0, &Val); + if (Status != 0) { + TdVmCall (TDVMCALL_HALT, 0, 0, 0, 0, 0); + } + return (UINT16)Val; +} + +/** + Reads a 32-bit I/O port. + + TDVMCALL_IO is invoked to read I/O port. + + @param Port The I/O port to read. + + @return The value read. + +**/ +UINT32 +EFIAPI +TdIoRead32 ( + IN UINTN Port + ) +{ + UINT64 Status; + UINT64 Val; + + ASSERT ((Port & 3) == 0); + + Status = TdVmCall (TDVMCALL_IO, TDVMCALL_ACCESS_SIZE_4, TDVMCALL_ACCESS_READ, Port, 0, &Val); + if (Status != 0) { + TdVmCall (TDVMCALL_HALT, 0, 0, 0, 0, 0); + } + return (UINT32)Val; +} + +/** + Writes an 8-bit I/O port. + + TDVMCALL_IO is invoked to write I/O port. + + @param Port The I/O port to write. + @param Value The value to write to the I/O port. + + @return The value written the I/O port. + +**/ +UINT8 +EFIAPI +TdIoWrite8 ( + IN UINTN Port, + IN UINT8 Value + ) +{ + UINT64 Status; + UINT64 Val; + + Val = Value; + Status = TdVmCall (TDVMCALL_IO, TDVMCALL_ACCESS_SIZE_1, TDVMCALL_ACCESS_WRITE, Port, Val, 0); + if (Status != 0) { + TdVmCall (TDVMCALL_HALT, 0, 0, 0, 0, 0); + } + return Value;; +} + +/** + Writes a 16-bit I/O port. + + TDVMCALL_IO is invoked to write I/O port. + + @param Port The I/O port to write. + @param Value The value to write to the I/O port. + + @return The value written the I/O port. + +**/ +UINT16 +EFIAPI +TdIoWrite16 ( + IN UINTN Port, + IN UINT16 Value + ) +{ + UINT64 Status; + UINT64 Val; + + ASSERT ((Port & 1) == 0); + Val = Value; + Status = TdVmCall (TDVMCALL_IO, TDVMCALL_ACCESS_SIZE_2, TDVMCALL_ACCESS_WRITE, Port, Val, 0); + if (Status != 0) { + TdVmCall (TDVMCALL_HALT, 0, 0, 0, 0, 0); + } + return Value;; +} + +/** + Writes a 32-bit I/O port. + + TDVMCALL_IO is invoked to write I/O port. + + @param Port The I/O port to write. + @param Value The value to write to the I/O port. + + @return The value written the I/O port. + +**/ +UINT32 +EFIAPI +TdIoWrite32 ( + IN UINTN Port, + IN UINT32 Value + ) +{ + UINT64 Status; + UINT64 Val; + + ASSERT ((Port & 3) == 0); + Val = Value; + Status = TdVmCall (TDVMCALL_IO, TDVMCALL_ACCESS_SIZE_4, TDVMCALL_ACCESS_WRITE, Port, Val, 0); + if (Status != 0) { + TdVmCall (TDVMCALL_HALT, 0, 0, 0, 0, 0); + } + return Value;; +} + +/** + Reads an 8-bit MMIO register. + + TDVMCALL_MMIO is invoked to read MMIO registers. + + @param Address The MMIO register to read. + + @return The value read. + +**/ +UINT8 +EFIAPI +TdMmioRead8 ( + IN UINTN Address + ) +{ + UINT64 Value; + UINT64 Status; + + Address |= TdSharedPageMask (); + + MemoryFence (); + Status = TdVmCall (TDVMCALL_MMIO, TDVMCALL_ACCESS_SIZE_1, TDVMCALL_ACCESS_READ, Address, 0, &Value); + if (Status != 0) { + Value = *(volatile UINT64*)Address; + } + MemoryFence (); + + return (UINT8)Value; +} + +/** + Writes an 8-bit MMIO register. + + TDVMCALL_MMIO is invoked to read write registers. + + @param Address The MMIO register to write. + @param Value The value to write to the MMIO register. + + @return Value. + +**/ +UINT8 +EFIAPI +TdMmioWrite8 ( + IN UINTN Address, + IN UINT8 Val + ) +{ + UINT64 Value; + UINT64 Status; + + Address |= TdSharedPageMask (); + + MemoryFence (); + Value = Val; + Status = TdVmCall (TDVMCALL_MMIO, TDVMCALL_ACCESS_SIZE_1, TDVMCALL_ACCESS_WRITE, Address, Value, 0); + if (Status != 0) { + *(volatile UINT8*)Address = Val; + } + MemoryFence (); + + return Val; +} + +/** + Reads a 16-bit MMIO register. + + TDVMCALL_MMIO is invoked to read MMIO registers. + + @param Address The MMIO register to read. + + @return The value read. + +**/ +UINT16 +EFIAPI +TdMmioRead16 ( + IN UINTN Address + ) +{ + UINT64 Value; + UINT64 Status; + + Address |= TdSharedPageMask (); + + MemoryFence (); + Status = TdVmCall (TDVMCALL_MMIO, TDVMCALL_ACCESS_SIZE_2, TDVMCALL_ACCESS_READ, Address, 0, &Value); + if (Status != 0) { + Value = *(volatile UINT64*)Address; + } + MemoryFence (); + + return (UINT16)Value; +} + +/** + Writes a 16-bit MMIO register. + + TDVMCALL_MMIO is invoked to write MMIO registers. + + @param Address The MMIO register to write. + @param Value The value to write to the MMIO register. + + @return Value. + +**/ +UINT16 +EFIAPI +TdMmioWrite16 ( + IN UINTN Address, + IN UINT16 Val + ) +{ + UINT64 Value; + UINT64 Status; + + ASSERT ((Address & 1) == 0); + + Address |= TdSharedPageMask (); + + MemoryFence (); + Value = Val; + Status = TdVmCall (TDVMCALL_MMIO, TDVMCALL_ACCESS_SIZE_2, TDVMCALL_ACCESS_WRITE, Address, Value, 0); + if (Status != 0) { + *(volatile UINT16*)Address = Val; + } + MemoryFence (); + + return Val; +} + +/** + Reads a 32-bit MMIO register. + + TDVMCALL_MMIO is invoked to read MMIO registers. + + @param Address The MMIO register to read. + + @return The value read. + +**/ +UINT32 +EFIAPI +TdMmioRead32 ( + IN UINTN Address + ) +{ + UINT64 Value; + UINT64 Status; + + Address |= TdSharedPageMask (); + + MemoryFence (); + Status = TdVmCall (TDVMCALL_MMIO, TDVMCALL_ACCESS_SIZE_4, TDVMCALL_ACCESS_READ, Address, 0, &Value); + if (Status != 0) { + Value = *(volatile UINT64*)Address; + } + MemoryFence (); + + return (UINT32)Value; +} + +/** + Writes a 32-bit MMIO register. + + TDVMCALL_MMIO is invoked to write MMIO registers. + + @param Address The MMIO register to write. + @param Value The value to write to the MMIO register. + + @return Value. + +**/ +UINT32 +EFIAPI +TdMmioWrite32 ( + IN UINTN Address, + IN UINT32 Val + ) +{ + UINT64 Value; + UINT64 Status; + + ASSERT ((Address & 3) == 0); + + Address |= TdSharedPageMask (); + + MemoryFence (); + Value = Val; + Status = TdVmCall (TDVMCALL_MMIO, TDVMCALL_ACCESS_SIZE_4, TDVMCALL_ACCESS_WRITE, Address, Value, 0); + if (Status != 0) { + *(volatile UINT32*)Address = Val; + } + MemoryFence (); + + return Val; +} + +/** + Reads a 64-bit MMIO register. + + TDVMCALL_MMIO is invoked to read MMIO registers. + + @param Address The MMIO register to read. + + @return The value read. + +**/ +UINT64 +EFIAPI +TdMmioRead64 ( + IN UINTN Address + ) +{ + UINT64 Value; + UINT64 Status; + + Address |= TdSharedPageMask (); + + MemoryFence (); + Status = TdVmCall (TDVMCALL_MMIO, TDVMCALL_ACCESS_SIZE_8, TDVMCALL_ACCESS_READ, Address, 0, &Value); + if (Status != 0) { + Value = *(volatile UINT64*)Address; + } + MemoryFence (); + + return Value; +} + +/** + Writes a 64-bit MMIO register. + + TDVMCALL_MMIO is invoked to write MMIO registers. + + @param Address The MMIO register to write. + @param Value The value to write to the MMIO register. + +**/ +UINT64 +EFIAPI +TdMmioWrite64 ( + IN UINTN Address, + IN UINT64 Value + ) +{ + UINT64 Status; + + ASSERT ((Address & 7) == 0); + + Address |= TdSharedPageMask (); + + MemoryFence (); + Status = TdVmCall (TDVMCALL_MMIO, TDVMCALL_ACCESS_SIZE_8, TDVMCALL_ACCESS_WRITE, Address, Value, 0); + if (Status != 0) { + *(volatile UINT64*)Address = Value; + } + MemoryFence (); + return Value; +} + + diff --git a/MdePkg/Library/BaseIoLibIntrinsic/IoLibMscTdx.c b/MdePkg/Library/BaseIoLibIntrinsic/IoLibMscTdx.c new file mode 100644 index 000000000000..247a6bdafa2a --- /dev/null +++ b/MdePkg/Library/BaseIoLibIntrinsic/IoLibMscTdx.c @@ -0,0 +1,271 @@ +/** @file + I/O Library. This file has compiler specifics for Microsft C as there is no + ANSI C standard for doing IO. + + MSC - uses intrinsic functions and the optimize will remove the function call + overhead. + + We don't advocate putting compiler specifics in libraries or drivers but there + is no other way to make this work. + + Copyright (c) 2006 - 2021, Intel Corporation. All rights reserved.
+ SPDX-License-Identifier: BSD-2-Clause-Patent + +**/ + + + +#include "BaseIoLibIntrinsicInternal.h" +#include +#include "IoLibTdx.h" + +// +// Microsoft Visual Studio 7.1 Function Prototypes for I/O Intrinsics. +// + +int _inp (unsigned short port); +unsigned short _inpw (unsigned short port); +unsigned long _inpd (unsigned short port); +int _outp (unsigned short port, int databyte ); +unsigned short _outpw (unsigned short port, unsigned short dataword ); +unsigned long _outpd (unsigned short port, unsigned long dataword ); +void _ReadWriteBarrier (void); + +#pragma intrinsic(_inp) +#pragma intrinsic(_inpw) +#pragma intrinsic(_inpd) +#pragma intrinsic(_outp) +#pragma intrinsic(_outpw) +#pragma intrinsic(_outpd) +#pragma intrinsic(_ReadWriteBarrier) + +// +// _ReadWriteBarrier() forces memory reads and writes to complete at the point +// in the call. This is only a hint to the compiler and does emit code. +// In past versions of the compiler, _ReadWriteBarrier was enforced only +// locally and did not affect functions up the call tree. In Visual C++ +// 2005, _ReadWriteBarrier is enforced all the way up the call tree. +// + +/** + Reads an 8-bit I/O port. + + Reads the 8-bit I/O port specified by Port. The 8-bit read value is returned. + This function must guarantee that all I/O read and write operations are + serialized. + + If 8-bit I/O port operations are not supported, then ASSERT(). + + For Td guest TDVMCALL_IO is invoked to read I/O port. + + @param Port The I/O port to read. + + @return The value read. + +**/ +UINT8 +EFIAPI +IoRead8 ( + IN UINTN Port + ) +{ + UINT8 Value; + + if (ProbeTdGuest ()) { + Value = TdIoRead8 (Port); + return Value; + } + + _ReadWriteBarrier (); + Value = (UINT8)_inp ((UINT16)Port); + _ReadWriteBarrier (); + return Value; +} + +/** + Writes an 8-bit I/O port. + + Writes the 8-bit I/O port specified by Port with the value specified by Value + and returns Value. This function must guarantee that all I/O read and write + operations are serialized. + + If 8-bit I/O port operations are not supported, then ASSERT(). + + For Td guest TDVMCALL_IO is invoked to write I/O port. + + @param Port The I/O port to write. + @param Value The value to write to the I/O port. + + @return The value written to the I/O port. + +**/ +UINT8 +EFIAPI +IoWrite8 ( + IN UINTN Port, + IN UINT8 Value + ) +{ + + if (ProbeTdGuest ()) { + TdIoWrite8 (Port, Value); + return Value; + } + + _ReadWriteBarrier (); + (UINT8)_outp ((UINT16)Port, Value); + _ReadWriteBarrier (); + return Value; +} + +/** + Reads a 16-bit I/O port. + + Reads the 16-bit I/O port specified by Port. The 16-bit read value is returned. + This function must guarantee that all I/O read and write operations are + serialized. + + If 16-bit I/O port operations are not supported, then ASSERT(). + If Port is not aligned on a 16-bit boundary, then ASSERT(). + + For Td guest TDVMCALL_IO is invoked to read I/O port. + + @param Port The I/O port to read. + + @return The value read. + +**/ +UINT16 +EFIAPI +IoRead16 ( + IN UINTN Port + ) +{ + UINT16 Value; + + ASSERT ((Port & 1) == 0); + + if (ProbeTdGuest ()) { + Value = TdIoRead16 (Port); + return Value; + } + + _ReadWriteBarrier (); + Value = _inpw ((UINT16)Port); + _ReadWriteBarrier (); + return Value; +} + +/** + Writes a 16-bit I/O port. + + Writes the 16-bit I/O port specified by Port with the value specified by Value + and returns Value. This function must guarantee that all I/O read and write + operations are serialized. + + If 16-bit I/O port operations are not supported, then ASSERT(). + If Port is not aligned on a 16-bit boundary, then ASSERT(). + + For Td guest TDVMCALL_IO is invoked to write I/O port. + + @param Port The I/O port to write. + @param Value The value to write to the I/O port. + + @return The value written to the I/O port. + +**/ +UINT16 +EFIAPI +IoWrite16 ( + IN UINTN Port, + IN UINT16 Value + ) +{ + ASSERT ((Port & 1) == 0); + + if (ProbeTdGuest ()) { + TdIoWrite16 (Port, Value); + return Value; + } + + _ReadWriteBarrier (); + _outpw ((UINT16)Port, Value); + _ReadWriteBarrier (); + return Value; +} + +/** + Reads a 32-bit I/O port. + + Reads the 32-bit I/O port specified by Port. The 32-bit read value is returned. + This function must guarantee that all I/O read and write operations are + serialized. + + If 32-bit I/O port operations are not supported, then ASSERT(). + If Port is not aligned on a 32-bit boundary, then ASSERT(). + + For Td guest TDVMCALL_IO is invoked to read I/O port. + + @param Port The I/O port to read. + + @return The value read. + +**/ +UINT32 +EFIAPI +IoRead32 ( + IN UINTN Port + ) +{ + UINT32 Value; + + ASSERT ((Port & 3) == 0); + + if (ProbeTdGuest ()) { + Value = TdIoRead32 (Port); + return Value; + } + + _ReadWriteBarrier (); + Value = _inpd ((UINT16)Port); + _ReadWriteBarrier (); + return Value; +} + +/** + Writes a 32-bit I/O port. + + Writes the 32-bit I/O port specified by Port with the value specified by Value + and returns Value. This function must guarantee that all I/O read and write + operations are serialized. + + If 32-bit I/O port operations are not supported, then ASSERT(). + If Port is not aligned on a 32-bit boundary, then ASSERT(). + + For Td guest TDVMCALL_IO is invoked to write I/O port. + + @param Port The I/O port to write. + @param Value The value to write to the I/O port. + + @return The value written to the I/O port. + +**/ +UINT32 +EFIAPI +IoWrite32 ( + IN UINTN Port, + IN UINT32 Value + ) +{ + ASSERT ((Port & 3) == 0); + + if (ProbeTdGuest ()) { + TdIoWrite32 (Port, Value); + return Value; + } + + _ReadWriteBarrier (); + _outpd ((UINT16)Port, Value); + _ReadWriteBarrier (); + return Value; +} diff --git a/MdePkg/Library/BaseIoLibIntrinsic/IoLibTdx.c b/MdePkg/Library/BaseIoLibIntrinsic/IoLibTdx.c new file mode 100644 index 000000000000..db208f74a600 --- /dev/null +++ b/MdePkg/Library/BaseIoLibIntrinsic/IoLibTdx.c @@ -0,0 +1,369 @@ +/** @file + Common I/O Library routines. + + Copyright (c) 2006 - 2021, Intel Corporation. All rights reserved.
+ SPDX-License-Identifier: BSD-2-Clause-Patent + +**/ + +#include "BaseIoLibIntrinsicInternal.h" +#include +#include "IoLibTdx.h" + +/** + Reads a 64-bit I/O port. + + Reads the 64-bit I/O port specified by Port. The 64-bit read value is returned. + This function must guarantee that all I/O read and write operations are + serialized. + + If 64-bit I/O port operations are not supported, then ASSERT(). + If Port is not aligned on a 64-bit boundary, then ASSERT(). + + @param Port The I/O port to read. + + @return The value read. + +**/ +UINT64 +EFIAPI +IoRead64 ( + IN UINTN Port + ) +{ + ASSERT (FALSE); + return 0; +} + +/** + Writes a 64-bit I/O port. + + Writes the 64-bit I/O port specified by Port with the value specified by Value + and returns Value. This function must guarantee that all I/O read and write + operations are serialized. + + If 64-bit I/O port operations are not supported, then ASSERT(). + If Port is not aligned on a 64-bit boundary, then ASSERT(). + + @param Port The I/O port to write. + @param Value The value to write to the I/O port. + + @return The value written the I/O port. + +**/ +UINT64 +EFIAPI +IoWrite64 ( + IN UINTN Port, + IN UINT64 Value + ) +{ + ASSERT (FALSE); + return 0; +} + + +/** + Reads an 8-bit MMIO register. + + Reads the 8-bit MMIO register specified by Address. The 8-bit read value is + returned. This function must guarantee that all MMIO read and write + operations are serialized. + + If 8-bit MMIO register operations are not supported, then ASSERT(). + + For Td guest TDVMCALL_MMIO is invoked to read MMIO registers. + + @param Address The MMIO register to read. + + @return The value read. + +**/ +UINT8 +EFIAPI +MmioRead8 ( + IN UINTN Address + ) +{ + UINT8 Value; + + if (ProbeTdGuest ()) { + Value = TdMmioRead8 (Address); + return Value; + } + + MemoryFence (); + Value = *(volatile UINT8*)Address; + MemoryFence (); + + return Value; +} + +/** + Writes an 8-bit MMIO register. + + Writes the 8-bit MMIO register specified by Address with the value specified + by Value and returns Value. This function must guarantee that all MMIO read + and write operations are serialized. + + If 8-bit MMIO register operations are not supported, then ASSERT(). + + For Td guest TDVMCALL_MMIO is invoked to write MMIO registers. + + @param Address The MMIO register to write. + @param Value The value to write to the MMIO register. + + @return Value. + +**/ +UINT8 +EFIAPI +MmioWrite8 ( + IN UINTN Address, + IN UINT8 Value + ) +{ + if (ProbeTdGuest ()) { + TdMmioWrite8 (Address, Value); + return Value; + } + + MemoryFence (); + *(volatile UINT8*)Address = Value; + MemoryFence (); + + return Value; +} + +/** + Reads a 16-bit MMIO register. + + Reads the 16-bit MMIO register specified by Address. The 16-bit read value is + returned. This function must guarantee that all MMIO read and write + operations are serialized. + + If 16-bit MMIO register operations are not supported, then ASSERT(). + If Address is not aligned on a 16-bit boundary, then ASSERT(). + + For Td guest TDVMCALL_MMIO is invoked to read MMIO registers. + + @param Address The MMIO register to read. + + @return The value read. + +**/ +UINT16 +EFIAPI +MmioRead16 ( + IN UINTN Address + ) +{ + UINT16 Value; + + ASSERT ((Address & 1) == 0); + + if (ProbeTdGuest ()) { + Value = TdMmioRead16 (Address); + return Value; + } + + MemoryFence (); + Value = *(volatile UINT16*)Address; + MemoryFence (); + + return Value; +} + +/** + Writes a 16-bit MMIO register. + + Writes the 16-bit MMIO register specified by Address with the value specified + by Value and returns Value. This function must guarantee that all MMIO read + and write operations are serialized. + + If 16-bit MMIO register operations are not supported, then ASSERT(). + If Address is not aligned on a 16-bit boundary, then ASSERT(). + + For Td guest TDVMCALL_MMIO is invoked to write MMIO registers. + + @param Address The MMIO register to write. + @param Value The value to write to the MMIO register. + + @return Value. + +**/ +UINT16 +EFIAPI +MmioWrite16 ( + IN UINTN Address, + IN UINT16 Value + ) +{ + ASSERT ((Address & 1) == 0); + + if (ProbeTdGuest ()) { + TdMmioWrite16 (Address, Value); + return Value; + } + + MemoryFence (); + *(volatile UINT16*)Address = Value; + MemoryFence (); + + return Value; +} + +/** + Reads a 32-bit MMIO register. + + Reads the 32-bit MMIO register specified by Address. The 32-bit read value is + returned. This function must guarantee that all MMIO read and write + operations are serialized. + + If 32-bit MMIO register operations are not supported, then ASSERT(). + If Address is not aligned on a 32-bit boundary, then ASSERT(). + + For Td guest TDVMCALL_MMIO is invoked to read MMIO registers. + + @param Address The MMIO register to read. + + @return The value read. + +**/ +UINT32 +EFIAPI +MmioRead32 ( + IN UINTN Address + ) +{ + UINT32 Value; + + ASSERT ((Address & 3) == 0); + + if (ProbeTdGuest ()) { + Value = TdMmioRead32 (Address); + return Value; + } + + MemoryFence (); + Value = *(volatile UINT32*)Address; + MemoryFence (); + + return Value; +} + +/** + Writes a 32-bit MMIO register. + + Writes the 32-bit MMIO register specified by Address with the value specified + by Value and returns Value. This function must guarantee that all MMIO read + and write operations are serialized. + + If 32-bit MMIO register operations are not supported, then ASSERT(). + If Address is not aligned on a 32-bit boundary, then ASSERT(). + + For Td guest TDVMCALL_MMIO is invoked to write MMIO registers. + + @param Address The MMIO register to write. + @param Value The value to write to the MMIO register. + + @return Value. + +**/ +UINT32 +EFIAPI +MmioWrite32 ( + IN UINTN Address, + IN UINT32 Value + ) +{ + ASSERT ((Address & 3) == 0); + + if (ProbeTdGuest ()) { + TdMmioWrite32 (Address, Value); + return Value; + } + + MemoryFence (); + *(volatile UINT32*)Address = Value; + MemoryFence (); + + return Value; +} + +/** + Reads a 64-bit MMIO register. + + Reads the 64-bit MMIO register specified by Address. The 64-bit read value is + returned. This function must guarantee that all MMIO read and write + operations are serialized. + + If 64-bit MMIO register operations are not supported, then ASSERT(). + If Address is not aligned on a 64-bit boundary, then ASSERT(). + + For Td guest TDVMCALL_MMIO is invoked to read MMIO registers. + + @param Address The MMIO register to read. + + @return The value read. + +**/ +UINT64 +EFIAPI +MmioRead64 ( + IN UINTN Address + ) +{ + UINT64 Value; + + ASSERT ((Address & 7) == 0); + + if (ProbeTdGuest ()) { + Value = TdMmioRead64 (Address); + return Value; + } + + MemoryFence (); + Value = *(volatile UINT64*)Address; + MemoryFence (); + + return Value; +} + +/** + Writes a 64-bit MMIO register. + + Writes the 64-bit MMIO register specified by Address with the value specified + by Value and returns Value. This function must guarantee that all MMIO read + and write operations are serialized. + + If 64-bit MMIO register operations are not supported, then ASSERT(). + If Address is not aligned on a 64-bit boundary, then ASSERT(). + + For Td guest TDVMCALL_MMIO is invoked to write MMIO registers. + + @param Address The MMIO register to write. + @param Value The value to write to the MMIO register. + +**/ +UINT64 +EFIAPI +MmioWrite64 ( + IN UINTN Address, + IN UINT64 Value + ) +{ + ASSERT ((Address & 7) == 0); + + if (ProbeTdGuest ()) { + TdMmioWrite64 (Address, Value); + return Value; + } + + MemoryFence (); + *(volatile UINT64*)Address = Value; + MemoryFence (); + + return Value; +} + diff --git a/MdePkg/Library/BaseIoLibIntrinsic/IoLibTdx.h b/MdePkg/Library/BaseIoLibIntrinsic/IoLibTdx.h new file mode 100644 index 000000000000..551e239c6976 --- /dev/null +++ b/MdePkg/Library/BaseIoLibIntrinsic/IoLibTdx.h @@ -0,0 +1,102 @@ +/** @file + Header file for Tdx IO library. + + Copyright (c) 2020 - 2021, Intel Corporation. All rights reserved.
+ SPDX-License-Identifier: BSD-2-Clause-Patent +**/ + +#ifndef __IOLIB_TDX_H__ +#define __IOLIB_TDX_H__ + +UINT8 +EFIAPI +TdIoRead8 ( + IN UINTN Port + ); + +UINT16 +EFIAPI +TdIoRead16 ( + IN UINTN Port + ); + +UINT32 +EFIAPI +TdIoRead32 ( + IN UINTN Port + ); + +UINT8 +EFIAPI +TdIoWrite8 ( + IN UINTN Port, + IN UINT8 Value + ); + +UINT16 +EFIAPI +TdIoWrite16 ( + IN UINTN Port, + IN UINT16 Value + ); + +UINT32 +EFIAPI +TdIoWrite32 ( + IN UINTN Port, + IN UINT32 Value + ); + +UINT8 +EFIAPI +TdMmioRead8 ( + IN UINTN Address + ); + +UINT8 +EFIAPI +TdMmioWrite8 ( + IN UINTN Address, + IN UINT8 Val + ); + +UINT16 +EFIAPI +TdMmioRead16 ( + IN UINTN Address + ); + +UINT16 +EFIAPI +TdMmioWrite16 ( + IN UINTN Address, + IN UINT16 Val + ); + +UINT32 +EFIAPI +TdMmioRead32 ( + IN UINTN Address + ); + +UINT32 +EFIAPI +TdMmioWrite32 ( + IN UINTN Address, + IN UINT32 Val + ); + +UINT64 +EFIAPI +TdMmioRead64 ( + IN UINTN Address + ); + +UINT64 +EFIAPI +TdMmioWrite64 ( + IN UINTN Address, + IN UINT64 Value + ); + +#endif diff --git a/MdePkg/Library/SecHobLib/HobLib.c b/MdePkg/Library/SecHobLib/HobLib.c new file mode 100644 index 000000000000..5923be7c2b20 --- /dev/null +++ b/MdePkg/Library/SecHobLib/HobLib.c @@ -0,0 +1,996 @@ +/** @file + Provide Hob Library functions for SEC phase. + +Copyright (c) 2007 - 2018, Intel Corporation. All rights reserved.
+SPDX-License-Identifier: BSD-2-Clause-Patent + +**/ + +#include + +#include + +#include +#include +#include +#include + +VOID *mHobList = NULL; + +VOID * +EFIAPI +PrePeiGetHobList( + VOID +) +{ + return mHobList; +} + +EFI_STATUS +EFIAPI +PrePeiSetHobList( + IN VOID *HobList + ) +{ + mHobList = HobList; + return EFI_SUCCESS; +} + + +/** + Returns the pointer to the HOB list. + + This function returns the pointer to first HOB in the list. + For PEI phase, the PEI service GetHobList() can be used to retrieve the pointer + to the HOB list. For the DXE phase, the HOB list pointer can be retrieved through + the EFI System Table by looking up theHOB list GUID in the System Configuration Table. + Since the System Configuration Table does not exist that the time the DXE Core is + launched, the DXE Core uses a global variable from the DXE Core Entry Point Library + to manage the pointer to the HOB list. + + If the pointer to the HOB list is NULL, then ASSERT(). + + @return The pointer to the HOB list. + +**/ +VOID * +EFIAPI +GetHobList ( + VOID + ) +{ + VOID *HobList; + + HobList = PrePeiGetHobList(); + ASSERT (HobList != NULL); + + return HobList; +} + +EFI_STATUS +EFIAPI +SetHobList ( + IN VOID *HobList + ) +{ + EFI_STATUS Status; + + Status = PrePeiSetHobList(HobList); + + return Status; +} + +EFI_HOB_HANDOFF_INFO_TABLE* +HobConstructor ( + IN VOID *EfiMemoryBegin, + IN UINTN EfiMemoryLength, + IN VOID *EfiFreeMemoryBottom, + IN VOID *EfiFreeMemoryTop + ) +{ + EFI_HOB_HANDOFF_INFO_TABLE *Hob; + EFI_HOB_GENERIC_HEADER *HobEnd; + + Hob = EfiFreeMemoryBottom; + HobEnd = (EFI_HOB_GENERIC_HEADER *)(Hob+1); + + Hob->Header.HobType = EFI_HOB_TYPE_HANDOFF; + Hob->Header.HobLength = sizeof(EFI_HOB_HANDOFF_INFO_TABLE); + Hob->Header.Reserved = 0; + + HobEnd->HobType = EFI_HOB_TYPE_END_OF_HOB_LIST; + HobEnd->HobLength = sizeof(EFI_HOB_GENERIC_HEADER); + HobEnd->Reserved = 0; + + Hob->Version = EFI_HOB_HANDOFF_TABLE_VERSION; + Hob->BootMode = BOOT_WITH_FULL_CONFIGURATION; + + Hob->EfiMemoryTop = (UINTN)EfiMemoryBegin + EfiMemoryLength; + Hob->EfiMemoryBottom = (UINTN)EfiMemoryBegin; + Hob->EfiFreeMemoryTop = (UINTN)EfiFreeMemoryTop; + Hob->EfiFreeMemoryBottom = (EFI_PHYSICAL_ADDRESS)(UINTN)(HobEnd+1); + Hob->EfiEndOfHobList = (EFI_PHYSICAL_ADDRESS)(UINTN)HobEnd; + + return Hob; + +} + +VOID * +CreateHob ( + IN UINT16 HobType, + IN UINT16 HobLength + ) +{ + EFI_HOB_HANDOFF_INFO_TABLE *HandOffHob; + EFI_HOB_GENERIC_HEADER *HobEnd; + EFI_PHYSICAL_ADDRESS FreeMemory; + VOID *Hob; + + HandOffHob = GetHobList (); + + HobLength = (UINT16)((HobLength + 0x7) & (~0x7)); + + FreeMemory = HandOffHob->EfiFreeMemoryTop - HandOffHob->EfiFreeMemoryBottom; + + if (FreeMemory < HobLength) { + return NULL; + } + + Hob = (VOID*) (UINTN) HandOffHob->EfiEndOfHobList; + ((EFI_HOB_GENERIC_HEADER*) Hob)->HobType = HobType; + ((EFI_HOB_GENERIC_HEADER*) Hob)->HobLength = HobLength; + ((EFI_HOB_GENERIC_HEADER*) Hob)->Reserved = 0; + + HobEnd = (EFI_HOB_GENERIC_HEADER*) ((UINTN)Hob + HobLength); + HandOffHob->EfiEndOfHobList = (EFI_PHYSICAL_ADDRESS) (UINTN) HobEnd; + + HobEnd->HobType = EFI_HOB_TYPE_END_OF_HOB_LIST; + HobEnd->HobLength = sizeof(EFI_HOB_GENERIC_HEADER); + HobEnd->Reserved = 0; + HobEnd++; + HandOffHob->EfiFreeMemoryBottom = (EFI_PHYSICAL_ADDRESS) (UINTN) HobEnd; + + return Hob; +} + + +/** + Returns the next instance of a HOB type from the starting HOB. + + This function searches the first instance of a HOB type from the starting HOB pointer. + If there does not exist such HOB type from the starting HOB pointer, it will return NULL. + In contrast with macro GET_NEXT_HOB(), this function does not skip the starting HOB pointer + unconditionally: it returns HobStart back if HobStart itself meets the requirement; + caller is required to use GET_NEXT_HOB() if it wishes to skip current HobStart. + + If HobStart is NULL, then ASSERT(). + + @param Type The HOB type to return. + @param HobStart The starting HOB pointer to search from. + + @return The next instance of a HOB type from the starting HOB. + +**/ +VOID * +EFIAPI +GetNextHob ( + IN UINT16 Type, + IN CONST VOID *HobStart + ) +{ + EFI_PEI_HOB_POINTERS Hob; + + Hob.Raw = (UINT8 *) HobStart; + // + // Parse the HOB list until end of list or matching type is found. + // + while (!END_OF_HOB_LIST (Hob)) { + if (Hob.Header->HobType == Type) { + return Hob.Raw; + } + Hob.Raw = GET_NEXT_HOB (Hob); + } + return NULL; +} + +/** + Returns the first instance of a HOB type among the whole HOB list. + + This function searches the first instance of a HOB type among the whole HOB list. + If there does not exist such HOB type in the HOB list, it will return NULL. + + If the pointer to the HOB list is NULL, then ASSERT(). + + @param Type The HOB type to return. + + @return The next instance of a HOB type from the starting HOB. + +**/ +VOID * +EFIAPI +GetFirstHob ( + IN UINT16 Type + ) +{ + VOID *HobList; + + HobList = GetHobList (); + return GetNextHob (Type, HobList); +} + +/** + Returns the next instance of the matched GUID HOB from the starting HOB. + + This function searches the first instance of a HOB from the starting HOB pointer. + Such HOB should satisfy two conditions: + its HOB type is EFI_HOB_TYPE_GUID_EXTENSION and its GUID Name equals to the input Guid. + If there does not exist such HOB from the starting HOB pointer, it will return NULL. + Caller is required to apply GET_GUID_HOB_DATA () and GET_GUID_HOB_DATA_SIZE () + to extract the data section and its size information, respectively. + In contrast with macro GET_NEXT_HOB(), this function does not skip the starting HOB pointer + unconditionally: it returns HobStart back if HobStart itself meets the requirement; + caller is required to use GET_NEXT_HOB() if it wishes to skip current HobStart. + + If Guid is NULL, then ASSERT(). + If HobStart is NULL, then ASSERT(). + + @param Guid The GUID to match with in the HOB list. + @param HobStart A pointer to a Guid. + + @return The next instance of the matched GUID HOB from the starting HOB. + +**/ +VOID * +EFIAPI +GetNextGuidHob ( + IN CONST EFI_GUID *Guid, + IN CONST VOID *HobStart + ) +{ + EFI_PEI_HOB_POINTERS GuidHob; + + GuidHob.Raw = (UINT8 *) HobStart; + while ((GuidHob.Raw = GetNextHob (EFI_HOB_TYPE_GUID_EXTENSION, GuidHob.Raw)) != NULL) { + if (CompareGuid (Guid, &GuidHob.Guid->Name)) { + break; + } + GuidHob.Raw = GET_NEXT_HOB (GuidHob); + } + return GuidHob.Raw; +} + +/** + Returns the first instance of the matched GUID HOB among the whole HOB list. + + This function searches the first instance of a HOB among the whole HOB list. + Such HOB should satisfy two conditions: + its HOB type is EFI_HOB_TYPE_GUID_EXTENSION and its GUID Name equals to the input Guid. + If there does not exist such HOB from the starting HOB pointer, it will return NULL. + Caller is required to apply GET_GUID_HOB_DATA () and GET_GUID_HOB_DATA_SIZE () + to extract the data section and its size information, respectively. + + If the pointer to the HOB list is NULL, then ASSERT(). + If Guid is NULL, then ASSERT(). + + @param Guid The GUID to match with in the HOB list. + + @return The first instance of the matched GUID HOB among the whole HOB list. + +**/ +VOID * +EFIAPI +GetFirstGuidHob ( + IN CONST EFI_GUID *Guid + ) +{ + VOID *HobList; + + HobList = GetHobList (); + return GetNextGuidHob (Guid, HobList); +} + +/** + Get the system boot mode from the HOB list. + + This function returns the system boot mode information from the + PHIT HOB in HOB list. + + If the pointer to the HOB list is NULL, then ASSERT(). + + @param VOID. + + @return The Boot Mode. + +**/ +EFI_BOOT_MODE +EFIAPI +GetBootModeHob ( + VOID + ) +{ + EFI_BOOT_MODE BootMode; + EFI_PEI_HOB_POINTERS Hob; + + Hob.Raw = GetHobList (); + BootMode = Hob.HandoffInformationTable->BootMode; + + return BootMode; +} + +EFI_STATUS +EFIAPI +SetBootMode ( + IN EFI_BOOT_MODE BootMode + ) +{ + EFI_PEI_HOB_POINTERS Hob; + + Hob.Raw = GetHobList (); + Hob.HandoffInformationTable->BootMode = BootMode; + return BootMode; +} + +/** + Builds a HOB for a loaded PE32 module. + + This function builds a HOB for a loaded PE32 module. + It can only be invoked during PEI phase; + for DXE phase, it will ASSERT() since PEI HOB is read-only for DXE phase. + + If ModuleName is NULL, then ASSERT(). + If there is no additional space for HOB creation, then ASSERT(). + + @param ModuleName The GUID File Name of the module. + @param MemoryAllocationModule The 64 bit physical address of the module. + @param ModuleLength The length of the module in bytes. + @param EntryPoint The 64 bit physical address of the module entry point. + +**/ +VOID +EFIAPI +BuildModuleHob ( + IN CONST EFI_GUID *ModuleName, + IN EFI_PHYSICAL_ADDRESS MemoryAllocationModule, + IN UINT64 ModuleLength, + IN EFI_PHYSICAL_ADDRESS EntryPoint + ) +{ + EFI_HOB_MEMORY_ALLOCATION_MODULE *Hob; + + ASSERT (((MemoryAllocationModule & (EFI_PAGE_SIZE - 1)) == 0) && + ((ModuleLength & (EFI_PAGE_SIZE - 1)) == 0)); + + Hob = CreateHob (EFI_HOB_TYPE_MEMORY_ALLOCATION, (UINT16) sizeof (EFI_HOB_MEMORY_ALLOCATION_MODULE)); + + if (Hob == NULL) { + ASSERT(FALSE); + return; + } + + CopyGuid (&(Hob->MemoryAllocationHeader.Name), &gEfiHobMemoryAllocModuleGuid); + Hob->MemoryAllocationHeader.MemoryBaseAddress = MemoryAllocationModule; + Hob->MemoryAllocationHeader.MemoryLength = ModuleLength; + Hob->MemoryAllocationHeader.MemoryType = EfiBootServicesCode; + + // + // Zero the reserved space to match HOB spec + // + ZeroMem (Hob->MemoryAllocationHeader.Reserved, sizeof (Hob->MemoryAllocationHeader.Reserved)); + + CopyGuid (&Hob->ModuleName, ModuleName); + Hob->EntryPoint = EntryPoint; +} + +/** + Builds a HOB that describes a chunk of system memory with Owner GUID. + + This function builds a HOB that describes a chunk of system memory. + It can only be invoked during PEI phase; + for DXE phase, it will ASSERT() since PEI HOB is read-only for DXE phase. + + If there is no additional space for HOB creation, then ASSERT(). + + @param ResourceType The type of resource described by this HOB. + @param ResourceAttribute The resource attributes of the memory described by this HOB. + @param PhysicalStart The 64 bit physical address of memory described by this HOB. + @param NumberOfBytes The length of the memory described by this HOB in bytes. + @param OwnerGUID GUID for the owner of this resource. + +**/ +VOID +EFIAPI +BuildResourceDescriptorWithOwnerHob ( + IN EFI_RESOURCE_TYPE ResourceType, + IN EFI_RESOURCE_ATTRIBUTE_TYPE ResourceAttribute, + IN EFI_PHYSICAL_ADDRESS PhysicalStart, + IN UINT64 NumberOfBytes, + IN EFI_GUID *OwnerGUID + ) +{ + EFI_HOB_RESOURCE_DESCRIPTOR *Hob; + Hob = CreateHob (EFI_HOB_TYPE_RESOURCE_DESCRIPTOR, (UINT16) sizeof (EFI_HOB_RESOURCE_DESCRIPTOR)); + + if (Hob == NULL) { + ASSERT(FALSE); + return; + } + + Hob->ResourceType = ResourceType; + Hob->ResourceAttribute = ResourceAttribute; + Hob->PhysicalStart = PhysicalStart; + Hob->ResourceLength = NumberOfBytes; + + CopyGuid (&Hob->Owner, OwnerGUID); +} + +/** + Builds a HOB that describes a chunk of system memory. + + This function builds a HOB that describes a chunk of system memory. + It can only be invoked during PEI phase; + for DXE phase, it will ASSERT() since PEI HOB is read-only for DXE phase. + + If there is no additional space for HOB creation, then ASSERT(). + + @param ResourceType The type of resource described by this HOB. + @param ResourceAttribute The resource attributes of the memory described by this HOB. + @param PhysicalStart The 64 bit physical address of memory described by this HOB. + @param NumberOfBytes The length of the memory described by this HOB in bytes. + +**/ +VOID +EFIAPI +BuildResourceDescriptorHob ( + IN EFI_RESOURCE_TYPE ResourceType, + IN EFI_RESOURCE_ATTRIBUTE_TYPE ResourceAttribute, + IN EFI_PHYSICAL_ADDRESS PhysicalStart, + IN UINT64 NumberOfBytes + ) +{ + EFI_HOB_RESOURCE_DESCRIPTOR *Hob; + + Hob = CreateHob (EFI_HOB_TYPE_RESOURCE_DESCRIPTOR, (UINT16) sizeof (EFI_HOB_RESOURCE_DESCRIPTOR)); + + if (Hob == NULL) { + ASSERT(FALSE); + return; + } + + Hob->ResourceType = ResourceType; + Hob->ResourceAttribute = ResourceAttribute; + Hob->PhysicalStart = PhysicalStart; + Hob->ResourceLength = NumberOfBytes; + ZeroMem (&(Hob->Owner), sizeof (EFI_GUID)); +} + +/** + Builds a customized HOB tagged with a GUID for identification and returns + the start address of GUID HOB data. + + This function builds a customized HOB tagged with a GUID for identification + and returns the start address of GUID HOB data so that caller can fill the customized data. + The HOB Header and Name field is already stripped. + It can only be invoked during PEI phase; + for DXE phase, it will ASSERT() since PEI HOB is read-only for DXE phase. + + If Guid is NULL, then ASSERT(). + If there is no additional space for HOB creation, then ASSERT(). + If DataLength > (0xFFF8 - sizeof (EFI_HOB_GUID_TYPE)), then ASSERT(). + HobLength is UINT16 and multiples of 8 bytes, so the max HobLength is 0xFFF8. + + @param Guid The GUID to tag the customized HOB. + @param DataLength The size of the data payload for the GUID HOB. + + @retval NULL The GUID HOB could not be allocated. + @retval others The start address of GUID HOB data. + +**/ +VOID * +EFIAPI +BuildGuidHob ( + IN CONST EFI_GUID *Guid, + IN UINTN DataLength + ) +{ + EFI_HOB_GUID_TYPE *Hob; + + // + // Make sure Guid is valid + // + ASSERT (Guid != NULL); + + // + // Make sure that data length is not too long. + // + ASSERT (DataLength <= (0xFFF8 - sizeof (EFI_HOB_GUID_TYPE))); + + Hob = CreateHob (EFI_HOB_TYPE_GUID_EXTENSION, (UINT16) (sizeof (EFI_HOB_GUID_TYPE) + DataLength)); + + if (Hob == NULL) { + ASSERT(FALSE); + return Hob; + } + CopyGuid (&Hob->Name, Guid); + return Hob + 1; +} + +/** + Builds a customized HOB tagged with a GUID for identification, copies the input data to the HOB + data field, and returns the start address of the GUID HOB data. + + This function builds a customized HOB tagged with a GUID for identification and copies the input + data to the HOB data field and returns the start address of the GUID HOB data. It can only be + invoked during PEI phase; for DXE phase, it will ASSERT() since PEI HOB is read-only for DXE phase. + The HOB Header and Name field is already stripped. + It can only be invoked during PEI phase; + for DXE phase, it will ASSERT() since PEI HOB is read-only for DXE phase. + + If Guid is NULL, then ASSERT(). + If Data is NULL and DataLength > 0, then ASSERT(). + If there is no additional space for HOB creation, then ASSERT(). + If DataLength > (0xFFF8 - sizeof (EFI_HOB_GUID_TYPE)), then ASSERT(). + HobLength is UINT16 and multiples of 8 bytes, so the max HobLength is 0xFFF8. + + @param Guid The GUID to tag the customized HOB. + @param Data The data to be copied into the data field of the GUID HOB. + @param DataLength The size of the data payload for the GUID HOB. + + @retval NULL The GUID HOB could not be allocated. + @retval others The start address of GUID HOB data. + +**/ +VOID * +EFIAPI +BuildGuidDataHob ( + IN CONST EFI_GUID *Guid, + IN VOID *Data, + IN UINTN DataLength + ) +{ + VOID *HobData; + + ASSERT (Data != NULL || DataLength == 0); + + HobData = BuildGuidHob (Guid, DataLength); + if (HobData == NULL) { + return HobData; + } + + return CopyMem (HobData, Data, DataLength); +} + +/** + Check FV alignment. + + @param BaseAddress The base address of the Firmware Volume. + @param Length The size of the Firmware Volume in bytes. + + @retval TRUE FvImage buffer is at its required alignment. + @retval FALSE FvImage buffer is not at its required alignment. + +**/ +BOOLEAN +InternalCheckFvAlignment ( + IN EFI_PHYSICAL_ADDRESS BaseAddress, + IN UINT64 Length + ) +{ + EFI_FIRMWARE_VOLUME_HEADER *FwVolHeader; + UINT32 FvAlignment; + + FvAlignment = 0; + FwVolHeader = (EFI_FIRMWARE_VOLUME_HEADER *) (UINTN) BaseAddress; + + // + // If EFI_FVB2_WEAK_ALIGNMENT is set in the volume header then the first byte of the volume + // can be aligned on any power-of-two boundary. A weakly aligned volume can not be moved from + // its initial linked location and maintain its alignment. + // + if ((FwVolHeader->Attributes & EFI_FVB2_WEAK_ALIGNMENT) != EFI_FVB2_WEAK_ALIGNMENT) { + // + // Get FvHeader alignment + // + FvAlignment = 1 << ((FwVolHeader->Attributes & EFI_FVB2_ALIGNMENT) >> 16); + // + // FvAlignment must be greater than or equal to 8 bytes of the minimum FFS alignment value. + // + if (FvAlignment < 8) { + FvAlignment = 8; + } + if ((UINTN)BaseAddress % FvAlignment != 0) { + // + // FvImage buffer is not at its required alignment. + // + DEBUG (( + DEBUG_ERROR, + "Unaligned FvImage found at 0x%lx:0x%lx, the required alignment is 0x%x\n", + BaseAddress, + Length, + FvAlignment + )); + return FALSE; + } + } + + return TRUE; +} + +VOID +EFIAPI +BuildFvHobs ( + IN EFI_PHYSICAL_ADDRESS PhysicalStart, + IN UINT64 NumberOfBytes, + IN EFI_RESOURCE_ATTRIBUTE_TYPE *ResourceAttribute OPTIONAL + ) +{ + + EFI_RESOURCE_ATTRIBUTE_TYPE Resource; + + BuildFvHob (PhysicalStart, NumberOfBytes); + + if (ResourceAttribute == NULL) { + Resource = (EFI_RESOURCE_ATTRIBUTE_PRESENT | + EFI_RESOURCE_ATTRIBUTE_INITIALIZED | + EFI_RESOURCE_ATTRIBUTE_TESTED | + EFI_RESOURCE_ATTRIBUTE_WRITE_BACK_CACHEABLE); + } else { + Resource = *ResourceAttribute; + } + + BuildResourceDescriptorHob (EFI_RESOURCE_FIRMWARE_DEVICE, Resource, PhysicalStart, NumberOfBytes); +} + + +/** + Builds a Firmware Volume HOB. + + This function builds a Firmware Volume HOB. + It can only be invoked during PEI phase; + for DXE phase, it will ASSERT() since PEI HOB is read-only for DXE phase. + + If there is no additional space for HOB creation, then ASSERT(). + If the FvImage buffer is not at its required alignment, then ASSERT(). + + @param BaseAddress The base address of the Firmware Volume. + @param Length The size of the Firmware Volume in bytes. + +**/ +VOID +EFIAPI +BuildFvHob ( + IN EFI_PHYSICAL_ADDRESS BaseAddress, + IN UINT64 Length + ) +{ + EFI_HOB_FIRMWARE_VOLUME *Hob; + + if (!InternalCheckFvAlignment (BaseAddress, Length)) { + ASSERT (FALSE); + return; + } + + Hob = CreateHob (EFI_HOB_TYPE_FV, (UINT16) sizeof (EFI_HOB_FIRMWARE_VOLUME)); + + if (Hob == NULL) { + ASSERT(FALSE); + return; + } + + Hob->BaseAddress = BaseAddress; + Hob->Length = Length; +} + +/** + Builds a EFI_HOB_TYPE_FV2 HOB. + + This function builds a EFI_HOB_TYPE_FV2 HOB. + It can only be invoked during PEI phase; + for DXE phase, it will ASSERT() since PEI HOB is read-only for DXE phase. + + If there is no additional space for HOB creation, then ASSERT(). + If the FvImage buffer is not at its required alignment, then ASSERT(). + + @param BaseAddress The base address of the Firmware Volume. + @param Length The size of the Firmware Volume in bytes. + @param FvName The name of the Firmware Volume. + @param FileName The name of the file. + +**/ +VOID +EFIAPI +BuildFv2Hob ( + IN EFI_PHYSICAL_ADDRESS BaseAddress, + IN UINT64 Length, + IN CONST EFI_GUID *FvName, + IN CONST EFI_GUID *FileName + ) +{ + EFI_HOB_FIRMWARE_VOLUME2 *Hob; + + if (!InternalCheckFvAlignment (BaseAddress, Length)) { + ASSERT (FALSE); + return; + } + + Hob = CreateHob (EFI_HOB_TYPE_FV2, (UINT16) sizeof (EFI_HOB_FIRMWARE_VOLUME2)); + + if (Hob == NULL) { + ASSERT(FALSE); + return; + } + + Hob->BaseAddress = BaseAddress; + Hob->Length = Length; + CopyGuid (&Hob->FvName, FvName); + CopyGuid (&Hob->FileName, FileName); +} + +/** + Builds a EFI_HOB_TYPE_FV3 HOB. + + This function builds a EFI_HOB_TYPE_FV3 HOB. + It can only be invoked during PEI phase; + for DXE phase, it will ASSERT() since PEI HOB is read-only for DXE phase. + + If there is no additional space for HOB creation, then ASSERT(). + If the FvImage buffer is not at its required alignment, then ASSERT(). + + @param BaseAddress The base address of the Firmware Volume. + @param Length The size of the Firmware Volume in bytes. + @param AuthenticationStatus The authentication status. + @param ExtractedFv TRUE if the FV was extracted as a file within + another firmware volume. FALSE otherwise. + @param FvName The name of the Firmware Volume. + Valid only if IsExtractedFv is TRUE. + @param FileName The name of the file. + Valid only if IsExtractedFv is TRUE. + +**/ +VOID +EFIAPI +BuildFv3Hob ( + IN EFI_PHYSICAL_ADDRESS BaseAddress, + IN UINT64 Length, + IN UINT32 AuthenticationStatus, + IN BOOLEAN ExtractedFv, + IN CONST EFI_GUID *FvName, OPTIONAL + IN CONST EFI_GUID *FileName OPTIONAL + ) +{ + EFI_HOB_FIRMWARE_VOLUME3 *Hob; + + if (!InternalCheckFvAlignment (BaseAddress, Length)) { + ASSERT (FALSE); + return; + } + + Hob = CreateHob (EFI_HOB_TYPE_FV3, (UINT16) sizeof (EFI_HOB_FIRMWARE_VOLUME3)); + + if (Hob == NULL) { + ASSERT(FALSE); + return; + } + + Hob->BaseAddress = BaseAddress; + Hob->Length = Length; + Hob->AuthenticationStatus = AuthenticationStatus; + Hob->ExtractedFv = ExtractedFv; + if (ExtractedFv) { + CopyGuid (&Hob->FvName, FvName); + CopyGuid (&Hob->FileName, FileName); + } +} + +/** + Builds a Capsule Volume HOB. + + This function builds a Capsule Volume HOB. + It can only be invoked during PEI phase; + for DXE phase, it will ASSERT() since PEI HOB is read-only for DXE phase. + + If the platform does not support Capsule Volume HOBs, then ASSERT(). + If there is no additional space for HOB creation, then ASSERT(). + + @param BaseAddress The base address of the Capsule Volume. + @param Length The size of the Capsule Volume in bytes. + +**/ +VOID +EFIAPI +BuildCvHob ( + IN EFI_PHYSICAL_ADDRESS BaseAddress, + IN UINT64 Length + ) +{ + ASSERT(FALSE); +} + +/** + Builds a HOB for the CPU. + + This function builds a HOB for the CPU. + It can only be invoked during PEI phase; + for DXE phase, it will ASSERT() since PEI HOB is read-only for DXE phase. + + If there is no additional space for HOB creation, then ASSERT(). + + @param SizeOfMemorySpace The maximum physical memory addressability of the processor. + @param SizeOfIoSpace The maximum physical I/O addressability of the processor. + +**/ +VOID +EFIAPI +BuildCpuHob ( + IN UINT8 SizeOfMemorySpace, + IN UINT8 SizeOfIoSpace + ) +{ + EFI_HOB_CPU *Hob; + + Hob = CreateHob (EFI_HOB_TYPE_CPU, (UINT16) sizeof (EFI_HOB_CPU)); + + if (Hob == NULL) { + ASSERT(FALSE); + return; + } + + Hob->SizeOfMemorySpace = SizeOfMemorySpace; + Hob->SizeOfIoSpace = SizeOfIoSpace; + + // + // Zero the reserved space to match HOB spec + // + ZeroMem (Hob->Reserved, sizeof (Hob->Reserved)); +} + +/** + Builds a HOB for the Stack. + + This function builds a HOB for the stack. + It can only be invoked during PEI phase; + for DXE phase, it will ASSERT() since PEI HOB is read-only for DXE phase. + + If there is no additional space for HOB creation, then ASSERT(). + + @param BaseAddress The 64 bit physical address of the Stack. + @param Length The length of the stack in bytes. + +**/ +VOID +EFIAPI +BuildStackHob ( + IN EFI_PHYSICAL_ADDRESS BaseAddress, + IN UINT64 Length + ) +{ + EFI_HOB_MEMORY_ALLOCATION_STACK *Hob; + + ASSERT (((BaseAddress & (EFI_PAGE_SIZE - 1)) == 0) && + ((Length & (EFI_PAGE_SIZE - 1)) == 0)); + + Hob = CreateHob (EFI_HOB_TYPE_MEMORY_ALLOCATION, (UINT16) sizeof (EFI_HOB_MEMORY_ALLOCATION_STACK)); + + if (Hob == NULL) { + ASSERT(FALSE); + return; + } + + CopyGuid (&(Hob->AllocDescriptor.Name), &gEfiHobMemoryAllocStackGuid); + Hob->AllocDescriptor.MemoryBaseAddress = BaseAddress; + Hob->AllocDescriptor.MemoryLength = Length; + Hob->AllocDescriptor.MemoryType = EfiBootServicesData; + + // + // Zero the reserved space to match HOB spec + // + ZeroMem (Hob->AllocDescriptor.Reserved, sizeof (Hob->AllocDescriptor.Reserved)); +} + +/** + Builds a HOB for the BSP store. + + This function builds a HOB for BSP store. + It can only be invoked during PEI phase; + for DXE phase, it will ASSERT() since PEI HOB is read-only for DXE phase. + + If there is no additional space for HOB creation, then ASSERT(). + + @param BaseAddress The 64 bit physical address of the BSP. + @param Length The length of the BSP store in bytes. + @param MemoryType The type of memory allocated by this HOB. + +**/ +VOID +EFIAPI +BuildBspStoreHob ( + IN EFI_PHYSICAL_ADDRESS BaseAddress, + IN UINT64 Length, + IN EFI_MEMORY_TYPE MemoryType + ) +{ + ASSERT(FALSE); +} + +/** + Update the Stack Hob if the stack has been moved + + @param BaseAddress The 64 bit physical address of the Stack. + @param Length The length of the stack in bytes. + +**/ +VOID +UpdateStackHob ( + IN EFI_PHYSICAL_ADDRESS BaseAddress, + IN UINT64 Length + ) +{ + EFI_PEI_HOB_POINTERS Hob; + + Hob.Raw = GetHobList (); + while ((Hob.Raw = GetNextHob (EFI_HOB_TYPE_MEMORY_ALLOCATION, Hob.Raw)) != NULL) { + if (CompareGuid (&gEfiHobMemoryAllocStackGuid, &(Hob.MemoryAllocationStack->AllocDescriptor.Name))) { + // + // Build a new memory allocation HOB with old stack info with EfiConventionalMemory type + // to be reclaimed by DXE core. + // + BuildMemoryAllocationHob ( + Hob.MemoryAllocationStack->AllocDescriptor.MemoryBaseAddress, + Hob.MemoryAllocationStack->AllocDescriptor.MemoryLength, + EfiConventionalMemory + ); + // + // Update the BSP Stack Hob to reflect the new stack info. + // + Hob.MemoryAllocationStack->AllocDescriptor.MemoryBaseAddress = BaseAddress; + Hob.MemoryAllocationStack->AllocDescriptor.MemoryLength = Length; + break; + } + Hob.Raw = GET_NEXT_HOB (Hob); + } + +} + + +/** + Builds a HOB for the memory allocation. + + This function builds a HOB for the memory allocation. + It can only be invoked during PEI phase; + for DXE phase, it will ASSERT() since PEI HOB is read-only for DXE phase. + + If there is no additional space for HOB creation, then ASSERT(). + + @param BaseAddress The 64 bit physical address of the memory. + @param Length The length of the memory allocation in bytes. + @param MemoryType The type of memory allocated by this HOB. + +**/ +VOID +EFIAPI +BuildMemoryAllocationHob ( + IN EFI_PHYSICAL_ADDRESS BaseAddress, + IN UINT64 Length, + IN EFI_MEMORY_TYPE MemoryType + ) +{ + EFI_HOB_MEMORY_ALLOCATION *Hob; + + ASSERT (((BaseAddress & (EFI_PAGE_SIZE - 1)) == 0) && + ((Length & (EFI_PAGE_SIZE - 1)) == 0)); + + Hob = CreateHob (EFI_HOB_TYPE_MEMORY_ALLOCATION, (UINT16) sizeof (EFI_HOB_MEMORY_ALLOCATION)); + + if (Hob == NULL) { + ASSERT(FALSE); + return; + } + + ZeroMem (&(Hob->AllocDescriptor.Name), sizeof (EFI_GUID)); + Hob->AllocDescriptor.MemoryBaseAddress = BaseAddress; + Hob->AllocDescriptor.MemoryLength = Length; + Hob->AllocDescriptor.MemoryType = MemoryType; + // + // Zero the reserved space to match HOB spec + // + ZeroMem (Hob->AllocDescriptor.Reserved, sizeof (Hob->AllocDescriptor.Reserved)); +} diff --git a/MdePkg/Library/SecHobLib/SecHobLib.inf b/MdePkg/Library/SecHobLib/SecHobLib.inf new file mode 100644 index 000000000000..95117bcf9ae4 --- /dev/null +++ b/MdePkg/Library/SecHobLib/SecHobLib.inf @@ -0,0 +1,50 @@ +## @file +# Instance of HOB Library using PEI Services. +# +# HOB Library implementation that uses PEI Services to retrieve the HOB List. +# +# Copyright (c) 2021, Intel Corporation. All rights reserved.
+# +# SPDX-License-Identifier: BSD-2-Clause-Patent +# +# +## + +[Defines] + INF_VERSION = 0x00010005 + BASE_NAME = SecHobLib + MODULE_UNI_FILE = SecHobLib.uni + FILE_GUID = 95A20151-9407-401A-89B3-5B4D9CDB932A + MODULE_TYPE = BASE + VERSION_STRING = 1.0 + LIBRARY_CLASS = HobLib|SEC + + +# +# VALID_ARCHITECTURES = IA32 X64 EBC (EBC is for build only) +# + +[Sources] + HobLib.c + + +[Packages] + MdePkg/MdePkg.dec + #OvmfPkg/OvmfPkg.dec + +[LibraryClasses] + BaseMemoryLib + DebugLib + +[Guids] + gEfiHobMemoryAllocStackGuid ## SOMETIMES_PRODUCES ## HOB # MemoryAllocation StackHob + gEfiHobMemoryAllocBspStoreGuid ## SOMETIMES_PRODUCES ## HOB # MemoryAllocation BspStoreHob + gEfiHobMemoryAllocModuleGuid ## SOMETIMES_PRODUCES ## HOB # MemoryAllocation ModuleHob + +# +# [Hob] +# MEMORY_ALLOCATION ## SOMETIMES_PRODUCES +# RESOURCE_DESCRIPTOR ## SOMETIMES_PRODUCES +# FIRMWARE_VOLUME ## SOMETIMES_PRODUCES +# + diff --git a/MdePkg/Library/SecHobLib/SecHobLib.uni b/MdePkg/Library/SecHobLib/SecHobLib.uni new file mode 100644 index 000000000000..071ac1c2cac9 --- /dev/null +++ b/MdePkg/Library/SecHobLib/SecHobLib.uni @@ -0,0 +1,16 @@ +// /** @file +// Instance of HOB Library using PEI Services. +// +// HOB Library implementation that retrieve the HOB List in SEC phase. +// +// Copyright (c) 2021, Intel Corporation. All rights reserved.
+// +// SPDX-License-Identifier: BSD-2-Clause-Patent +// +// **/ + + +#string STR_MODULE_ABSTRACT #language en-US "Instance of HOB Library in SEC Phase" + +#string STR_MODULE_DESCRIPTION #language en-US "HOB Library implementation that retrieve the HOB List in SEC Phase." + diff --git a/MdePkg/Library/SecMemoryAllocationLib/MemoryAllocationLib.c b/MdePkg/Library/SecMemoryAllocationLib/MemoryAllocationLib.c new file mode 100644 index 000000000000..19245e3f6963 --- /dev/null +++ b/MdePkg/Library/SecMemoryAllocationLib/MemoryAllocationLib.c @@ -0,0 +1,827 @@ +/** @file + Support routines for memory allocation routines + based on PeiService for PEI phase drivers. + + Copyright (c) 2006 - 2018, Intel Corporation. All rights reserved.
+ SPDX-License-Identifier: BSD-2-Clause-Patent + +**/ + + +#include + + +#include +#include +#include +#include +#include + +/** + Allocates one or more 4KB pages of type EfiBootServicesData. + + Allocates the number of 4KB pages of type EfiBootServicesData and returns a pointer to the + allocated buffer. The buffer returned is aligned on a 4KB boundary. If Pages is 0, then NULL + is returned. If there is not enough memory remaining to satisfy the request, then NULL is + returned. + + @param Pages The number of 4 KB pages to allocate. + + @return A pointer to the allocated buffer or NULL if allocation fails. + +**/ +VOID * +EFIAPI +AllocatePages ( + IN UINTN Pages + ) +{ + return AllocatePagesWithMemoryType(EfiBootServicesData, Pages); +} + +/** + Allocates one or more 4KB pages of type EfiRuntimeServicesData. + + Allocates the number of 4KB pages of type EfiRuntimeServicesData and returns a pointer to the + allocated buffer. The buffer returned is aligned on a 4KB boundary. If Pages is 0, then NULL + is returned. If there is not enough memory remaining to satisfy the request, then NULL is + returned. + + @param Pages The number of 4 KB pages to allocate. + + @return A pointer to the allocated buffer or NULL if allocation fails. + +**/ +VOID * +EFIAPI +AllocateRuntimePages ( + IN UINTN Pages + ) +{ + ASSERT(FALSE); + return NULL; +} + +/** + Allocates one or more 4KB pages of type EfiReservedMemoryType. + + Allocates the number of 4KB pages of type EfiReservedMemoryType and returns a pointer to the + allocated buffer. The buffer returned is aligned on a 4KB boundary. If Pages is 0, then NULL + is returned. If there is not enough memory remaining to satisfy the request, then NULL is + returned. + + @param Pages The number of 4 KB pages to allocate. + + @return A pointer to the allocated buffer or NULL if allocation fails. + +**/ +VOID * +EFIAPI +AllocateReservedPages ( + IN UINTN Pages + ) +{ + ASSERT(FALSE); + return NULL; +} + +/** + Frees one or more 4KB pages that were previously allocated with one of the page allocation + functions in the Memory Allocation Library. + + Frees the number of 4KB pages specified by Pages from the buffer specified by Buffer. Buffer + must have been allocated on a previous call to the page allocation services of the Memory + Allocation Library. If it is not possible to free allocated pages, then this function will + perform no actions. + + If Buffer was not allocated with a page allocation function in the Memory Allocation Library, + then ASSERT(). + If Pages is zero, then ASSERT(). + + @param Buffer The pointer to the buffer of pages to free. + @param Pages The number of 4 KB pages to free. + +**/ +VOID +EFIAPI +FreePages ( + IN VOID *Buffer, + IN UINTN Pages + ) +{ + ASSERT(FALSE); +} + + +/** + Allocates one or more 4KB pages of type EfiBootServicesData at a specified alignment. + + Allocates the number of 4KB pages specified by Pages of type EfiBootServicesData with an + alignment specified by Alignment. The allocated buffer is returned. If Pages is 0, then NULL is + returned. If there is not enough memory at the specified alignment remaining to satisfy the + request, then NULL is returned. + + If Alignment is not a power of two and Alignment is not zero, then ASSERT(). + If Pages plus EFI_SIZE_TO_PAGES (Alignment) overflows, then ASSERT(). + + @param Pages The number of 4 KB pages to allocate. + @param Alignment The requested alignment of the allocation. + Must be a power of two. + If Alignment is zero, then byte alignment is used. + + @return A pointer to the allocated buffer or NULL if allocation fails. + +**/ +VOID * +EFIAPI +AllocateAlignedPages ( + IN UINTN Pages, + IN UINTN Alignment + ) +{ + VOID *Memory; + UINTN AlignmentMask; + + // + // Alignment must be a power of two or zero. + // + ASSERT ((Alignment & (Alignment - 1)) == 0); + + if (Pages == 0) { + return NULL; + } + // + // Make sure that Pages plus EFI_SIZE_TO_PAGES (Alignment) does not overflow. + // + ASSERT (Pages <= (MAX_ADDRESS - EFI_SIZE_TO_PAGES (Alignment))); + // + // We would rather waste some memory to save PEI code size. + // + Memory = (VOID *)(UINTN)AllocatePages (Pages + EFI_SIZE_TO_PAGES (Alignment)); + if (Alignment == 0) { + AlignmentMask = Alignment; + } else { + AlignmentMask = Alignment - 1; + } + return (VOID *) (UINTN) (((UINTN) Memory + AlignmentMask) & ~AlignmentMask); +} + +/** + Allocates one or more 4KB pages of type EfiRuntimeServicesData at a specified alignment. + + Allocates the number of 4KB pages specified by Pages of type EfiRuntimeServicesData with an + alignment specified by Alignment. The allocated buffer is returned. If Pages is 0, then NULL is + returned. If there is not enough memory at the specified alignment remaining to satisfy the + request, then NULL is returned. + + If Alignment is not a power of two and Alignment is not zero, then ASSERT(). + If Pages plus EFI_SIZE_TO_PAGES (Alignment) overflows, then ASSERT(). + + @param Pages The number of 4 KB pages to allocate. + @param Alignment The requested alignment of the allocation. + Must be a power of two. + If Alignment is zero, then byte alignment is used. + + @return A pointer to the allocated buffer or NULL if allocation fails. + +**/ +VOID * +EFIAPI +AllocateAlignedRuntimePages ( + IN UINTN Pages, + IN UINTN Alignment + ) +{ + ASSERT(FALSE); + return NULL; +} + +/** + Allocates one or more 4KB pages of type EfiReservedMemoryType at a specified alignment. + + Allocates the number of 4KB pages specified by Pages of type EfiReservedMemoryType with an + alignment specified by Alignment. The allocated buffer is returned. If Pages is 0, then NULL is + returned. If there is not enough memory at the specified alignment remaining to satisfy the + request, then NULL is returned. + + If Alignment is not a power of two and Alignment is not zero, then ASSERT(). + If Pages plus EFI_SIZE_TO_PAGES (Alignment) overflows, then ASSERT(). + + @param Pages The number of 4 KB pages to allocate. + @param Alignment The requested alignment of the allocation. + Must be a power of two. + If Alignment is zero, then byte alignment is used. + + @return A pointer to the allocated buffer or NULL if allocation fails. + +**/ +VOID * +EFIAPI +AllocateAlignedReservedPages ( + IN UINTN Pages, + IN UINTN Alignment + ) +{ + ASSERT(FALSE); + return NULL; +} + +/** + Frees one or more 4KB pages that were previously allocated with one of the aligned page + allocation functions in the Memory Allocation Library. + + Frees the number of 4KB pages specified by Pages from the buffer specified by Buffer. Buffer + must have been allocated on a previous call to the aligned page allocation services of the Memory + Allocation Library. If it is not possible to free allocated pages, then this function will + perform no actions. + + If Buffer was not allocated with an aligned page allocation function in the Memory Allocation + Library, then ASSERT(). + If Pages is zero, then ASSERT(). + + @param Buffer The pointer to the buffer of pages to free. + @param Pages The number of 4 KB pages to free. + +**/ +VOID +EFIAPI +FreeAlignedPages ( + IN VOID *Buffer, + IN UINTN Pages + ) +{ + ASSERT(FALSE); +} + +/** + Allocates a buffer of a certain pool type. + + Allocates the number bytes specified by AllocationSize of a certain pool type and returns a + pointer to the allocated buffer. If AllocationSize is 0, then a valid buffer of 0 size is + returned. If there is not enough memory remaining to satisfy the request, then NULL is returned. + + @param MemoryType The type of memory to allocate. + @param AllocationSize The number of bytes to allocate. + + @return A pointer to the allocated buffer or NULL if allocation fails. + +**/ +VOID * +InternalAllocatePool ( + IN EFI_MEMORY_TYPE MemoryType, + IN UINTN AllocationSize + ) +{ + ASSERT(FALSE); + return NULL; +} + +/** + Allocates a buffer of type EfiBootServicesData. + + Allocates the number bytes specified by AllocationSize of type EfiBootServicesData and returns a + pointer to the allocated buffer. If AllocationSize is 0, then a valid buffer of 0 size is + returned. If there is not enough memory remaining to satisfy the request, then NULL is returned. + + @param AllocationSize The number of bytes to allocate. + + @return A pointer to the allocated buffer or NULL if allocation fails. + +**/ +VOID * +EFIAPI +TdAllocatePool ( + IN UINTN AllocationSize + ) +{ + EFI_HOB_MEMORY_POOL *Hob; + + Hob = GetHobList (); + + // + // Verify that there is sufficient memory to satisfy the allocation + // + if (AllocationSize > 0x10000) { + // Please call AllocatePages for big allocations + return 0; + } else { + + Hob = (EFI_HOB_MEMORY_POOL *)CreateHob (EFI_HOB_TYPE_MEMORY_POOL, (UINT16)(sizeof (EFI_HOB_TYPE_MEMORY_POOL) + AllocationSize)); + return (VOID *)(Hob + 1); + } +} + +/** + Allocates a buffer of type EfiBootServicesData. + + Allocates the number bytes specified by AllocationSize of type EfiBootServicesData and returns a + pointer to the allocated buffer. If AllocationSize is 0, then a valid buffer of 0 size is + returned. If there is not enough memory remaining to satisfy the request, then NULL is returned. + + @param AllocationSize The number of bytes to allocate. + + @return A pointer to the allocated buffer or NULL if allocation fails. + +**/ +VOID * +EFIAPI +AllocatePool ( + IN UINTN AllocationSize + ) +{ + EFI_HOB_MEMORY_POOL *Hob; + + Hob = GetHobList (); + + // + // Verify that there is sufficient memory to satisfy the allocation + // + if (AllocationSize > 0x10000) { + // Please call AllocatePages for big allocations + return 0; + } else { + Hob = (EFI_HOB_MEMORY_POOL *)CreateHob (EFI_HOB_TYPE_MEMORY_POOL, (UINT16)(sizeof (EFI_HOB_TYPE_MEMORY_POOL) + AllocationSize)); + return (VOID *)(Hob + 1); + } +} + +/** + Allocates a buffer of type EfiRuntimeServicesData. + + Allocates the number bytes specified by AllocationSize of type EfiRuntimeServicesData and returns + a pointer to the allocated buffer. If AllocationSize is 0, then a valid buffer of 0 size is + returned. If there is not enough memory remaining to satisfy the request, then NULL is returned. + + @param AllocationSize The number of bytes to allocate. + + @return A pointer to the allocated buffer or NULL if allocation fails. + +**/ +VOID * +EFIAPI +AllocateRuntimePool ( + IN UINTN AllocationSize + ) +{ + ASSERT(FALSE); + return NULL; +} + +/** + Allocates a buffer of type EfiReservedMemoryType. + + Allocates the number bytes specified by AllocationSize of type EfiReservedMemoryType and returns + a pointer to the allocated buffer. If AllocationSize is 0, then a valid buffer of 0 size is + returned. If there is not enough memory remaining to satisfy the request, then NULL is returned. + + @param AllocationSize The number of bytes to allocate. + + @return A pointer to the allocated buffer or NULL if allocation fails. + +**/ +VOID * +EFIAPI +AllocateReservedPool ( + IN UINTN AllocationSize + ) +{ + ASSERT(FALSE); + return NULL; +} + +/** + Allocates and zeros a buffer of a certain pool type. + + Allocates the number bytes specified by AllocationSize of a certain pool type, clears the buffer + with zeros, and returns a pointer to the allocated buffer. If AllocationSize is 0, then a valid + buffer of 0 size is returned. If there is not enough memory remaining to satisfy the request, + then NULL is returned. + + @param PoolType The type of memory to allocate. + @param AllocationSize The number of bytes to allocate and zero. + + @return A pointer to the allocated buffer or NULL if allocation fails. + +**/ +VOID * +InternalAllocateZeroPool ( + IN EFI_MEMORY_TYPE PoolType, + IN UINTN AllocationSize + ) +{ + ASSERT(FALSE); + return NULL; +} + +/** + Allocates and zeros a buffer of type EfiBootServicesData. + + Allocates the number bytes specified by AllocationSize of type EfiBootServicesData, clears the + buffer with zeros, and returns a pointer to the allocated buffer. If AllocationSize is 0, then a + valid buffer of 0 size is returned. If there is not enough memory remaining to satisfy the + request, then NULL is returned. + + @param AllocationSize The number of bytes to allocate and zero. + + @return A pointer to the allocated buffer or NULL if allocation fails. + +**/ +VOID * +EFIAPI +AllocateZeroPool ( + IN UINTN AllocationSize + ) +{ + VOID *Memory; + + Memory = AllocatePool (AllocationSize); + if (Memory != NULL) { + Memory = ZeroMem (Memory, AllocationSize); + } + return Memory; +} + +/** + Allocates and zeros a buffer of type EfiRuntimeServicesData. + + Allocates the number bytes specified by AllocationSize of type EfiRuntimeServicesData, clears the + buffer with zeros, and returns a pointer to the allocated buffer. If AllocationSize is 0, then a + valid buffer of 0 size is returned. If there is not enough memory remaining to satisfy the + request, then NULL is returned. + + @param AllocationSize The number of bytes to allocate and zero. + + @return A pointer to the allocated buffer or NULL if allocation fails. + +**/ +VOID * +EFIAPI +AllocateRuntimeZeroPool ( + IN UINTN AllocationSize + ) +{ + ASSERT(FALSE); + return NULL; +} + +/** + Allocates and zeros a buffer of type EfiReservedMemoryType. + + Allocates the number bytes specified by AllocationSize of type EfiReservedMemoryType, clears the + buffer with zeros, and returns a pointer to the allocated buffer. If AllocationSize is 0, then a + valid buffer of 0 size is returned. If there is not enough memory remaining to satisfy the + request, then NULL is returned. + + @param AllocationSize The number of bytes to allocate and zero. + + @return A pointer to the allocated buffer or NULL if allocation fails. + +**/ +VOID * +EFIAPI +AllocateReservedZeroPool ( + IN UINTN AllocationSize + ) +{ + ASSERT(FALSE); + return NULL; +} + +/** + Copies a buffer to an allocated buffer of a certain pool type. + + Allocates the number bytes specified by AllocationSize of a certain pool type, copies + AllocationSize bytes from Buffer to the newly allocated buffer, and returns a pointer to the + allocated buffer. If AllocationSize is 0, then a valid buffer of 0 size is returned. If there + is not enough memory remaining to satisfy the request, then NULL is returned. + If Buffer is NULL, then ASSERT(). + If AllocationSize is greater than (MAX_ADDRESS - Buffer + 1), then ASSERT(). + + @param PoolType The type of pool to allocate. + @param AllocationSize The number of bytes to allocate and zero. + @param Buffer The buffer to copy to the allocated buffer. + + @return A pointer to the allocated buffer or NULL if allocation fails. + +**/ +VOID * +InternalAllocateCopyPool ( + IN EFI_MEMORY_TYPE PoolType, + IN UINTN AllocationSize, + IN CONST VOID *Buffer + ) +{ + ASSERT(FALSE); + return NULL; +} + +/** + Copies a buffer to an allocated buffer of type EfiBootServicesData. + + Allocates the number bytes specified by AllocationSize of type EfiBootServicesData, copies + AllocationSize bytes from Buffer to the newly allocated buffer, and returns a pointer to the + allocated buffer. If AllocationSize is 0, then a valid buffer of 0 size is returned. If there + is not enough memory remaining to satisfy the request, then NULL is returned. + + If Buffer is NULL, then ASSERT(). + If AllocationSize is greater than (MAX_ADDRESS - Buffer + 1), then ASSERT(). + + @param AllocationSize The number of bytes to allocate and zero. + @param Buffer The buffer to copy to the allocated buffer. + + @return A pointer to the allocated buffer or NULL if allocation fails. + +**/ +VOID * +EFIAPI +AllocateCopyPool ( + IN UINTN AllocationSize, + IN CONST VOID *Buffer + ) +{ + VOID *Memory; + + ASSERT (Buffer != NULL); + ASSERT (AllocationSize <= (MAX_ADDRESS - (UINTN) Buffer + 1)); + + Memory = AllocatePool (AllocationSize); + if (Memory != NULL) { + Memory = CopyMem (Memory, Buffer, AllocationSize); + } + return Memory; +} + +/** + Copies a buffer to an allocated buffer of type EfiRuntimeServicesData. + + Allocates the number bytes specified by AllocationSize of type EfiRuntimeServicesData, copies + AllocationSize bytes from Buffer to the newly allocated buffer, and returns a pointer to the + allocated buffer. If AllocationSize is 0, then a valid buffer of 0 size is returned. If there + is not enough memory remaining to satisfy the request, then NULL is returned. + + If Buffer is NULL, then ASSERT(). + If AllocationSize is greater than (MAX_ADDRESS - Buffer + 1), then ASSERT(). + + @param AllocationSize The number of bytes to allocate and zero. + @param Buffer The buffer to copy to the allocated buffer. + + @return A pointer to the allocated buffer or NULL if allocation fails. + +**/ +VOID * +EFIAPI +AllocateRuntimeCopyPool ( + IN UINTN AllocationSize, + IN CONST VOID *Buffer + ) +{ + ASSERT(FALSE); + return NULL; +} + +/** + Copies a buffer to an allocated buffer of type EfiReservedMemoryType. + + Allocates the number bytes specified by AllocationSize of type EfiReservedMemoryType, copies + AllocationSize bytes from Buffer to the newly allocated buffer, and returns a pointer to the + allocated buffer. If AllocationSize is 0, then a valid buffer of 0 size is returned. If there + is not enough memory remaining to satisfy the request, then NULL is returned. + + If Buffer is NULL, then ASSERT(). + If AllocationSize is greater than (MAX_ADDRESS - Buffer + 1), then ASSERT(). + + @param AllocationSize The number of bytes to allocate and zero. + @param Buffer The buffer to copy to the allocated buffer. + + @return A pointer to the allocated buffer or NULL if allocation fails. + +**/ +VOID * +EFIAPI +AllocateReservedCopyPool ( + IN UINTN AllocationSize, + IN CONST VOID *Buffer + ) +{ + ASSERT(FALSE); + return NULL; +} + +/** + Reallocates a buffer of a specified memory type. + + Allocates and zeros the number bytes specified by NewSize from memory of the type + specified by PoolType. If OldBuffer is not NULL, then the smaller of OldSize and + NewSize bytes are copied from OldBuffer to the newly allocated buffer, and + OldBuffer is freed. A pointer to the newly allocated buffer is returned. + If NewSize is 0, then a valid buffer of 0 size is returned. If there is not + enough memory remaining to satisfy the request, then NULL is returned. + + If the allocation of the new buffer is successful and the smaller of NewSize and OldSize + is greater than (MAX_ADDRESS - OldBuffer + 1), then ASSERT(). + + @param PoolType The type of pool to allocate. + @param OldSize The size, in bytes, of OldBuffer. + @param NewSize The size, in bytes, of the buffer to reallocate. + @param OldBuffer The buffer to copy to the allocated buffer. This is an + optional parameter that may be NULL. + + @return A pointer to the allocated buffer or NULL if allocation fails. + +**/ +VOID * +InternalReallocatePool ( + IN EFI_MEMORY_TYPE PoolType, + IN UINTN OldSize, + IN UINTN NewSize, + IN VOID *OldBuffer OPTIONAL + ) +{ + ASSERT(FALSE); + return NULL; +} + +/** + Reallocates a buffer of type EfiBootServicesData. + + Allocates and zeros the number bytes specified by NewSize from memory of type + EfiBootServicesData. If OldBuffer is not NULL, then the smaller of OldSize and + NewSize bytes are copied from OldBuffer to the newly allocated buffer, and + OldBuffer is freed. A pointer to the newly allocated buffer is returned. + If NewSize is 0, then a valid buffer of 0 size is returned. If there is not + enough memory remaining to satisfy the request, then NULL is returned. + + If the allocation of the new buffer is successful and the smaller of NewSize and OldSize + is greater than (MAX_ADDRESS - OldBuffer + 1), then ASSERT(). + + @param OldSize The size, in bytes, of OldBuffer. + @param NewSize The size, in bytes, of the buffer to reallocate. + @param OldBuffer The buffer to copy to the allocated buffer. This is an optional + parameter that may be NULL. + + @return A pointer to the allocated buffer or NULL if allocation fails. + +**/ +VOID * +EFIAPI +ReallocatePool ( + IN UINTN OldSize, + IN UINTN NewSize, + IN VOID *OldBuffer OPTIONAL + ) +{ + ASSERT(FALSE); + return NULL; +} + +/** + Reallocates a buffer of type EfiRuntimeServicesData. + + Allocates and zeros the number bytes specified by NewSize from memory of type + EfiRuntimeServicesData. If OldBuffer is not NULL, then the smaller of OldSize and + NewSize bytes are copied from OldBuffer to the newly allocated buffer, and + OldBuffer is freed. A pointer to the newly allocated buffer is returned. + If NewSize is 0, then a valid buffer of 0 size is returned. If there is not + enough memory remaining to satisfy the request, then NULL is returned. + + If the allocation of the new buffer is successful and the smaller of NewSize and OldSize + is greater than (MAX_ADDRESS - OldBuffer + 1), then ASSERT(). + + @param OldSize The size, in bytes, of OldBuffer. + @param NewSize The size, in bytes, of the buffer to reallocate. + @param OldBuffer The buffer to copy to the allocated buffer. This is an optional + parameter that may be NULL. + + @return A pointer to the allocated buffer or NULL if allocation fails. + +**/ +VOID * +EFIAPI +ReallocateRuntimePool ( + IN UINTN OldSize, + IN UINTN NewSize, + IN VOID *OldBuffer OPTIONAL + ) +{ + ASSERT(FALSE); + return NULL; +} + +/** + Reallocates a buffer of type EfiReservedMemoryType. + + Allocates and zeros the number bytes specified by NewSize from memory of type + EfiReservedMemoryType. If OldBuffer is not NULL, then the smaller of OldSize and + NewSize bytes are copied from OldBuffer to the newly allocated buffer, and + OldBuffer is freed. A pointer to the newly allocated buffer is returned. + If NewSize is 0, then a valid buffer of 0 size is returned. If there is not + enough memory remaining to satisfy the request, then NULL is returned. + + If the allocation of the new buffer is successful and the smaller of NewSize and OldSize + is greater than (MAX_ADDRESS - OldBuffer + 1), then ASSERT(). + + @param OldSize The size, in bytes, of OldBuffer. + @param NewSize The size, in bytes, of the buffer to reallocate. + @param OldBuffer The buffer to copy to the allocated buffer. This is an + optional parameter that may be NULL. + + @return A pointer to the allocated buffer or NULL if allocation fails. + +**/ +VOID * +EFIAPI +ReallocateReservedPool ( + IN UINTN OldSize, + IN UINTN NewSize, + IN VOID *OldBuffer OPTIONAL + ) +{ + ASSERT(FALSE); + return NULL; +} + +/** + Frees a buffer that was previously allocated with one of the pool allocation functions in the + Memory Allocation Library. + + Frees the buffer specified by Buffer. Buffer must have been allocated on a previous call to the + pool allocation services of the Memory Allocation Library. If it is not possible to free pool + resources, then this function will perform no actions. + + If Buffer was not allocated with a pool allocation function in the Memory Allocation Library, + then ASSERT(). + + @param Buffer The pointer to the buffer to free. + +**/ +VOID +EFIAPI +FreePool ( + IN VOID *Buffer + ) +{ + // + // PEI phase does not support to free pool, so leave it as NOP. + // +} + +/** + Allocates one or more 4KB pages of given type MemoryType. + + Allocates the number of 4KB pages of MemoryType and returns a pointer to the + allocated buffer. The buffer returned is aligned on a 4KB boundary. If Pages is 0, then NULL + is returned. If there is not enough memory remaining to satisfy the request, then NULL is + returned. + + @param Pages The number of 4 KB pages to allocate. + @param MemoryType Type of memory to use for this allocation. + + @return A pointer to the allocated buffer or NULL if allocation fails. + +**/ +VOID * +EFIAPI +AllocatePagesWithMemoryType ( + IN UINTN MemoryType, + IN UINTN Pages + ) +{ + EFI_PEI_HOB_POINTERS Hob; + EFI_PHYSICAL_ADDRESS Offset; + + Hob.Raw = GetHobList (); + + // Check to see if on 4k boundary + Offset = Hob.HandoffInformationTable->EfiFreeMemoryTop & 0xFFF; + if (Offset != 0) { + // If not aligned, make the allocation aligned. + Hob.HandoffInformationTable->EfiFreeMemoryTop -= Offset; + } + + // + // Verify that there is sufficient memory to satisfy the allocation + // + if (Hob.HandoffInformationTable->EfiFreeMemoryTop - ((Pages * EFI_PAGE_SIZE) + sizeof (EFI_HOB_MEMORY_ALLOCATION)) < Hob.HandoffInformationTable->EfiFreeMemoryBottom) { + return 0; + } else { + // + // Update the PHIT to reflect the memory usage + // + Hob.HandoffInformationTable->EfiFreeMemoryTop -= Pages * EFI_PAGE_SIZE; + + // This routine used to create a memory allocation HOB a la PEI, but that's not + // necessary for us. + + // + // Create a memory allocation HOB. + // + BuildMemoryAllocationHob ( + Hob.HandoffInformationTable->EfiFreeMemoryTop, + Pages * EFI_PAGE_SIZE, + (EFI_MEMORY_TYPE)MemoryType + ); + return (VOID *)(UINTN)Hob.HandoffInformationTable->EfiFreeMemoryTop; + } +} + + diff --git a/MdePkg/Library/SecMemoryAllocationLib/SecMemoryAllocationLib.inf b/MdePkg/Library/SecMemoryAllocationLib/SecMemoryAllocationLib.inf new file mode 100644 index 000000000000..243ef1de5303 --- /dev/null +++ b/MdePkg/Library/SecMemoryAllocationLib/SecMemoryAllocationLib.inf @@ -0,0 +1,40 @@ +## @file +# Instance of Memory Allocation Library using PEI Services. +# +# Memory Allocation Library that uses PEI Services to allocate memory. +# Free operations are ignored. +# +# Copyright (c) 2007 - 2018, Intel Corporation. All rights reserved.
+# +# SPDX-License-Identifier: BSD-2-Clause-Patent +# +# +## + +[Defines] + INF_VERSION = 0x00010005 + BASE_NAME = SecMemoryAllocationLib + MODULE_UNI_FILE = SecMemoryAllocationLib.uni + FILE_GUID = 9435C171-D3F0-47AB-B45C-E51D5411EDE2 + MODULE_TYPE = BASE + VERSION_STRING = 1.0 + LIBRARY_CLASS = MemoryAllocationLib|SEC + + +# +# VALID_ARCHITECTURES = IA32 X64 EBC (EBC is for build only) +# + +[Sources] + MemoryAllocationLib.c + +[Packages] + MdePkg/MdePkg.dec + #OvmfPkg/OvmfPkg.dec + + +[LibraryClasses] + DebugLib + BaseMemoryLib + #PeiServicesLib + HobLib \ No newline at end of file diff --git a/MdePkg/Library/SecMemoryAllocationLib/SecMemoryAllocationLib.uni b/MdePkg/Library/SecMemoryAllocationLib/SecMemoryAllocationLib.uni new file mode 100644 index 000000000000..f492b6240a88 --- /dev/null +++ b/MdePkg/Library/SecMemoryAllocationLib/SecMemoryAllocationLib.uni @@ -0,0 +1,17 @@ +// /** @file +// Instance of Memory Allocation Library using PEI Services. +// +// Memory Allocation Library that uses PEI Services to allocate memory. +// Free operations are ignored. +// +// Copyright (c) 2007 - 2014, Intel Corporation. All rights reserved.
+// +// SPDX-License-Identifier: BSD-2-Clause-Patent +// +// **/ + + +#string STR_MODULE_ABSTRACT #language en-US "Instance of Memory Allocation Library using PEI Services" + +#string STR_MODULE_DESCRIPTION #language en-US "Memory Allocation Library that uses PEI Services to allocate memory. Free operations are ignored." + diff --git a/MdePkg/Library/TdxLib/AcceptPages.c b/MdePkg/Library/TdxLib/AcceptPages.c new file mode 100644 index 000000000000..358a39b572f3 --- /dev/null +++ b/MdePkg/Library/TdxLib/AcceptPages.c @@ -0,0 +1,74 @@ +/** @file + + There are 4 defined types in TD memory. + Unaccepted memory is a special type of private memory. The guest + firmware must invoke TDCALL [TDG.MEM.PAGE.ACCEPT] the unaccepted + memory before use it. + + Copyright (c) 2020 - 2021, Intel Corporation. All rights reserved.
+ SPDX-License-Identifier: BSD-2-Clause-Patent + +**/ + +#include +#include +#include +#include +#include + +UINT64 mNumberOfDuplicatedAcceptedPages; + +/** + This function accept a pending private page, and initialize the page to + all-0 using the TD ephemeral private key. + + @param[in] StartAddress Guest physical address of the private + page to accept. + @param[in] NumberOfPages Number of the pages to be accepted. + @param[in] PageSize GPA page size. Only accept 1G/2M/4K size. + + @return EFI_SUCCESS +**/ +EFI_STATUS +EFIAPI +TdAcceptPages ( + IN UINT64 StartAddress, + IN UINT64 NumberOfPages, + IN UINT64 PageSize + ) +{ + UINT64 Address; + UINT64 Status; + UINT64 Index; + UINT64 GpaPageSize; + + Address = StartAddress; + + if (PageSize == SIZE_4KB) { + GpaPageSize = TDCALL_ACCEPT_PAGE_SIZE_4K; + } else if (PageSize == SIZE_2MB) { + GpaPageSize = TDCALL_ACCEPT_PAGE_SIZE_2M; + } else if (PageSize == SIZE_1GB) { + GpaPageSize = TDCALL_ACCEPT_PAGE_SIZE_1G; + } else { + DEBUG ((DEBUG_ERROR, "Accept page size must be 4K/2M/1G. Invalid page size - 0x%llx\n", PageSize)); + return EFI_INVALID_PARAMETER; + } + + for (Index = 0; Index < NumberOfPages; Index++) { + Status = TdCall (TDCALL_TDACCEPTPAGE,Address, GpaPageSize, 0, 0); + if (Status != TDX_EXIT_REASON_SUCCESS) { + if ((Status & ~0xFFULL) == TDX_EXIT_REASON_PAGE_ALREADY_ACCEPTED) { + mNumberOfDuplicatedAcceptedPages++; + DEBUG ((DEBUG_VERBOSE, "Address %llx already accepted. Total number of already accepted pages %ld\n", + Address, mNumberOfDuplicatedAcceptedPages)); + } else { + DEBUG ((DEBUG_ERROR, "Address %llx failed to be accepted. Error = %ld\n", + Address, Status)); + ASSERT (Status == TDX_EXIT_REASON_SUCCESS); + } + } + Address += PageSize; + } + return EFI_SUCCESS; +} diff --git a/MdePkg/Library/TdxLib/Rtmr.c b/MdePkg/Library/TdxLib/Rtmr.c new file mode 100644 index 000000000000..9c60d3c65e0e --- /dev/null +++ b/MdePkg/Library/TdxLib/Rtmr.c @@ -0,0 +1,118 @@ +/** @file + + Extends one of the RTMR measurement registers in TDCS with the provided + extension data in memory. + + Copyright (c) 2020 - 2021, Intel Corporation. All rights reserved.
+ SPDX-License-Identifier: BSD-2-Clause-Patent + +**/ + +#include +#include +#include +#include +#include +#include + +#define RTMR_COUNT 4 +#define TD_EXTEND_BUFFER_LEN (64 + 64) +#define EXTEND_BUFFER_ADDRESS_MASK 0x3f + + +#pragma pack(16) +typedef struct { + UINT8 Buffer[TD_EXTEND_BUFFER_LEN]; +} TDX_EXTEND_BUFFER; +#pragma pack() + +UINT8 *mExtendBufferAddress = NULL; +TDX_EXTEND_BUFFER mExtendBuffer; + +/** + TD.RTMR.EXTEND requires 64B-aligned guest physical address of + 48B-extension data. In runtime we walk thru the Buffer to find + out a 64B-aligned start address. + + @return Start address of the extend buffer + +**/ +UINT8 * +EFIAPI +GetExtendBuffer ( + VOID + ) +{ + UINT8 *ExtendBufferAddress; + UINT64 Gap; + + if (mExtendBufferAddress != NULL) { + return mExtendBufferAddress; + } + + ExtendBufferAddress = mExtendBuffer.Buffer; + + Gap = 0x40 - ((UINT64)(UINTN)ExtendBufferAddress & EXTEND_BUFFER_ADDRESS_MASK); + mExtendBufferAddress = (UINT8*)((UINT64)(UINTN)ExtendBufferAddress + Gap); + + DEBUG ((DEBUG_VERBOSE, "ExtendBufferAddress: 0x%p, Gap: 0x%x\n", ExtendBufferAddress, Gap)); + DEBUG ((DEBUG_VERBOSE, "mExtendBufferAddress: 0x%p\n", mExtendBufferAddress)); + + ASSERT (mExtendBufferAddress + 64 <= ExtendBufferAddress + TD_EXTEND_BUFFER_LEN); + + return mExtendBufferAddress; +} + + +/** + This function extends one of the RTMR measurement register + in TDCS with the provided extension data in memory. + RTMR extending supports SHA384 which length is 48 bytes. + + @param[in] Data Point to the data to be extended + @param[in] DataLen Length of the data. Must be 48 + @param[in] Index RTMR index + + @return EFI_SUCCESS + @return EFI_INVALID_PARAMETER + @return EFI_DEVICE_ERROR + +**/ +EFI_STATUS +EFIAPI +TdExtendRtmr ( + IN UINT32 *Data, + IN UINT32 DataLen, + IN UINT8 Index + ) +{ + EFI_STATUS Status; + UINT64 TdCallStatus; + UINT8 *ExtendBuffer; + + Status = EFI_SUCCESS; + + ASSERT (Index >= 0 && Index < RTMR_COUNT); + ASSERT (DataLen == SHA384_DIGEST_SIZE); + + ExtendBuffer = GetExtendBuffer(); + ASSERT (ExtendBuffer != NULL); + ZeroMem (ExtendBuffer, SHA384_DIGEST_SIZE); + CopyMem (ExtendBuffer, Data, SHA384_DIGEST_SIZE); + + TdCallStatus = TdCall (TDCALL_TDEXTENDRTMR, (UINT64)(UINTN)ExtendBuffer, Index, 0, 0); + + if (TdCallStatus == TDX_EXIT_REASON_SUCCESS) { + Status = EFI_SUCCESS; + } else if (TdCallStatus == TDX_EXIT_REASON_OPERAND_INVALID) { + Status = EFI_INVALID_PARAMETER; + } else { + Status = EFI_DEVICE_ERROR; + } + + if (Status != EFI_SUCCESS) { + DEBUG ((DEBUG_ERROR, "Error returned from TdExtendRtmr call - 0x%lx\n", TdCallStatus)); + } + + return Status; +} diff --git a/MdePkg/Library/TdxLib/TdSharedPageMask.c b/MdePkg/Library/TdxLib/TdSharedPageMask.c new file mode 100644 index 000000000000..e6cbad322145 --- /dev/null +++ b/MdePkg/Library/TdxLib/TdSharedPageMask.c @@ -0,0 +1,46 @@ +/** @file + + Copyright (c) 2021, Intel Corporation. All rights reserved.
+ SPDX-License-Identifier: BSD-2-Clause-Patent + +**/ + +#include +#include +#include +#include +#include + +UINT64 mTdSharedPageMask = 0; + +/** + This function ges the Td guest shared page mask. + + The guest indicates if a page is shared using the Guest Physical Address + (GPA) Shared (S) bit. If the GPA Width(GPAW) is 48, the S-bit is bit-47. + If the GPAW is 52, the S-bit is bit-51. + + @return Shared page bit mask +**/ +UINT64 +EFIAPI +TdSharedPageMask ( + VOID + ) +{ + UINT64 Status; + UINT8 Gpaw; + TD_RETURN_DATA TdReturnData; + + if (mTdSharedPageMask != 0) { + return mTdSharedPageMask; + } + + Status = TdCall (TDCALL_TDINFO, 0,0,0, &TdReturnData); + ASSERT (Status == TDX_EXIT_REASON_SUCCESS); + + Gpaw = (UINT8)(TdReturnData.TdInfo.Gpaw & 0x3f); + mTdSharedPageMask = 1ULL << (Gpaw - 1); + ASSERT(Gpaw == 48 || Gpaw == 52); + return mTdSharedPageMask; +} diff --git a/MdePkg/Library/TdxLib/TdxLib.inf b/MdePkg/Library/TdxLib/TdxLib.inf new file mode 100644 index 000000000000..08951fc95b70 --- /dev/null +++ b/MdePkg/Library/TdxLib/TdxLib.inf @@ -0,0 +1,40 @@ +## @file +# Tdx library +# +# Copyright (c) 2020 - 2021, Intel Corporation. All rights reserved.
+# SPDX-License-Identifier: BSD-2-Clause-Patent +# +## + +[Defines] + INF_VERSION = 0x00010005 + BASE_NAME = TdxLib + FILE_GUID = 032A8E0D-0C27-40C0-9CAA-23B731C1B223 + MODULE_TYPE = BASE + VERSION_STRING = 1.0 + LIBRARY_CLASS = TdxLib + +# +# The following information is for reference only and not required by the build tools. +# +# VALID_ARCHITECTURES = X64 +# + +[Sources] + AcceptPages.c + Rtmr.c + TdSharedPageMask.c + X64/Tdcall.nasm + X64/Tdvmcall.nasm + +[Packages] + MdePkg/MdePkg.dec + OvmfPkg/OvmfPkg.dec + +[LibraryClasses] + BaseLib + BaseMemoryLib + DebugLib + +[Pcd] + gUefiOvmfPkgTokenSpaceGuid.PcdUseTdxEmulation diff --git a/MdePkg/Library/TdxLib/X64/Tdcall.nasm b/MdePkg/Library/TdxLib/X64/Tdcall.nasm new file mode 100644 index 000000000000..e5bbdca3df08 --- /dev/null +++ b/MdePkg/Library/TdxLib/X64/Tdcall.nasm @@ -0,0 +1,124 @@ +;------------------------------------------------------------------------------ +;* +;* Copyright (c) 2020 - 2021, Intel Corporation. All rights reserved.
+;* SPDX-License-Identifier: BSD-2-Clause-Patent +;* +;* +;------------------------------------------------------------------------------ + +DEFAULT REL +SECTION .text + +%macro tdcall 0 +%if (FixedPcdGet32 (PcdUseTdxEmulation) != 0) + vmcall +%else + db 0x66,0x0f,0x01,0xcc +%endif +%endmacro + +%macro tdcall_push_regs 0 + push rbp + mov rbp, rsp + push r15 + push r14 + push r13 + push r12 + push rbx + push rsi + push rdi +%endmacro + +%macro tdcall_pop_regs 0 + pop rdi + pop rsi + pop rbx + pop r12 + pop r13 + pop r14 + pop r15 + pop rbp +%endmacro + +%define number_of_regs_pushed 8 +%define number_of_parameters 4 + +; +; Keep these in sync for push_regs/pop_regs, code below +; uses them to find 5th or greater parameters +; +%define first_variable_on_stack_offset \ + ((number_of_regs_pushed * 8) + (number_of_parameters * 8) + 8) +%define second_variable_on_stack_offset \ + ((first_variable_on_stack_offset) + 8) + +%macro tdcall_regs_preamble 2 + mov rax, %1 + + mov ecx, %2 + + ; R10 = 0 (standard TDVMCALL) + + xor r10d, r10d + + ; Zero out unused (for standard TDVMCALL) registers to avoid leaking + ; secrets to the VMM. + + xor ebx, ebx + xor esi, esi + xor edi, edi + + xor edx, edx + xor ebp, ebp + xor r8d, r8d + xor r9d, r9d +%endmacro + +%macro tdcall_regs_postamble 0 + xor ebx, ebx + xor esi, esi + xor edi, edi + + xor ecx, ecx + xor edx, edx + xor r8d, r8d + xor r9d, r9d + xor r10d, r10d + xor r11d, r11d +%endmacro + +; TdCall ( +; UINT64 Leaf, // Rcx +; UINT64 P1, // Rdx +; UINT64 P2, // R8 +; UINT64 P3, // R9 +; UINT64 Results, // rsp + 0x28 +; ) +global ASM_PFX(TdCall) +ASM_PFX(TdCall): + tdcall_push_regs + + mov rax, rcx + mov rcx, rdx + mov rdx, r8 + mov r8, r9 + + tdcall + + ; exit if tdcall reports failure. + test rax, rax + jnz .exit + + ; test if caller wanted results + mov r12, [rsp + first_variable_on_stack_offset ] + test r12, r12 + jz .exit + mov [r12 + 0 ], rcx + mov [r12 + 8 ], rdx + mov [r12 + 16], r8 + mov [r12 + 24], r9 + mov [r12 + 32], r10 + mov [r12 + 40], r11 +.exit: + tdcall_pop_regs + ret diff --git a/MdePkg/Library/TdxLib/X64/Tdvmcall.nasm b/MdePkg/Library/TdxLib/X64/Tdvmcall.nasm new file mode 100644 index 000000000000..12de700ff6ff --- /dev/null +++ b/MdePkg/Library/TdxLib/X64/Tdvmcall.nasm @@ -0,0 +1,210 @@ +;------------------------------------------------------------------------------ +;* +;* Copyright (c) 2020 - 2021, Intel Corporation. All rights reserved.
+;* SPDX-License-Identifier: BSD-2-Clause-Patent +;* +;* +;------------------------------------------------------------------------------ + +DEFAULT REL +SECTION .text + +%define TDVMCALL_EXPOSE_REGS_MASK 0xffec +%define TDVMCALL 0x0 +%define EXIT_REASON_CPUID 0xa + +%macro tdcall 0 +%if (FixedPcdGet32 (PcdUseTdxEmulation) != 0) + vmcall +%else + db 0x66,0x0f,0x01,0xcc +%endif +%endmacro + +%macro tdcall_push_regs 0 + push rbp + mov rbp, rsp + push r15 + push r14 + push r13 + push r12 + push rbx + push rsi + push rdi +%endmacro + +%macro tdcall_pop_regs 0 + pop rdi + pop rsi + pop rbx + pop r12 + pop r13 + pop r14 + pop r15 + pop rbp +%endmacro + +%define number_of_regs_pushed 8 +%define number_of_parameters 4 + +; +; Keep these in sync for push_regs/pop_regs, code below +; uses them to find 5th or greater parameters +; +%define first_variable_on_stack_offset \ + ((number_of_regs_pushed * 8) + (number_of_parameters * 8) + 8) +%define second_variable_on_stack_offset \ + ((first_variable_on_stack_offset) + 8) + +%macro tdcall_regs_preamble 2 + mov rax, %1 + + mov ecx, %2 + + ; R10 = 0 (standard TDVMCALL) + + xor r10d, r10d + + ; Zero out unused (for standard TDVMCALL) registers to avoid leaking + ; secrets to the VMM. + + xor ebx, ebx + xor esi, esi + xor edi, edi + + xor edx, edx + xor ebp, ebp + xor r8d, r8d + xor r9d, r9d +%endmacro + +%macro tdcall_regs_postamble 0 + xor ebx, ebx + xor esi, esi + xor edi, edi + + xor ecx, ecx + xor edx, edx + xor r8d, r8d + xor r9d, r9d + xor r10d, r10d + xor r11d, r11d +%endmacro + +;------------------------------------------------------------------------------ +; 0 => RAX = TDCALL leaf +; M => RCX = TDVMCALL register behavior +; 1 => R10 = standard vs. vendor +; RDI => R11 = TDVMCALL function / nr +; RSI = R12 = p1 +; RDX => R13 = p2 +; RCX => R14 = p3 +; R8 => R15 = p4 + +; UINT64 +; EFIAPI +; TdVmCall ( +; UINT64 Leaf, // Rcx +; UINT64 P1, // Rdx +; UINT64 P2, // R8 +; UINT64 P3, // R9 +; UINT64 P4, // rsp + 0x28 +; UINT64 *Val // rsp + 0x30 +; ) +global ASM_PFX(TdVmCall) +ASM_PFX(TdVmCall): + tdcall_push_regs + + mov r11, rcx + mov r12, rdx + mov r13, r8 + mov r14, r9 + mov r15, [rsp + first_variable_on_stack_offset ] + + tdcall_regs_preamble TDVMCALL, TDVMCALL_EXPOSE_REGS_MASK + + tdcall + + ; ignore return dataif TDCALL reports failure. + test rax, rax + jnz .no_return_data + + ; Propagate TDVMCALL success/failure to return value. + mov rax, r10 + + ; Retrieve the Val pointer. + mov r9, [rsp + second_variable_on_stack_offset ] + test r9, r9 + jz .no_return_data + + ; On success, propagate TDVMCALL output value to output param + test rax, rax + jnz .no_return_data + mov [r9], r11 +.no_return_data: + tdcall_regs_postamble + + tdcall_pop_regs + + ret + +;------------------------------------------------------------------------------ +; 0 => RAX = TDCALL leaf +; M => RCX = TDVMCALL register behavior +; 1 => R10 = standard vs. vendor +; RDI => R11 = TDVMCALL function / nr +; RSI = R12 = p1 +; RDX => R13 = p2 +; RCX => R14 = p3 +; R8 => R15 = p4 + +; UINT64 +; EFIAPI +; TdVmCallCpuid ( +; UINT64 EaxIn, // Rcx +; UINT64 EcxIn, // Rdx +; UINT64 *Results // R8 +; ) +global ASM_PFX(TdVmCallCpuid) +ASM_PFX(TdVmCallCpuid): + tdcall_push_regs + + mov r11, EXIT_REASON_CPUID + mov r12, rcx + mov r13, rdx + + tdcall_regs_preamble TDVMCALL, TDVMCALL_EXPOSE_REGS_MASK + + ; Save *results pointers + push r8 + + tdcall + + ; Panic if TDCALL reports failure. + test rax, rax + jnz .no_return_data + + ; Propagate TDVMCALL success/failure to return value. + mov rax, r10 + test rax, rax + jnz .no_return_data + + ; Retrieve *Results + pop r8 + test r8, r8 + jnz .no_return_data + ; Caller pass in buffer so store results r12-r15 contains eax-edx + mov [r8 + 0], r12 + mov [r8 + 8], r13 + mov [r8 + 16], r14 + mov [r8 + 24], r15 + +.no_return_data: + tdcall_regs_postamble + + tdcall_pop_regs + + ret + +.panic: + ud2 diff --git a/MdePkg/Library/TdxProbeLib/TdxProbeLibNull.c b/MdePkg/Library/TdxProbeLib/TdxProbeLibNull.c new file mode 100644 index 000000000000..631f51ebf79b --- /dev/null +++ b/MdePkg/Library/TdxProbeLib/TdxProbeLibNull.c @@ -0,0 +1,25 @@ +/** @file + Null instance of TdxProbeLib. + + Copyright (c) 2021, Intel Corporation. All rights reserved.
+ SPDX-License-Identifier: BSD-2-Clause-Patent + +**/ + + +#include +#include + +/** + Probe whether it is TD guest or Non-TD guest. + + @return TRUE TD guest + @return FALSE Non-TD guest +**/ +BOOLEAN +EFIAPI +ProbeTdGuest ( + VOID ) +{ + return FALSE; +} diff --git a/MdePkg/Library/TdxProbeLib/TdxProbeLibNull.inf b/MdePkg/Library/TdxProbeLib/TdxProbeLibNull.inf new file mode 100644 index 000000000000..f3d5eb69ae15 --- /dev/null +++ b/MdePkg/Library/TdxProbeLib/TdxProbeLibNull.inf @@ -0,0 +1,27 @@ +## @file +# Null Tdx Probe library instance +# +# Copyright (c) 2020 - 2021, Intel Corporation. All rights reserved.
+# SPDX-License-Identifier: BSD-2-Clause-Patent +# +## + +[Defines] + INF_VERSION = 0x00010005 + BASE_NAME = TdxProbeLibNull + FILE_GUID = D0DCCD86-FC42-4EE3-9132-53827C77661C + MODULE_TYPE = BASE + VERSION_STRING = 1.0 + LIBRARY_CLASS = TdxProbeLib + +# +# The following information is for reference only and not required by the build tools. +# +# VALID_ARCHITECTURES = X64 IA32 +# + +[Sources] + TdxProbeLibNull.c + +[Packages] + MdePkg/MdePkg.dec diff --git a/MdePkg/MdePkg.dec b/MdePkg/MdePkg.dec index 3928db65d188..888d92086a49 100644 --- a/MdePkg/MdePkg.dec +++ b/MdePkg/MdePkg.dec @@ -983,6 +983,9 @@ gEfiPeiDelayedDispatchPpiGuid = { 0x869c711d, 0x649c, 0x44fe, { 0x8b, 0x9e, 0x2c, 0xbb, 0x29, 0x11, 0xc3, 0xe6 }} [Protocols] + ## Include/Protocol/Tdx.h + gTdTcg2ProtocolGuid = {0x96751a3d, 0x72f4, 0x41a6, {0xa7, 0x94, 0xed, 0x5d, 0x0e, 0x67, 0xae, 0x6b}} + ## Include/Protocol/Pcd.h gPcdProtocolGuid = { 0x11B34006, 0xD85B, 0x4D0A, { 0xA2, 0x90, 0xD5, 0xA5, 0x71, 0x31, 0x0E, 0xF7 }} diff --git a/OvmfPkg/8254TimerDxe/8254Timer.inf b/OvmfPkg/8254TimerDxe/8254Timer.inf index 8a07c8247ebe..007e3f405c0a 100644 --- a/OvmfPkg/8254TimerDxe/8254Timer.inf +++ b/OvmfPkg/8254TimerDxe/8254Timer.inf @@ -26,6 +26,7 @@ DebugLib UefiDriverEntryPoint IoLib + TdxProbeLib [Sources] Timer.h diff --git a/OvmfPkg/8254TimerDxe/Timer.c b/OvmfPkg/8254TimerDxe/Timer.c index fd1691beb3c7..701cc65a303e 100644 --- a/OvmfPkg/8254TimerDxe/Timer.c +++ b/OvmfPkg/8254TimerDxe/Timer.c @@ -7,6 +7,7 @@ SPDX-License-Identifier: BSD-2-Clause-Patent **/ #include "Timer.h" +#include // // The handle onto which the Timer Architectural Protocol will be installed @@ -340,6 +341,10 @@ TimerDriverInitialize ( EFI_STATUS Status; UINT32 TimerVector; + if(ProbeTdGuest()) { + return EFI_UNSUPPORTED; + } + // // Initialize the pointer to our notify function. // diff --git a/OvmfPkg/8259InterruptControllerDxe/8259.c b/OvmfPkg/8259InterruptControllerDxe/8259.c index 1c2ac1039d40..94b7cb83c90e 100644 --- a/OvmfPkg/8259InterruptControllerDxe/8259.c +++ b/OvmfPkg/8259InterruptControllerDxe/8259.c @@ -8,6 +8,7 @@ SPDX-License-Identifier: BSD-2-Clause-Patent #include "8259.h" +#include // // Global for the Legacy 8259 Protocol that is produced by this driver // @@ -586,6 +587,9 @@ Install8259 ( EFI_STATUS Status; EFI_8259_IRQ Irq; + if(ProbeTdGuest()) { + return EFI_UNSUPPORTED; + } // // Initialze mask values from PCDs // diff --git a/OvmfPkg/8259InterruptControllerDxe/8259.inf b/OvmfPkg/8259InterruptControllerDxe/8259.inf index 7320ff2490a7..09a4b41996af 100644 --- a/OvmfPkg/8259InterruptControllerDxe/8259.inf +++ b/OvmfPkg/8259InterruptControllerDxe/8259.inf @@ -29,6 +29,7 @@ UefiDriverEntryPoint IoLib PcdLib + TdxProbeLib [Protocols] gEfiLegacy8259ProtocolGuid ## PRODUCES diff --git a/OvmfPkg/AcpiPlatformDxe/AcpiPlatformDxe.inf b/OvmfPkg/AcpiPlatformDxe/AcpiPlatformDxe.inf index e486b8afa56d..91918c8f5533 100644 --- a/OvmfPkg/AcpiPlatformDxe/AcpiPlatformDxe.inf +++ b/OvmfPkg/AcpiPlatformDxe/AcpiPlatformDxe.inf @@ -51,14 +51,17 @@ DxeServicesTableLib OrderedCollectionLib XenPlatformLib + TdxProbeLib [Protocols] gEfiAcpiTableProtocolGuid # PROTOCOL ALWAYS_CONSUMED gEfiFirmwareVolume2ProtocolGuid # PROTOCOL SOMETIMES_CONSUMED gEfiPciIoProtocolGuid # PROTOCOL SOMETIMES_CONSUMED + gTdTcg2ProtocolGuid # PROTOCOL SOMETIMES_CONSUMES [Guids] gRootBridgesConnectedEventGroupGuid + gUefiOvmfPkgTdxPlatformGuid [Pcd] gEfiMdeModulePkgTokenSpaceGuid.PcdAcpiTableStorageFile @@ -66,6 +69,7 @@ gUefiCpuPkgTokenSpaceGuid.PcdCpuLocalApicBaseAddress gUefiOvmfPkgTokenSpaceGuid.Pcd8259LegacyModeEdgeLevel gUefiOvmfPkgTokenSpaceGuid.PcdOvmfFdBaseAddress + gUefiOvmfPkgTokenSpaceGuid.PcdTdRelocatedMailboxBase [Depex] gEfiAcpiTableProtocolGuid diff --git a/OvmfPkg/AcpiPlatformDxe/Qemu.c b/OvmfPkg/AcpiPlatformDxe/Qemu.c index 7fb42270043f..3e1da8ef1037 100644 --- a/OvmfPkg/AcpiPlatformDxe/Qemu.c +++ b/OvmfPkg/AcpiPlatformDxe/Qemu.c @@ -17,6 +17,10 @@ #include #include #include +#include +#include + +STATIC EFI_HOB_PLATFORM_INFO *mPlatformInfoHob = NULL; BOOLEAN QemuDetected ( @@ -30,28 +34,6 @@ QemuDetected ( return TRUE; } - -STATIC -UINTN -CountBits16 ( - UINT16 Mask - ) -{ - // - // For all N >= 1, N bits are enough to represent the number of bits set - // among N bits. It's true for N == 1. When adding a new bit (N := N+1), - // the maximum number of possibly set bits increases by one, while the - // representable maximum doubles. - // - Mask = ((Mask & 0xAAAA) >> 1) + (Mask & 0x5555); - Mask = ((Mask & 0xCCCC) >> 2) + (Mask & 0x3333); - Mask = ((Mask & 0xF0F0) >> 4) + (Mask & 0x0F0F); - Mask = ((Mask & 0xFF00) >> 8) + (Mask & 0x00FF); - - return Mask; -} - - STATIC EFI_STATUS EFIAPI @@ -63,7 +45,6 @@ QemuInstallAcpiMadtTable ( ) { UINTN CpuCount; - UINTN PciLinkIsoCount; UINTN NewBufferSize; EFI_ACPI_1_0_MULTIPLE_APIC_DESCRIPTION_TABLE_HEADER *Madt; EFI_ACPI_1_0_PROCESSOR_LOCAL_APIC_STRUCTURE *LocalApic; @@ -73,6 +54,7 @@ QemuInstallAcpiMadtTable ( VOID *Ptr; UINTN Loop; EFI_STATUS Status; + ACPI_MADT_MPWK_STRUCT *MadtMpWk; ASSERT (AcpiTableBufferSize >= sizeof (EFI_ACPI_DESCRIPTION_HEADER)); @@ -80,19 +62,15 @@ QemuInstallAcpiMadtTable ( CpuCount = QemuFwCfgRead16 (); ASSERT (CpuCount >= 1); - // - // Set Level-tiggered, Active High for these identity mapped IRQs. The bitset - // corresponds to the union of all possible interrupt assignments for the LNKA, - // LNKB, LNKC, LNKD PCI interrupt lines. See the DSDT. - // - PciLinkIsoCount = CountBits16 (PcdGet16 (Pcd8259LegacyModeEdgeLevel)); - +#define NUM_8259_IRQS 16 NewBufferSize = 1 * sizeof (*Madt) + CpuCount * sizeof (*LocalApic) + 1 * sizeof (*IoApic) + - (1 + PciLinkIsoCount) * sizeof (*Iso) + + NUM_8259_IRQS * sizeof (*Iso) + 1 * sizeof (*LocalApicNmi); + NewBufferSize += sizeof(ACPI_MADT_MPWK_STRUCT); + Madt = AllocatePool (NewBufferSize); if (Madt == NULL) { return EFI_OUT_OF_RESOURCES; @@ -130,31 +108,21 @@ QemuInstallAcpiMadtTable ( Iso = Ptr; Iso->Type = EFI_ACPI_1_0_INTERRUPT_SOURCE_OVERRIDE; Iso->Length = sizeof (*Iso); - Iso->Bus = 0x00; // ISA - Iso->Source = 0x00; // IRQ0 + Iso->Bus = 0x00; // ISA + Iso->Source = 0x00; // IRQ0 Iso->GlobalSystemInterruptVector = 0x00000002; - Iso->Flags = 0x0000; // Conforms to specs of the bus + Iso->Flags = 0x0005; // Edge-triggered, Active High ++Iso; - // - // Set Level-triggered, Active High for all possible PCI link targets. - // - for (Loop = 0; Loop < 16; ++Loop) { - if ((PcdGet16 (Pcd8259LegacyModeEdgeLevel) & (1 << Loop)) == 0) { - continue; - } + for (Loop = 1; Loop < NUM_8259_IRQS; ++Loop) { Iso->Type = EFI_ACPI_1_0_INTERRUPT_SOURCE_OVERRIDE; Iso->Length = sizeof (*Iso); Iso->Bus = 0x00; // ISA Iso->Source = (UINT8) Loop; Iso->GlobalSystemInterruptVector = (UINT32) Loop; - Iso->Flags = 0x000D; // Level-triggered, Active High + Iso->Flags = 0x0005; // Edge-triggered, Active High ++Iso; } - ASSERT ( - (UINTN) (Iso - (EFI_ACPI_1_0_INTERRUPT_SOURCE_OVERRIDE_STRUCTURE *)Ptr) == - 1 + PciLinkIsoCount - ); Ptr = Iso; LocalApicNmi = Ptr; @@ -172,6 +140,17 @@ QemuInstallAcpiMadtTable ( LocalApicNmi->LocalApicInti = 0x01; Ptr = LocalApicNmi + 1; + MadtMpWk = Ptr; + MadtMpWk->Type = ACPI_MADT_MPWK_STRUCT_TYPE; + MadtMpWk->Length = sizeof(ACPI_MADT_MPWK_STRUCT); + MadtMpWk->MailBoxVersion = 1; + MadtMpWk->Reserved2 = 0; + MadtMpWk->MailBoxAddress = PcdGet64 (PcdTdRelocatedMailboxBase); + Ptr = MadtMpWk + 1; + + DEBUG ((DEBUG_INFO, "%a:%d:ACPI setting mailbox to 0x%x 0x%x\n", __func__, __LINE__, + MadtMpWk->MailBoxAddress, PcdGet64 (PcdTdRelocatedMailboxBase))); + ASSERT ((UINTN) ((UINT8 *)Ptr - (UINT8 *)Madt) == NewBufferSize); Status = InstallAcpiTable (AcpiProtocol, Madt, NewBufferSize, TableKey); @@ -355,13 +334,18 @@ GetSuspendStates ( // // check for overrides // - Status = QemuFwCfgFindFile ("etc/system-states", &FwCfgItem, &FwCfgSize); - if (Status != RETURN_SUCCESS || FwCfgSize != sizeof SystemStates) { - DEBUG ((DEBUG_INFO, "ACPI using S3/S4 defaults\n")); - return; + if (mPlatformInfoHob) { + CopyMem(SystemStates, mPlatformInfoHob->SystemStates, sizeof SystemStates); + DEBUG ((DEBUG_INFO, ">>>> GetSuspendStates mPlatformInfoHob\n")); + } else { + Status = QemuFwCfgFindFile ("etc/system-states", &FwCfgItem, &FwCfgSize); + if (Status != RETURN_SUCCESS || FwCfgSize != sizeof SystemStates) { + DEBUG ((DEBUG_INFO, "ACPI using S3/S4 defaults\n")); + return; + } + QemuFwCfgSelectItem (FwCfgItem); + QemuFwCfgReadBytes (sizeof SystemStates, SystemStates); } - QemuFwCfgSelectItem (FwCfgItem); - QemuFwCfgReadBytes (sizeof SystemStates, SystemStates); // // Each byte corresponds to a system state. In each byte, the MSB tells us @@ -489,6 +473,12 @@ QemuInstallAcpiTable ( { EFI_ACPI_DESCRIPTION_HEADER *Hdr; EFI_ACPI_TABLE_INSTALL_ACPI_TABLE TableInstallFunction; + EFI_HOB_GUID_TYPE *GuidHob; + + GuidHob = GetFirstGuidHob(&gUefiOvmfPkgTdxPlatformGuid); + if (GuidHob) { + mPlatformInfoHob = (EFI_HOB_PLATFORM_INFO *)GET_GUID_HOB_DATA (GuidHob); + } Hdr = (EFI_ACPI_DESCRIPTION_HEADER*) AcpiTableBuffer; switch (Hdr->Signature) { diff --git a/OvmfPkg/AcpiPlatformDxe/QemuFwCfgAcpi.c b/OvmfPkg/AcpiPlatformDxe/QemuFwCfgAcpi.c index b62027db6e66..b679e6b44ec6 100644 --- a/OvmfPkg/AcpiPlatformDxe/QemuFwCfgAcpi.c +++ b/OvmfPkg/AcpiPlatformDxe/QemuFwCfgAcpi.c @@ -18,7 +18,11 @@ #include #include #include +#include +#include +#include +EFI_TCG2_PROTOCOL *mTdTcg2Protocol = NULL; // // The user structure for the ordered collection that will track the fw_cfg @@ -35,6 +39,71 @@ typedef struct { // part of ACPI tables. } BLOB; +/** + Mesure firmware acpi configuration data from qemu. + @param[in] EventData Pointer to the event data. + @param[in] EventSize Size of event data. + @param[in] CfgDataBase Configuration data base address. + @param[in] EventSize Size of configuration data . + @retval EFI_NOT_FOUND Cannot locate protocol. + @retval EFI_OUT_OF_RESOURCES Allocate zero pool failure. + @return Status codes returned by + mTcg2Protocol->HashLogExtendEvent. +**/ +STATIC +EFI_STATUS +EFIAPI +MeasureQemuFwCfgAcpi( + IN CHAR8 *EventData, + IN UINT32 EventSize, + IN EFI_PHYSICAL_ADDRESS CfgDataBase, + IN UINTN CfgDataLength +) +{ + EFI_TCG2_EVENT *Tcg2Event; + EFI_STATUS Status; + + if (ProbeTdGuest () == FALSE) { + return EFI_SUCCESS; + } + + if (mTdTcg2Protocol == NULL) { + Status = gBS->LocateProtocol (&gTdTcg2ProtocolGuid, NULL, (VOID **) &mTdTcg2Protocol); + if (EFI_ERROR (Status)) { + // + // TdTcg2 protocol is not installed. + // + DEBUG ((EFI_D_ERROR, "MesureQemuFwCfgAcpi - TdTcg2 - %r\n", Status)); + return EFI_NOT_FOUND; + } + } + + Tcg2Event = AllocateZeroPool (EventSize + sizeof (EFI_TCG2_EVENT) - sizeof(Tcg2Event->Event)); + if (Tcg2Event == NULL) { + return EFI_OUT_OF_RESOURCES; + } + + Tcg2Event->Size = EventSize + sizeof (EFI_TCG2_EVENT) - sizeof(Tcg2Event->Event); + Tcg2Event->Header.EventType = EV_PLATFORM_CONFIG_FLAGS; + Tcg2Event->Header.PCRIndex = 1; + Tcg2Event->Header.HeaderSize = sizeof (EFI_TCG2_EVENT_HEADER); + Tcg2Event->Header.HeaderVersion = EFI_TCG2_EVENT_HEADER_VERSION; + CopyMem (&Tcg2Event->Event[0], EventData, EventSize); + + Status = mTdTcg2Protocol->HashLogExtendEvent (mTdTcg2Protocol, + 0, + CfgDataBase, + CfgDataLength, + Tcg2Event + ); + + FreePool (Tcg2Event); + + DEBUG ((DEBUG_INFO, "MeasureQemuFwCfg %s, %r\n", EventData, Status)); + + return Status; +} + /** Compare a standalone key against a user structure containing an embedded key. @@ -382,6 +451,16 @@ ProcessCmdAllocate ( QemuFwCfgSelectItem (FwCfgItem); QemuFwCfgReadBytes (FwCfgSize, Blob->Base); + + Status = MeasureQemuFwCfgAcpi ((CHAR8 *) Allocate->File, + sizeof(Allocate->File), + (EFI_PHYSICAL_ADDRESS) Blob->Base, + FwCfgSize + ); + if (EFI_ERROR (Status)) { + DEBUG ((DEBUG_ERROR, "Measure %s failure\n", Allocate->File)); + } + ZeroMem (Blob->Base + Blob->Size, EFI_PAGES_TO_SIZE (NumPages) - Blob->Size); DEBUG ((DEBUG_VERBOSE, "%a: File=\"%a\" Alignment=0x%x Zone=%d Size=0x%Lx " @@ -913,9 +992,16 @@ Process2ndPassCmdAddPointer ( goto RollbackSeenPointer; } - Status = AcpiProtocol->InstallAcpiTable (AcpiProtocol, + if(ProbeTdGuest()) { + Status = QemuInstallAcpiTable(AcpiProtocol, (VOID *)(UINTN)PointerValue, TableSize, &InstalledKey[*NumInstalled]); + } else { + Status = AcpiProtocol->InstallAcpiTable (AcpiProtocol, + (VOID *)(UINTN)PointerValue, TableSize, + &InstalledKey[*NumInstalled]); + } + if (EFI_ERROR (Status)) { DEBUG ((DEBUG_ERROR, "%a: InstallAcpiTable(): %r\n", __FUNCTION__, Status)); @@ -992,6 +1078,17 @@ InstallQemuFwCfgTables ( EnablePciDecoding (&OriginalPciAttributes, &OriginalPciAttributesCount); QemuFwCfgSelectItem (FwCfgItem); QemuFwCfgReadBytes (FwCfgSize, LoaderStart); + + Status = MeasureQemuFwCfgAcpi ( + "etc/table-loader", + sizeof ("etc/table-loader"), + (EFI_PHYSICAL_ADDRESS) LoaderStart, + FwCfgSize + ); + if (EFI_ERROR (Status)) { + DEBUG ((DEBUG_ERROR, "Measure etc/table-loader failure\n")); + } + RestorePciDecoding (OriginalPciAttributes, OriginalPciAttributesCount); LoaderEnd = LoaderStart + FwCfgSize / sizeof *LoaderEntry; diff --git a/OvmfPkg/EmuVariableFvbRuntimeDxe/Fvb.c b/OvmfPkg/EmuVariableFvbRuntimeDxe/Fvb.c index 766ad1e59f47..0f9b78e1fdd1 100644 --- a/OvmfPkg/EmuVariableFvbRuntimeDxe/Fvb.c +++ b/OvmfPkg/EmuVariableFvbRuntimeDxe/Fvb.c @@ -26,6 +26,7 @@ #include #include #include +#include #include "Fvb.h" #define EFI_AUTHENTICATED_VARIABLE_GUID \ @@ -596,6 +597,154 @@ ValidateFvHeader ( } +/** + Check padding data all bit should be 1. + + @param[in] Buffer - A pointer to buffer header + @param[in] BufferSize - Buffer size + + @retval TRUE - The padding data is valid. + @retval TRUE - The padding data is invalid. + +**/ +BOOLEAN +CheckPaddingData ( + IN UINT8 *Buffer, + IN UINT32 BufferSize + ) + { + UINT32 index; + + for (index = 0; index < BufferSize; index++) { + if (Buffer[index] != 0xFF) { + return FALSE; + } + } + + return TRUE; + } + + +/** + Check the integrity of CFV data. + + @param[in] TdxCfvBase - A pointer to CFV header + @param[in] TdxCfvSize - CFV data size + + @retval TRUE - The CFV data is valid. + @retval FALSE - The CFV data is invalid. + +**/ +BOOLEAN +ValidateTdxCfv ( + IN UINT8 *TdxCfvBase, + IN UINT32 TdxCfvSize + ) +{ + UINT16 Checksum; + UINTN VariableBase; + UINT32 VariableOffset; + UINT32 VariableOffsetBeforeAlign; + EFI_FIRMWARE_VOLUME_HEADER *CfvFvHeader; + VARIABLE_STORE_HEADER *CfvVariableStoreHeader; + AUTHENTICATED_VARIABLE_HEADER *VariableHeader; + + static EFI_GUID FvHdrGUID = EFI_SYSTEM_NV_DATA_FV_GUID; + static EFI_GUID VarStoreHdrGUID = EFI_AUTHENTICATED_VARIABLE_GUID; + + VariableOffset = 0; + + if (TdxCfvBase == NULL) { + DEBUG ((DEBUG_ERROR, "TDX CFV: CFV pointer is NULL\n")); + return FALSE; + } + + // + // Verify the header zerovetor, filesystemguid, + // revision, signature, attributes, fvlength, checksum + // HeaderLength cannot be an odd number + // + CfvFvHeader = (EFI_FIRMWARE_VOLUME_HEADER *) TdxCfvBase; + + if ((!IsZeroBuffer (CfvFvHeader->ZeroVector, 16)) || + (!CompareGuid (&FvHdrGUID, &CfvFvHeader->FileSystemGuid)) || + (CfvFvHeader->Signature != EFI_FVH_SIGNATURE) || + (CfvFvHeader->Attributes != 0x4feff) || + (CfvFvHeader->HeaderLength != EMU_FV_HEADER_LENGTH) || + (CfvFvHeader->Revision != EFI_FVH_REVISION) + ) { + DEBUG ((DEBUG_ERROR, "TDX CFV: Basic FV headers were invalid\n")); + return FALSE; + } + + // + // Verify the header checksum + // + Checksum = CalculateSum16 ((VOID*) CfvFvHeader, CfvFvHeader->HeaderLength); + + if (Checksum != 0) { + DEBUG ((DEBUG_ERROR, "TDX CFV: FV checksum was invalid\n")); + return FALSE; + } + + // + // Verify the header signature, size, format, state + // + CfvVariableStoreHeader = (VARIABLE_STORE_HEADER *) (TdxCfvBase + CfvFvHeader->HeaderLength); + if ((!CompareGuid (&VarStoreHdrGUID, &CfvVariableStoreHeader->Signature)) || + (CfvVariableStoreHeader->Format != VARIABLE_STORE_FORMATTED) || + (CfvVariableStoreHeader->State != VARIABLE_STORE_HEALTHY) + ) { + DEBUG ((DEBUG_ERROR, "TDX CFV: Variable Store header was invalid\n")); + return FALSE; + } + + // + // Verify the header startId, state + // Verify data to the end + // + VariableBase = (UINTN)TdxCfvBase + CfvFvHeader->HeaderLength + sizeof (VARIABLE_STORE_HEADER); + while (VariableOffset < (CfvVariableStoreHeader->Size - sizeof (VARIABLE_STORE_HEADER))) { + VariableHeader = (AUTHENTICATED_VARIABLE_HEADER *) (VariableBase + VariableOffset); + if (VariableHeader->StartId != VARIABLE_DATA) { + if (!CheckPaddingData ((UINT8 *)VariableHeader, CfvVariableStoreHeader->Size - sizeof(VARIABLE_STORE_HEADER) - VariableOffset)) { + DEBUG ((DEBUG_ERROR, "TDX CFV: Variable header was invalid\n")); + return FALSE; + } + VariableOffset = CfvVariableStoreHeader->Size - sizeof(VARIABLE_STORE_HEADER); + } + else { + if (!((VariableHeader->State == VAR_IN_DELETED_TRANSITION) || + (VariableHeader->State == VAR_DELETED) || + (VariableHeader->State == VAR_HEADER_VALID_ONLY) || + (VariableHeader->State == VAR_ADDED))) { + DEBUG ((DEBUG_ERROR, "TDX CFV: Variable header was invalid\n")); + return FALSE; + } + + VariableOffset += sizeof (AUTHENTICATED_VARIABLE_HEADER) + VariableHeader->NameSize + VariableHeader->DataSize; + // Verify VariableOffset should be less than or equal CfvVariableStoreHeader->Size - sizeof(VARIABLE_STORE_HEADER) + if (VariableOffset > (CfvVariableStoreHeader->Size - sizeof(VARIABLE_STORE_HEADER))) { + DEBUG ((DEBUG_ERROR, "TDX CFV: Variable header was invalid\n")); + return FALSE; + } + + VariableOffsetBeforeAlign = VariableOffset; + // 4 byte align + VariableOffset = (VariableOffset + 3) & (UINTN)(~3); + + if (!CheckPaddingData ((UINT8 *)(VariableBase + VariableOffsetBeforeAlign), VariableOffset - VariableOffsetBeforeAlign)) { + DEBUG ((DEBUG_ERROR, "TDX CFV: Variable header was invalid\n")); + return FALSE; + } + } + } + + return TRUE; + +} + + /** Initializes the FV Header and Variable Store Header to support variable operations. @@ -718,6 +867,8 @@ FvbInitialize ( EFI_HANDLE Handle; EFI_PHYSICAL_ADDRESS Address; RETURN_STATUS PcdStatus; + UINT8 *CfvBase; + UINT32 CfvSize; DEBUG ((DEBUG_INFO, "EMU Variable FVB Started\n")); @@ -770,7 +921,21 @@ FvbInitialize ( mEmuVarsFvb.BufferPtr = Ptr; // - // Initialize the main FV header and variable store header + // Copy the content from Configuration FV in Td guest + // + if (ProbeTdGuest()) { + CfvBase = (UINT8*)(UINTN)PcdGet32(PcdCfvBase); + CfvSize = (UINT32)PcdGet32(PcdCfvRawDataSize); + DEBUG ((DEBUG_INFO, "Copy the content from Configuration FV. 0x%p : 0x%x\n", CfvBase, CfvSize)); + + if (ValidateTdxCfv(CfvBase, CfvSize)) { + CopyMem(Ptr, CfvBase, CfvSize); + Initialize = FALSE; + } + } + + // + // Initialize the main FV header and variable store header. // if (Initialize) { SetMem (Ptr, EMU_FVB_SIZE, ERASED_UINT8); diff --git a/OvmfPkg/EmuVariableFvbRuntimeDxe/Fvb.inf b/OvmfPkg/EmuVariableFvbRuntimeDxe/Fvb.inf index 225ea27f5ffd..f6c4dcf4c5f4 100644 --- a/OvmfPkg/EmuVariableFvbRuntimeDxe/Fvb.inf +++ b/OvmfPkg/EmuVariableFvbRuntimeDxe/Fvb.inf @@ -44,6 +44,7 @@ UefiDriverEntryPoint UefiLib UefiRuntimeLib + TdxProbeLib [Guids] gEfiEventVirtualAddressChangeGuid # ALWAYS_CONSUMED Create Event: EVENT_GROUP_GUID @@ -56,6 +57,8 @@ gEfiMdeModulePkgTokenSpaceGuid.PcdFlashNvStorageVariableSize gEfiMdeModulePkgTokenSpaceGuid.PcdFlashNvStorageFtwWorkingSize gEfiMdeModulePkgTokenSpaceGuid.PcdFlashNvStorageFtwSpareSize + gUefiOvmfPkgTokenSpaceGuid.PcdCfvBase + gUefiOvmfPkgTokenSpaceGuid.PcdCfvRawDataSize [Pcd] gEfiMdeModulePkgTokenSpaceGuid.PcdFlashNvStorageVariableBase64 diff --git a/OvmfPkg/Include/IndustryStandard/AcpiTdx.h b/OvmfPkg/Include/IndustryStandard/AcpiTdx.h new file mode 100644 index 000000000000..16d96b83e20e --- /dev/null +++ b/OvmfPkg/Include/IndustryStandard/AcpiTdx.h @@ -0,0 +1,30 @@ +/** @file + Processor or Compiler specific defines and types x64 (Intel 64, AMD64). + + Copyright (c) 2006 - 2020, Intel Corporation. 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 _ACPI_TDX_H_ +#define _ACPI_TDX_H_ + +#define ACPI_MADT_MPWK_STRUCT_TYPE 0x10 + +#pragma pack(1) + +typedef struct { + UINT8 Type; + UINT8 Length; + UINT16 MailBoxVersion; + UINT32 Reserved2; + UINT64 MailBoxAddress; +} ACPI_MADT_MPWK_STRUCT; +#pragma pack() +#endif diff --git a/OvmfPkg/Include/Library/MemEncryptLib.h b/OvmfPkg/Include/Library/MemEncryptLib.h new file mode 100644 index 000000000000..c610748cb7b0 --- /dev/null +++ b/OvmfPkg/Include/Library/MemEncryptLib.h @@ -0,0 +1,20 @@ +/** @file + + Define Memory Encrypted Virtualization base library helper function + + Copyright (c) 2020, Intel Corporation. All rights reserved.
+ Copyright (c) 2017, AMD Incorporated. All rights reserved.
+ + SPDX-License-Identifier: BSD-2-Clause-Patent + +**/ + +#ifndef _MEM_ENCRYPT_LIB_H_ +#define _MEM_ENCRYPT_LIB_H_ + +#define MEM_ENCRYPT_NONE_ENABLED 0x00 +#define MEM_ENCRYPT_SEV_ENABLED 0x01 +#define MEM_ENCRYPT_SEVES_ENABLED 0x02 +#define MEM_ENCRYPT_TDX_ENABLED 0x04 + +#endif // _MEM_ENCRYPT_LIB_H_ diff --git a/OvmfPkg/Include/Library/MemEncryptTdxLib.h b/OvmfPkg/Include/Library/MemEncryptTdxLib.h new file mode 100644 index 000000000000..a93f23affc63 --- /dev/null +++ b/OvmfPkg/Include/Library/MemEncryptTdxLib.h @@ -0,0 +1,109 @@ +/** @file + + Define Memory Encrypted Virtualization base library helper function + + Copyright (c) 2020, Intel Corporation. All rights reserved.
+ Copyright (c) 2017, AMD Incorporated. All rights reserved.
+ + SPDX-License-Identifier: BSD-2-Clause-Patent + +**/ + +#ifndef _MEM_ENCRYPT_TDX_LIB_H_ +#define _MEM_ENCRYPT_TDX_LIB_H_ + +#include + +/** + Returns boolean to indicate whether to indicate which, if any, memory encryption is enabled + + @param[in] Type Bitmask of encryption technologies to check is enabled + + @retval TRUE The encryption type(s) are enabled + @retval FALSE The encryption type(s) are not enabled +**/ +BOOLEAN +EFIAPI +MemEncryptTdxIsEnabled ( + VOID + ); + +/** + This function clears memory encryption bit for the memory region specified by + BaseAddress and NumPages from the current page table context. + + @param[in] Cr3BaseAddress Cr3 Base Address (if zero then use + current CR3) + @param[in] BaseAddress The physical address that is the start + address of a memory region. + @param[in] NumPages 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 +MemEncryptTdxClearPageEncMask ( + IN PHYSICAL_ADDRESS Cr3BaseAddress, + IN PHYSICAL_ADDRESS BaseAddress, + IN UINTN NumPages, + IN BOOLEAN Flush + ); + +/** + This function sets memory encryption bit for the memory region specified by + BaseAddress and NumPages from the current page table context. + + @param[in] Cr3BaseAddress Cr3 Base Address (if zero then use + current CR3) + @param[in] BaseAddress The physical address that is the start + address of a memory region. + @param[in] NumPages The number of pages from start memory + region. + @param[in] Flush Flush the caches before setting 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 Setting the memory encryption attribute + is not supported +**/ +RETURN_STATUS +EFIAPI +MemEncryptTdxSetPageEncMask ( + IN PHYSICAL_ADDRESS Cr3BaseAddress, + IN PHYSICAL_ADDRESS BaseAddress, + IN UINTN NumPages, + IN BOOLEAN Flush + ); + + +/** + Locate the page range that covers the initial (pre-SMBASE-relocation) SMRAM + Save State Map. + + @param[out] BaseAddress The base address of the lowest-address page that + covers the initial SMRAM Save State Map. + + @param[out] NumberOfPages The number of pages in the page range that covers + the initial SMRAM Save State Map. + + @retval RETURN_SUCCESS BaseAddress and NumberOfPages have been set on + output. + + @retval RETURN_UNSUPPORTED SMM is unavailable. +**/ +RETURN_STATUS +EFIAPI +MemEncryptLocateInitialSmramSaveStateMapPages ( + OUT UINTN *BaseAddress, + OUT UINTN *NumberOfPages + ); +#endif // _MEM_ENCRYPT_TDX_LIB_H_ diff --git a/OvmfPkg/Include/Library/PrePiLibTdx.h b/OvmfPkg/Include/Library/PrePiLibTdx.h new file mode 100644 index 000000000000..405ee4e5c628 --- /dev/null +++ b/OvmfPkg/Include/Library/PrePiLibTdx.h @@ -0,0 +1,241 @@ +/** @file + Library that helps implement monolithic PEI. (SEC goes to DXE) + + Copyright (c) 2008 - 2009, Apple Inc. All rights reserved.
+ + SPDX-License-Identifier: BSD-2-Clause-Patent + +**/ + +#ifndef __PRE_PI_LIB_TDX_H__ +#define __PRE_PI_LIB_TDX_H__ + +/** + This service enables discovery of additional firmware volumes. + + @param Instance This instance of the firmware volume to find. The value 0 is the + Boot Firmware Volume (BFV). + @param FwVolHeader Pointer to the firmware volume header of the volume to return. + + @retval EFI_SUCCESS The volume was found. + @retval EFI_NOT_FOUND The volume was not found. + @retval EFI_INVALID_PARAMETER FwVolHeader is NULL. + +**/ +EFI_STATUS +EFIAPI +FfsFindNextVolume ( + IN UINTN Instance, + IN OUT EFI_PEI_FV_HANDLE *VolumeHandle + ); + + +/** + This service enables discovery of additional firmware files. + + @param SearchType A filter to find files only of this type. + @param FwVolHeader Pointer to the firmware volume header of the volume to search. + This parameter must point to a valid FFS volume. + @param FileHeader Pointer to the current file from which to begin searching. + + @retval EFI_SUCCESS The file was found. + @retval EFI_NOT_FOUND The file was not found. + @retval EFI_NOT_FOUND The header checksum was not zero. + +**/ +EFI_STATUS +EFIAPI +FfsFindNextFile ( + IN EFI_FV_FILETYPE SearchType, + IN EFI_PEI_FV_HANDLE VolumeHandle, + IN OUT EFI_PEI_FILE_HANDLE *FileHandle + ); + + +/** + This service enables discovery sections of a given type within a valid FFS file. + + @param SearchType The value of the section type to find. + @param FfsFileHeader A pointer to the file header that contains the set of sections to + be searched. + @param SectionData A pointer to the discovered section, if successful. + + @retval EFI_SUCCESS The section was found. + @retval EFI_NOT_FOUND The section was not found. + +**/ +EFI_STATUS +EFIAPI +FfsFindSectionData ( + IN EFI_SECTION_TYPE SectionType, + IN EFI_PEI_FILE_HANDLE FileHandle, + OUT VOID **SectionData + ); + + +/** + Find a file in the volume by name + + @param FileName A pointer to the name of the file to + find within the firmware volume. + + @param VolumeHandle The firmware volume to search FileHandle + Upon exit, points to the found file's + handle or NULL if it could not be found. + + @retval EFI_SUCCESS File was found. + + @retval EFI_NOT_FOUND File was not found. + + @retval EFI_INVALID_PARAMETER VolumeHandle or FileHandle or + FileName was NULL. + +**/ +EFI_STATUS +EFIAPI +FfsFindFileByName ( + IN CONST EFI_GUID *FileName, + IN CONST EFI_PEI_FV_HANDLE VolumeHandle, + OUT EFI_PEI_FILE_HANDLE *FileHandle + ); + + +/** + Get information about the file by name. + + @param FileHandle Handle of the file. + + @param FileInfo Upon exit, points to the file's + information. + + @retval EFI_SUCCESS File information returned. + + @retval EFI_INVALID_PARAMETER If FileHandle does not + represent a valid file. + + @retval EFI_INVALID_PARAMETER If FileInfo is NULL. + +**/ +EFI_STATUS +EFIAPI +FfsGetFileInfo ( + IN CONST EFI_PEI_FILE_HANDLE FileHandle, + OUT EFI_FV_FILE_INFO *FileInfo + ); + + +/** + Get Information about the volume by name + + @param VolumeHandle Handle of the volume. + + @param VolumeInfo Upon exit, points to the volume's + information. + + @retval EFI_SUCCESS File information returned. + + @retval EFI_INVALID_PARAMETER If FileHandle does not + represent a valid file. + + @retval EFI_INVALID_PARAMETER If FileInfo is NULL. + +**/ +EFI_STATUS +EFIAPI +FfsGetVolumeInfo ( + IN EFI_PEI_FV_HANDLE VolumeHandle, + OUT EFI_FV_INFO *VolumeInfo + ); + + + +/** + Get Fv image from the FV type file, then add FV & FV2 Hob. + + @param FileHandle File handle of a Fv type file. + + @retval EFI_NOT_FOUND FV image can't be found. + @retval EFI_SUCCESS Successfully to process it. + +**/ +EFI_STATUS +EFIAPI +FfsProcessFvFile ( + IN EFI_PEI_FILE_HANDLE FvFileHandle + ); + + +/** + Search through every FV until you find a file of type FileType + + @param FileType File handle of a Fv type file. + @param Volumehandle On success Volume Handle of the match + @param FileHandle On success File Handle of the match + + @retval EFI_NOT_FOUND FV image can't be found. + @retval EFI_SUCCESS Successfully found FileType + +**/ +EFI_STATUS +EFIAPI +FfsAnyFvFindFirstFile ( + IN EFI_FV_FILETYPE FileType, + OUT EFI_PEI_FV_HANDLE *VolumeHandle, + OUT EFI_PEI_FILE_HANDLE *FileHandle + ); + +EFI_STATUS +EFIAPI +FfsAnyFvFindFileByName ( + IN CONST EFI_GUID *Name, + OUT EFI_PEI_FV_HANDLE *VolumeHandle, + OUT EFI_PEI_FILE_HANDLE *FileHandle + ); + + +/** + Get Fv image from the FV type file, then add FV & FV2 Hob. + + @param FileHandle File handle of a Fv type file. + + + @retval EFI_NOT_FOUND FV image can't be found. + @retval EFI_SUCCESS Successfully to process it. + +**/ +EFI_STATUS +EFIAPI +FfsProcessFvFile ( + IN EFI_PEI_FILE_HANDLE FvFileHandle + ); + +EFI_STATUS +EFIAPI +LoadPeCoffImage ( + IN VOID *PeCoffImage, + OUT EFI_PHYSICAL_ADDRESS *ImageAddress, + OUT UINT64 *ImageSize, + OUT EFI_PHYSICAL_ADDRESS *EntryPoint + ); + +EFI_STATUS +EFIAPI +LoadDxeCoreFromFfsFile ( + IN EFI_PEI_FILE_HANDLE FileHandle, + IN UINTN StackSize + ); + +EFI_STATUS +EFIAPI +LoadDxeCoreFromFv ( + IN UINTN *FvInstance, OPTIONAL + IN UINTN StackSize + ); + +EFI_STATUS +EFIAPI +DecompressFirstFv ( + VOID + ); + +#endif diff --git a/OvmfPkg/Include/Library/TdvfPlatformLib.h b/OvmfPkg/Include/Library/TdvfPlatformLib.h new file mode 100644 index 000000000000..27bfb03ee2e8 --- /dev/null +++ b/OvmfPkg/Include/Library/TdvfPlatformLib.h @@ -0,0 +1,27 @@ +#ifndef __TDVF_PLATFORM_LIB_H__ +#define __TDVF_PLATFORM_LIB_H__ + +#include +#include +#include +#include +#include + +#define EFI_RESOURCE_ATTRIBUTE_ENCRYPTED 0x04000000 +#pragma pack(1) +typedef struct { + /// + EFI_HOB_GUID_TYPE GuidHeader; + UINT64 RelocatedMailBox; + UINT16 HostBridgePciDevId; + BOOLEAN SetNxForStack; + UINT8 SystemStates[6]; +} EFI_HOB_PLATFORM_INFO; +#pragma pack() +VOID +EFIAPI +TdvfPlatformInitialize ( + IN OUT EFI_HOB_PLATFORM_INFO * + ); + +#endif diff --git a/OvmfPkg/Include/Library/TdxStartupLib.h b/OvmfPkg/Include/Library/TdxStartupLib.h new file mode 100644 index 000000000000..1a53253cca27 --- /dev/null +++ b/OvmfPkg/Include/Library/TdxStartupLib.h @@ -0,0 +1,40 @@ +#ifndef __TDX_STARTUP_LIB_H__ +#define __TDX_STARTUP_LIB_H__ + +#include +#include +#include +#include +#include +#include +#include + +#pragma pack(1) + +typedef struct { + UINT32 count; + TPMI_ALG_HASH hashAlg; + BYTE sha384[SHA384_DIGEST_SIZE]; +} TDX_DIGEST_VALUE; + +#pragma pack() + + +typedef +VOID +(EFIAPI * fProcessLibraryConstructorList)( + IN EFI_PEI_FILE_HANDLE FileHandle, + IN CONST EFI_PEI_SERVICES **PeiServices + ); + + +VOID +EFIAPI +TdxStartup( + IN VOID * Context, + IN VOID * VmmHobList, + IN UINTN Info, + IN fProcessLibraryConstructorList Function +); + +#endif diff --git a/OvmfPkg/Include/TdxCommondefs.inc b/OvmfPkg/Include/TdxCommondefs.inc new file mode 100644 index 000000000000..673e5c867b4d --- /dev/null +++ b/OvmfPkg/Include/TdxCommondefs.inc @@ -0,0 +1,114 @@ +;------------------------------------------------------------------------------ +; @file +; TDX Common defitions used by the APs in mailbox +; +; Copyright (c) 2021, Intel Corporation. All rights reserved.
+; SPDX-License-Identifier: BSD-2-Clause-Patent +; +;------------------------------------------------------------------------------ + +CommandOffset equ 00h +ApicidOffset equ 04h +WakeupVectorOffset equ 08h +OSArgsOffset equ 10h +FirmwareArgsOffset equ 800h +WakeupArgsRelocatedMailBox equ 800h +AcceptPageArgsPhysicalStart equ 800h +AcceptPageArgsPhysicalEnd equ 808h +AcceptPageArgsAcceptSize equ 810h +;AcceptPageArgsTallies equ 818h +AcceptPageArgsPageSize equ 818h +CpuArrivalOffset equ 900h +CpusExitingOffset equ 0a00h +TalliesOffset equ 0a08h + +SIZE_4KB equ 1000h +SIZE_2MB equ 200000h +SIZE_1GB equ 40000000h + +MpProtectedModeWakeupCommandNoop equ 0 +MpProtectedModeWakeupCommandWakeup equ 1 +MpProtectedModeWakeupCommandSleep equ 2 +MpProtectedModeWakeupCommandAcceptPages equ 3 + +MailboxApicIdInvalid equ 0xffffffff +MailboxApicidBroadcast equ 0xfffffffe + +%macro simple_spinlock 3 + mov edx, %1 + mov eax, 0 + mov ebx, 1 +%%testlock: + lock cmpxchg [edx], ebx + jnz %3 + mov eax, 0 + mov ebx, 1 + lock cmpxchg [edx+4], ebx + jnz %2 +%%firstone: + pause +%endmacro + +%macro simple_releaselock 3 +%2: + mov eax, 1 + mov edx, %1 + jmp %%testlock +%3: + pause + mov eax, 0 +%%testlock: + mov ebx, 0 + lock cmpxchg [edx], ebx + jnz %3 +%endmacro + +%define EFI_HOB_TYPE_HANDOFF 0x0001 +%define EFI_HOB_TYPE_MEMORY_ALLOCATION 0x0002 +%define EFI_HOB_TYPE_RESOURCE_DESCRIPTOR 0x0003 +%define EFI_HOB_TYPE_GUID_EXTENSION 0x0004 +%define EFI_HOB_TYPE_FV 0x0005 +%define EFI_HOB_TYPE_CPU 0x0006 +%define EFI_HOB_TYPE_MEMORY_POOL 0x0007 +%define EFI_HOB_TYPE_FV2 0x0009 +%define EFI_HOB_TYPE_LOAD_PEIM_UNUSED 0x000A +%define EFI_HOB_TYPE_UEFI_CAPSULE 0x000B +%define EFI_HOB_TYPE_FV3 0x000C +%define EFI_HOB_TYPE_UNUSED 0xFFFE +%define EFI_HOB_TYPE_END_OF_HOB_LIST 0xFFFF + +%define EFI_RESOURCE_SYSTEM_MEMORY 0x00000000 +%define EFI_RESOURCE_MEMORY_MAPPED_IO 0x00000001 +%define EFI_RESOURCE_IO 0x00000002 +%define EFI_RESOURCE_FIRMWARE_DEVICE 0x00000003 +%define EFI_RESOURCE_MEMORY_MAPPED_IO_PORT 0x00000004 +%define EFI_RESOURCE_MEMORY_RESERVED 0x00000005 +%define EFI_RESOURCE_IO_RESERVED 0x00000006 +%define EFI_RESOURCE_MAX_MEMORY_TYPE 0x00000007 + +%define EFI_RESOURCE_ATTRIBUTE_PRESENT 0x00000001 +%define EFI_RESOURCE_ATTRIBUTE_INITIALIZED 0x00000002 +%define EFI_RESOURCE_ATTRIBUTE_TESTED 0x00000004 +%define EFI_RESOURCE_ATTRIBUTE_READ_PROTECTED 0x00000080 + +%define EFI_RESOURCE_ATTRIBUTE_UNCACHEABLE 0x00000400 +%define EFI_RESOURCE_ATTRIBUTE_WRITE_COMBINEABLE 0x00000800 +%define EFI_RESOURCE_ATTRIBUTE_WRITE_THROUGH_CACHEABLE 0x00001000 +%define EFI_RESOURCE_ATTRIBUTE_WRITE_BACK_CACHEABLE 0x00002000 + + +%define EFI_IO_ATTR (EFI_RESOURCE_ATTRIBUTE_PRESENT + \ + EFI_RESOURCE_ATTRIBUTE_INITIALIZED + \ + EFI_RESOURCE_ATTRIBUTE_TESTED + \ + EFI_RESOURCE_ATTRIBUTE_UNCACHEABLE) + +%define EFI_LOW_MEM_ATTR (EFI_RESOURCE_ATTRIBUTE_PRESENT + \ + EFI_RESOURCE_ATTRIBUTE_INITIALIZED + \ + EFI_RESOURCE_ATTRIBUTE_TESTED + \ + EFI_RESOURCE_ATTRIBUTE_WRITE_COMBINEABLE + \ + EFI_RESOURCE_ATTRIBUTE_WRITE_THROUGH_CACHEABLE + \ + EFI_RESOURCE_ATTRIBUTE_WRITE_BACK_CACHEABLE + \ + EFI_RESOURCE_ATTRIBUTE_UNCACHEABLE) + +%define TDCALL_TDINFO 0x1 +%define TDCALL_TDACCEPTPAGE 0x6 diff --git a/OvmfPkg/IncompatiblePciDeviceSupportDxe/IncompatiblePciDeviceSupport.c b/OvmfPkg/IncompatiblePciDeviceSupportDxe/IncompatiblePciDeviceSupport.c index 53c768167de9..c0f1cddefe6f 100644 --- a/OvmfPkg/IncompatiblePciDeviceSupportDxe/IncompatiblePciDeviceSupport.c +++ b/OvmfPkg/IncompatiblePciDeviceSupportDxe/IncompatiblePciDeviceSupport.c @@ -19,6 +19,7 @@ #include #include +#include // // The Legacy BIOS protocol has been located. @@ -45,9 +46,15 @@ typedef struct { EFI_ACPI_ADDRESS_SPACE_DESCRIPTOR AddressSpaceDesc; EFI_ACPI_END_TAG_DESCRIPTOR EndDesc; } MMIO64_PREFERENCE; + +typedef struct { + EFI_ACPI_ADDRESS_SPACE_DESCRIPTOR AddressSpaceDesc; + EFI_ACPI_END_TAG_DESCRIPTOR EndDesc; +} OPTION_ROM_PREFERENCE; + #pragma pack () -STATIC CONST MMIO64_PREFERENCE mConfiguration = { +STATIC CONST MMIO64_PREFERENCE mMmio6Configuration = { // // AddressSpaceDesc // @@ -85,6 +92,44 @@ STATIC CONST MMIO64_PREFERENCE mConfiguration = { } }; +STATIC CONST OPTION_ROM_PREFERENCE mOptionRomConfiguration = { + // + // AddressSpaceDesc + // + { + ACPI_ADDRESS_SPACE_DESCRIPTOR, // Desc + (UINT16)( // Len + sizeof (EFI_ACPI_ADDRESS_SPACE_DESCRIPTOR) - + OFFSET_OF ( + EFI_ACPI_ADDRESS_SPACE_DESCRIPTOR, + ResType + ) + ), + ACPI_ADDRESS_SPACE_TYPE_MEM, // ResType + 0, // GenFlag + BIT0, // Disable option roms SpecificFlag + 64, // AddrSpaceGranularity: + // aperture selection hint + // for BAR allocation + MAX_UINT64, // AddrRangeMin + MAX_UINT64, // AddrRangeMax: + // no special alignment + // for affected BARs + MAX_UINT64, // AddrTranslationOffset: + // hint covers all + // eligible BARs + 0 // AddrLen: + // use probed BAR size + }, + // + // EndDesc + // + { + ACPI_END_TAG_DESCRIPTOR, // Desc + 0 // Checksum: to be ignored + } +}; + // // The CheckDevice() member function has been called. // @@ -201,7 +246,20 @@ CheckDevice ( OUT VOID **Configuration ) { + CONST VOID *ConfigurationPtr; + UINTN ConfigurationSize; + BOOLEAN TdGuest; + mCheckDeviceCalled = TRUE; + TdGuest = ProbeTdGuest(); + if(TdGuest) { + ConfigurationPtr = &mOptionRomConfiguration; + ConfigurationSize = sizeof(mOptionRomConfiguration); + goto AllocateConfiguration; + } else { + ConfigurationPtr = &mMmio6Configuration; + ConfigurationSize = sizeof(mMmio6Configuration); + } // // Unlike the general description of this protocol member suggests, there is @@ -231,11 +289,19 @@ CheckDevice ( // the edk2 PCI Bus UEFI_DRIVER actually handles error codes; see the // UpdatePciInfo() function. // - *Configuration = AllocateCopyPool (sizeof mConfiguration, &mConfiguration); +AllocateConfiguration: + *Configuration = AllocateCopyPool (ConfigurationSize, ConfigurationPtr); if (*Configuration == NULL) { + if(TdGuest) { DEBUG ((DEBUG_WARN, - "%a: 64-bit MMIO BARs may be degraded for PCI 0x%04x:0x%04x (rev %d)\n", - __FUNCTION__, (UINT32)VendorId, (UINT32)DeviceId, (UINT8)RevisionId)); + "%a: Check device for Option ROM of PCI 0x%04x:0x%04x (rev %d) failed.\n\n", + __FUNCTION__, (UINT32)VendorId, (UINT32)DeviceId, (UINT8)RevisionId)); + } else { + DEBUG ((DEBUG_WARN, + "%a: 64-bit MMIO BARs may be degraded for PCI 0x%04x:0x%04x (rev %d)\n", + __FUNCTION__, (UINT32)VendorId, (UINT32)DeviceId, (UINT8)RevisionId)); + } + return EFI_OUT_OF_RESOURCES; } return EFI_SUCCESS; @@ -265,6 +331,10 @@ DriverInitialize ( EFI_EVENT Event; VOID *Registration; + if(ProbeTdGuest()) { + goto InstallProtocol; + } + // // If the PCI Bus driver is not supposed to allocate resources, then it makes // no sense to install a protocol that influences the resource allocation. @@ -326,6 +396,7 @@ DriverInitialize ( Status = gBS->SignalEvent (Event); ASSERT_EFI_ERROR (Status); +InstallProtocol: mIncompatiblePciDeviceSupport.CheckDevice = CheckDevice; Status = gBS->InstallMultipleProtocolInterfaces (&ImageHandle, &gEfiIncompatiblePciDeviceSupportProtocolGuid, diff --git a/OvmfPkg/IncompatiblePciDeviceSupportDxe/IncompatiblePciDeviceSupport.inf b/OvmfPkg/IncompatiblePciDeviceSupportDxe/IncompatiblePciDeviceSupport.inf index f08b6f4bd4b4..971178804704 100644 --- a/OvmfPkg/IncompatiblePciDeviceSupportDxe/IncompatiblePciDeviceSupport.inf +++ b/OvmfPkg/IncompatiblePciDeviceSupportDxe/IncompatiblePciDeviceSupport.inf @@ -30,6 +30,7 @@ PcdLib UefiBootServicesTableLib UefiDriverEntryPoint + TdxProbeLib [Protocols] gEfiIncompatiblePciDeviceSupportProtocolGuid ## SOMETIMES_PRODUCES diff --git a/OvmfPkg/IoMmuDxe/AmdSevIoMmu.c b/OvmfPkg/IoMmuDxe/IoMmu.c similarity index 92% rename from OvmfPkg/IoMmuDxe/AmdSevIoMmu.c rename to OvmfPkg/IoMmuDxe/IoMmu.c index 49ffa2448811..cb6eb0855b8b 100644 --- a/OvmfPkg/IoMmuDxe/AmdSevIoMmu.c +++ b/OvmfPkg/IoMmuDxe/IoMmu.c @@ -1,910 +1,930 @@ -/** @file - - The protocol provides support to allocate, free, map and umap a DMA buffer - for bus master (e.g PciHostBridge). When SEV is enabled, the DMA operations - must be performed on unencrypted buffer hence we use a bounce buffer to map - the guest buffer into an unencrypted DMA buffer. - - Copyright (c) 2017, AMD Inc. All rights reserved.
- Copyright (c) 2017, Intel Corporation. All rights reserved.
- - SPDX-License-Identifier: BSD-2-Clause-Patent - -**/ - -#include "AmdSevIoMmu.h" - -#define MAP_INFO_SIG SIGNATURE_64 ('M', 'A', 'P', '_', 'I', 'N', 'F', 'O') - -typedef struct { - UINT64 Signature; - LIST_ENTRY Link; - EDKII_IOMMU_OPERATION Operation; - UINTN NumberOfBytes; - UINTN NumberOfPages; - EFI_PHYSICAL_ADDRESS CryptedAddress; - EFI_PHYSICAL_ADDRESS PlainTextAddress; -} MAP_INFO; - -// -// List of the MAP_INFO structures that have been set up by IoMmuMap() and not -// yet torn down by IoMmuUnmap(). The list represents the full set of mappings -// currently in effect. -// -STATIC LIST_ENTRY mMapInfos = INITIALIZE_LIST_HEAD_VARIABLE (mMapInfos); - -#define COMMON_BUFFER_SIG SIGNATURE_64 ('C', 'M', 'N', 'B', 'U', 'F', 'F', 'R') - -// -// ASCII names for EDKII_IOMMU_OPERATION constants, for debug logging. -// -STATIC CONST CHAR8 * CONST -mBusMasterOperationName[EdkiiIoMmuOperationMaximum] = { - "Read", - "Write", - "CommonBuffer", - "Read64", - "Write64", - "CommonBuffer64" -}; - -// -// The following structure enables Map() and Unmap() to perform in-place -// decryption and encryption, respectively, for BusMasterCommonBuffer[64] -// operations, without dynamic memory allocation or release. -// -// Both COMMON_BUFFER_HEADER and COMMON_BUFFER_HEADER.StashBuffer are allocated -// by AllocateBuffer() and released by FreeBuffer(). -// -#pragma pack (1) -typedef struct { - UINT64 Signature; - - // - // Always allocated from EfiBootServicesData type memory, and always - // encrypted. - // - VOID *StashBuffer; - - // - // Followed by the actual common buffer, starting at the next page. - // -} COMMON_BUFFER_HEADER; -#pragma pack () - -/** - Provides the controller-specific addresses required to access system memory - from a DMA bus master. On SEV guest, the DMA operations must be performed on - shared buffer hence we allocate a bounce buffer to map the HostAddress to a - DeviceAddress. The Encryption attribute is removed from the DeviceAddress - buffer. - - @param This The protocol instance pointer. - @param Operation Indicates if the bus master is going to read or - write to system memory. - @param HostAddress The system memory address to map to the PCI - controller. - @param NumberOfBytes On input the number of bytes to map. On output - the number of bytes that were mapped. - @param DeviceAddress The resulting map address for the bus master - PCI controller to use to access the hosts - HostAddress. - @param Mapping A resulting value to pass to Unmap(). - - @retval EFI_SUCCESS The range was mapped for the returned - NumberOfBytes. - @retval EFI_UNSUPPORTED The HostAddress cannot be mapped as a common - buffer. - @retval EFI_INVALID_PARAMETER One or more parameters are invalid. - @retval EFI_OUT_OF_RESOURCES The request could not be completed due to a - lack of resources. - @retval EFI_DEVICE_ERROR The system hardware could not map the requested - address. - -**/ -EFI_STATUS -EFIAPI -IoMmuMap ( - IN EDKII_IOMMU_PROTOCOL *This, - IN EDKII_IOMMU_OPERATION Operation, - IN VOID *HostAddress, - IN OUT UINTN *NumberOfBytes, - OUT EFI_PHYSICAL_ADDRESS *DeviceAddress, - OUT VOID **Mapping - ) -{ - EFI_STATUS Status; - MAP_INFO *MapInfo; - EFI_ALLOCATE_TYPE AllocateType; - COMMON_BUFFER_HEADER *CommonBufferHeader; - VOID *DecryptionSource; - - DEBUG (( - DEBUG_VERBOSE, - "%a: Operation=%a Host=0x%p Bytes=0x%Lx\n", - __FUNCTION__, - ((Operation >= 0 && - Operation < ARRAY_SIZE (mBusMasterOperationName)) ? - mBusMasterOperationName[Operation] : - "Invalid"), - HostAddress, - (UINT64)((NumberOfBytes == NULL) ? 0 : *NumberOfBytes) - )); - - if (HostAddress == NULL || NumberOfBytes == NULL || DeviceAddress == NULL || - Mapping == NULL) { - return EFI_INVALID_PARAMETER; - } - - // - // Allocate a MAP_INFO structure to remember the mapping when Unmap() is - // called later. - // - MapInfo = AllocatePool (sizeof (MAP_INFO)); - if (MapInfo == NULL) { - Status = EFI_OUT_OF_RESOURCES; - goto Failed; - } - - // - // Initialize the MAP_INFO structure, except the PlainTextAddress field - // - ZeroMem (&MapInfo->Link, sizeof MapInfo->Link); - MapInfo->Signature = MAP_INFO_SIG; - MapInfo->Operation = Operation; - MapInfo->NumberOfBytes = *NumberOfBytes; - MapInfo->NumberOfPages = EFI_SIZE_TO_PAGES (MapInfo->NumberOfBytes); - MapInfo->CryptedAddress = (UINTN)HostAddress; - - // - // In the switch statement below, we point "MapInfo->PlainTextAddress" to the - // plaintext buffer, according to Operation. We also set "DecryptionSource". - // - MapInfo->PlainTextAddress = MAX_ADDRESS; - AllocateType = AllocateAnyPages; - DecryptionSource = (VOID *)(UINTN)MapInfo->CryptedAddress; - switch (Operation) { - // - // For BusMasterRead[64] and BusMasterWrite[64] operations, a bounce buffer - // is necessary regardless of whether the original (crypted) buffer crosses - // the 4GB limit or not -- we have to allocate a separate plaintext buffer. - // The only variable is whether the plaintext buffer should be under 4GB. - // - case EdkiiIoMmuOperationBusMasterRead: - case EdkiiIoMmuOperationBusMasterWrite: - MapInfo->PlainTextAddress = BASE_4GB - 1; - AllocateType = AllocateMaxAddress; - // - // fall through - // - case EdkiiIoMmuOperationBusMasterRead64: - case EdkiiIoMmuOperationBusMasterWrite64: - // - // Allocate the implicit plaintext bounce buffer. - // - Status = gBS->AllocatePages ( - AllocateType, - EfiBootServicesData, - MapInfo->NumberOfPages, - &MapInfo->PlainTextAddress - ); - if (EFI_ERROR (Status)) { - goto FreeMapInfo; - } - break; - - // - // For BusMasterCommonBuffer[64] operations, a to-be-plaintext buffer and a - // stash buffer (for in-place decryption) have been allocated already, with - // AllocateBuffer(). We only check whether the address of the to-be-plaintext - // buffer is low enough for the requested operation. - // - case EdkiiIoMmuOperationBusMasterCommonBuffer: - if ((MapInfo->CryptedAddress > BASE_4GB) || - (EFI_PAGES_TO_SIZE (MapInfo->NumberOfPages) > - BASE_4GB - MapInfo->CryptedAddress)) { - // - // CommonBuffer operations cannot be remapped. If the common buffer is - // above 4GB, then it is not possible to generate a mapping, so return an - // error. - // - Status = EFI_UNSUPPORTED; - goto FreeMapInfo; - } - // - // fall through - // - case EdkiiIoMmuOperationBusMasterCommonBuffer64: - // - // The buffer at MapInfo->CryptedAddress comes from AllocateBuffer(). - // - MapInfo->PlainTextAddress = MapInfo->CryptedAddress; - // - // Stash the crypted data. - // - CommonBufferHeader = (COMMON_BUFFER_HEADER *)( - (UINTN)MapInfo->CryptedAddress - EFI_PAGE_SIZE - ); - ASSERT (CommonBufferHeader->Signature == COMMON_BUFFER_SIG); - CopyMem ( - CommonBufferHeader->StashBuffer, - (VOID *)(UINTN)MapInfo->CryptedAddress, - MapInfo->NumberOfBytes - ); - // - // Point "DecryptionSource" to the stash buffer so that we decrypt - // it to the original location, after the switch statement. - // - DecryptionSource = CommonBufferHeader->StashBuffer; - break; - - default: - // - // Operation is invalid - // - Status = EFI_INVALID_PARAMETER; - goto FreeMapInfo; - } - - // - // Clear the memory encryption mask on the plaintext buffer. - // - Status = MemEncryptSevClearPageEncMask ( - 0, - MapInfo->PlainTextAddress, - MapInfo->NumberOfPages, - TRUE - ); - ASSERT_EFI_ERROR (Status); - if (EFI_ERROR (Status)) { - CpuDeadLoop (); - } - - // - // If this is a read operation from the Bus Master's point of view, - // then copy the contents of the real buffer into the mapped buffer - // so the Bus Master can read the contents of the real buffer. - // - // For BusMasterCommonBuffer[64] operations, the CopyMem() below will decrypt - // the original data (from the stash buffer) back to the original location. - // - if (Operation == EdkiiIoMmuOperationBusMasterRead || - Operation == EdkiiIoMmuOperationBusMasterRead64 || - Operation == EdkiiIoMmuOperationBusMasterCommonBuffer || - Operation == EdkiiIoMmuOperationBusMasterCommonBuffer64) { - CopyMem ( - (VOID *) (UINTN) MapInfo->PlainTextAddress, - DecryptionSource, - MapInfo->NumberOfBytes - ); - } - - // - // Track all MAP_INFO structures. - // - InsertHeadList (&mMapInfos, &MapInfo->Link); - // - // Populate output parameters. - // - *DeviceAddress = MapInfo->PlainTextAddress; - *Mapping = MapInfo; - - DEBUG (( - DEBUG_VERBOSE, - "%a: Mapping=0x%p Device(PlainText)=0x%Lx Crypted=0x%Lx Pages=0x%Lx\n", - __FUNCTION__, - MapInfo, - MapInfo->PlainTextAddress, - MapInfo->CryptedAddress, - (UINT64)MapInfo->NumberOfPages - )); - - return EFI_SUCCESS; - -FreeMapInfo: - FreePool (MapInfo); - -Failed: - *NumberOfBytes = 0; - return Status; -} - -/** - Completes the Map() operation and releases any corresponding resources. - - This is an internal worker function that only extends the Map() API with - the MemoryMapLocked parameter. - - @param This The protocol instance pointer. - @param Mapping The mapping value returned from Map(). - @param MemoryMapLocked The function is executing on the stack of - gBS->ExitBootServices(); changes to the UEFI - memory map are forbidden. - - @retval EFI_SUCCESS The range was unmapped. - @retval EFI_INVALID_PARAMETER Mapping is not a value that was returned by - Map(). - @retval EFI_DEVICE_ERROR The data was not committed to the target system - memory. -**/ -STATIC -EFI_STATUS -EFIAPI -IoMmuUnmapWorker ( - IN EDKII_IOMMU_PROTOCOL *This, - IN VOID *Mapping, - IN BOOLEAN MemoryMapLocked - ) -{ - MAP_INFO *MapInfo; - EFI_STATUS Status; - COMMON_BUFFER_HEADER *CommonBufferHeader; - VOID *EncryptionTarget; - - DEBUG (( - DEBUG_VERBOSE, - "%a: Mapping=0x%p MemoryMapLocked=%d\n", - __FUNCTION__, - Mapping, - MemoryMapLocked - )); - - if (Mapping == NULL) { - return EFI_INVALID_PARAMETER; - } - - MapInfo = (MAP_INFO *)Mapping; - - // - // set CommonBufferHeader to suppress incorrect compiler/analyzer warnings - // - CommonBufferHeader = NULL; - - // - // For BusMasterWrite[64] operations and BusMasterCommonBuffer[64] operations - // we have to encrypt the results, ultimately to the original place (i.e., - // "MapInfo->CryptedAddress"). - // - // For BusMasterCommonBuffer[64] operations however, this encryption has to - // land in-place, so divert the encryption to the stash buffer first. - // - EncryptionTarget = (VOID *)(UINTN)MapInfo->CryptedAddress; - - switch (MapInfo->Operation) { - case EdkiiIoMmuOperationBusMasterCommonBuffer: - case EdkiiIoMmuOperationBusMasterCommonBuffer64: - ASSERT (MapInfo->PlainTextAddress == MapInfo->CryptedAddress); - - CommonBufferHeader = (COMMON_BUFFER_HEADER *)( - (UINTN)MapInfo->PlainTextAddress - EFI_PAGE_SIZE - ); - ASSERT (CommonBufferHeader->Signature == COMMON_BUFFER_SIG); - EncryptionTarget = CommonBufferHeader->StashBuffer; - // - // fall through - // - - case EdkiiIoMmuOperationBusMasterWrite: - case EdkiiIoMmuOperationBusMasterWrite64: - CopyMem ( - EncryptionTarget, - (VOID *) (UINTN) MapInfo->PlainTextAddress, - MapInfo->NumberOfBytes - ); - break; - - default: - // - // nothing to encrypt after BusMasterRead[64] operations - // - break; - } - - // - // Restore the memory encryption mask on the area we used to hold the - // plaintext. - // - Status = MemEncryptSevSetPageEncMask ( - 0, - MapInfo->PlainTextAddress, - MapInfo->NumberOfPages, - TRUE - ); - ASSERT_EFI_ERROR (Status); - if (EFI_ERROR (Status)) { - CpuDeadLoop (); - } - - // - // For BusMasterCommonBuffer[64] operations, copy the stashed data to the - // original (now encrypted) location. - // - // For all other operations, fill the late bounce buffer (which existed as - // plaintext at some point) with zeros, and then release it (unless the UEFI - // memory map is locked). - // - if (MapInfo->Operation == EdkiiIoMmuOperationBusMasterCommonBuffer || - MapInfo->Operation == EdkiiIoMmuOperationBusMasterCommonBuffer64) { - CopyMem ( - (VOID *)(UINTN)MapInfo->CryptedAddress, - CommonBufferHeader->StashBuffer, - MapInfo->NumberOfBytes - ); - } else { - ZeroMem ( - (VOID *)(UINTN)MapInfo->PlainTextAddress, - EFI_PAGES_TO_SIZE (MapInfo->NumberOfPages) - ); - if (!MemoryMapLocked) { - gBS->FreePages (MapInfo->PlainTextAddress, MapInfo->NumberOfPages); - } - } - - // - // Forget the MAP_INFO structure, then free it (unless the UEFI memory map is - // locked). - // - RemoveEntryList (&MapInfo->Link); - if (!MemoryMapLocked) { - FreePool (MapInfo); - } - - return EFI_SUCCESS; -} - -/** - Completes the Map() operation and releases any corresponding resources. - - @param This The protocol instance pointer. - @param Mapping The mapping value returned from Map(). - - @retval EFI_SUCCESS The range was unmapped. - @retval EFI_INVALID_PARAMETER Mapping is not a value that was returned by - Map(). - @retval EFI_DEVICE_ERROR The data was not committed to the target system - memory. -**/ -EFI_STATUS -EFIAPI -IoMmuUnmap ( - IN EDKII_IOMMU_PROTOCOL *This, - IN VOID *Mapping - ) -{ - return IoMmuUnmapWorker ( - This, - Mapping, - FALSE // MemoryMapLocked - ); -} - -/** - Allocates pages that are suitable for an OperationBusMasterCommonBuffer or - OperationBusMasterCommonBuffer64 mapping. - - @param This The protocol instance pointer. - @param Type This parameter is not used and must be ignored. - @param MemoryType The type of memory to allocate, - EfiBootServicesData or EfiRuntimeServicesData. - @param Pages The number of pages to allocate. - @param HostAddress A pointer to store the base system memory - address of the allocated range. - @param Attributes The requested bit mask of attributes for the - allocated range. - - @retval EFI_SUCCESS The requested memory pages were allocated. - @retval EFI_UNSUPPORTED Attributes is unsupported. The only legal - attribute bits are MEMORY_WRITE_COMBINE and - MEMORY_CACHED. - @retval EFI_INVALID_PARAMETER One or more parameters are invalid. - @retval EFI_OUT_OF_RESOURCES The memory pages could not be allocated. - -**/ -EFI_STATUS -EFIAPI -IoMmuAllocateBuffer ( - IN EDKII_IOMMU_PROTOCOL *This, - IN EFI_ALLOCATE_TYPE Type, - IN EFI_MEMORY_TYPE MemoryType, - IN UINTN Pages, - IN OUT VOID **HostAddress, - IN UINT64 Attributes - ) -{ - EFI_STATUS Status; - EFI_PHYSICAL_ADDRESS PhysicalAddress; - VOID *StashBuffer; - UINTN CommonBufferPages; - COMMON_BUFFER_HEADER *CommonBufferHeader; - - DEBUG (( - DEBUG_VERBOSE, - "%a: MemoryType=%u Pages=0x%Lx Attributes=0x%Lx\n", - __FUNCTION__, - (UINT32)MemoryType, - (UINT64)Pages, - Attributes - )); - - // - // Validate Attributes - // - if ((Attributes & EDKII_IOMMU_ATTRIBUTE_INVALID_FOR_ALLOCATE_BUFFER) != 0) { - return EFI_UNSUPPORTED; - } - - // - // Check for invalid inputs - // - if (HostAddress == NULL) { - return EFI_INVALID_PARAMETER; - } - - // - // The only valid memory types are EfiBootServicesData and - // EfiRuntimeServicesData - // - if (MemoryType != EfiBootServicesData && - MemoryType != EfiRuntimeServicesData) { - return EFI_INVALID_PARAMETER; - } - - // - // We'll need a header page for the COMMON_BUFFER_HEADER structure. - // - if (Pages > MAX_UINTN - 1) { - return EFI_OUT_OF_RESOURCES; - } - CommonBufferPages = Pages + 1; - - // - // Allocate the stash in EfiBootServicesData type memory. - // - // Map() will temporarily save encrypted data in the stash for - // BusMasterCommonBuffer[64] operations, so the data can be decrypted to the - // original location. - // - // Unmap() will temporarily save plaintext data in the stash for - // BusMasterCommonBuffer[64] operations, so the data can be encrypted to the - // original location. - // - // StashBuffer always resides in encrypted memory. - // - StashBuffer = AllocatePages (Pages); - if (StashBuffer == NULL) { - return EFI_OUT_OF_RESOURCES; - } - - PhysicalAddress = (UINTN)-1; - if ((Attributes & EDKII_IOMMU_ATTRIBUTE_DUAL_ADDRESS_CYCLE) == 0) { - // - // Limit allocations to memory below 4GB - // - PhysicalAddress = SIZE_4GB - 1; - } - Status = gBS->AllocatePages ( - AllocateMaxAddress, - MemoryType, - CommonBufferPages, - &PhysicalAddress - ); - if (EFI_ERROR (Status)) { - goto FreeStashBuffer; - } - - CommonBufferHeader = (VOID *)(UINTN)PhysicalAddress; - PhysicalAddress += EFI_PAGE_SIZE; - - CommonBufferHeader->Signature = COMMON_BUFFER_SIG; - CommonBufferHeader->StashBuffer = StashBuffer; - - *HostAddress = (VOID *)(UINTN)PhysicalAddress; - - DEBUG (( - DEBUG_VERBOSE, - "%a: Host=0x%Lx Stash=0x%p\n", - __FUNCTION__, - PhysicalAddress, - StashBuffer - )); - return EFI_SUCCESS; - -FreeStashBuffer: - FreePages (StashBuffer, Pages); - return Status; -} - -/** - Frees memory that was allocated with AllocateBuffer(). - - @param This The protocol instance pointer. - @param Pages The number of pages to free. - @param HostAddress The base system memory address of the allocated - range. - - @retval EFI_SUCCESS The requested memory pages were freed. - @retval EFI_INVALID_PARAMETER The memory range specified by HostAddress and - Pages was not allocated with AllocateBuffer(). - -**/ -EFI_STATUS -EFIAPI -IoMmuFreeBuffer ( - IN EDKII_IOMMU_PROTOCOL *This, - IN UINTN Pages, - IN VOID *HostAddress - ) -{ - UINTN CommonBufferPages; - COMMON_BUFFER_HEADER *CommonBufferHeader; - - DEBUG (( - DEBUG_VERBOSE, - "%a: Host=0x%p Pages=0x%Lx\n", - __FUNCTION__, - HostAddress, - (UINT64)Pages - )); - - CommonBufferPages = Pages + 1; - CommonBufferHeader = (COMMON_BUFFER_HEADER *)( - (UINTN)HostAddress - EFI_PAGE_SIZE - ); - - // - // Check the signature. - // - ASSERT (CommonBufferHeader->Signature == COMMON_BUFFER_SIG); - if (CommonBufferHeader->Signature != COMMON_BUFFER_SIG) { - return EFI_INVALID_PARAMETER; - } - - // - // Free the stash buffer. This buffer was always encrypted, so no need to - // zero it. - // - FreePages (CommonBufferHeader->StashBuffer, Pages); - - // - // Release the common buffer itself. Unmap() has re-encrypted it in-place, so - // no need to zero it. - // - return gBS->FreePages ((UINTN)CommonBufferHeader, CommonBufferPages); -} - - -/** - Set IOMMU attribute for a system memory. - - If the IOMMU protocol exists, the system memory cannot be used - for DMA by default. - - When a device requests a DMA access for a system memory, - the device driver need use SetAttribute() to update the IOMMU - attribute to request DMA access (read and/or write). - - The DeviceHandle is used to identify which device submits the request. - The IOMMU implementation need translate the device path to an IOMMU device - ID, and set IOMMU hardware register accordingly. - 1) DeviceHandle can be a standard PCI device. - The memory for BusMasterRead need set EDKII_IOMMU_ACCESS_READ. - The memory for BusMasterWrite need set EDKII_IOMMU_ACCESS_WRITE. - The memory for BusMasterCommonBuffer need set - EDKII_IOMMU_ACCESS_READ|EDKII_IOMMU_ACCESS_WRITE. - After the memory is used, the memory need set 0 to keep it being - protected. - 2) DeviceHandle can be an ACPI device (ISA, I2C, SPI, etc). - The memory for DMA access need set EDKII_IOMMU_ACCESS_READ and/or - EDKII_IOMMU_ACCESS_WRITE. - - @param[in] This The protocol instance pointer. - @param[in] DeviceHandle The device who initiates the DMA access - request. - @param[in] Mapping The mapping value returned from Map(). - @param[in] IoMmuAccess The IOMMU access. - - @retval EFI_SUCCESS The IoMmuAccess is set for the memory range - specified by DeviceAddress and Length. - @retval EFI_INVALID_PARAMETER DeviceHandle is an invalid handle. - @retval EFI_INVALID_PARAMETER Mapping is not a value that was returned by - Map(). - @retval EFI_INVALID_PARAMETER IoMmuAccess specified an illegal combination - of access. - @retval EFI_UNSUPPORTED DeviceHandle is unknown by the IOMMU. - @retval EFI_UNSUPPORTED The bit mask of IoMmuAccess is not supported - by the IOMMU. - @retval EFI_UNSUPPORTED The IOMMU does not support the memory range - specified by Mapping. - @retval EFI_OUT_OF_RESOURCES There are not enough resources available to - modify the IOMMU access. - @retval EFI_DEVICE_ERROR The IOMMU device reported an error while - attempting the operation. - -**/ -EFI_STATUS -EFIAPI -IoMmuSetAttribute ( - IN EDKII_IOMMU_PROTOCOL *This, - IN EFI_HANDLE DeviceHandle, - IN VOID *Mapping, - IN UINT64 IoMmuAccess - ) -{ - return EFI_UNSUPPORTED; -} - -EDKII_IOMMU_PROTOCOL mAmdSev = { - EDKII_IOMMU_PROTOCOL_REVISION, - IoMmuSetAttribute, - IoMmuMap, - IoMmuUnmap, - IoMmuAllocateBuffer, - IoMmuFreeBuffer, -}; - -/** - Notification function that is queued when gBS->ExitBootServices() signals the - EFI_EVENT_GROUP_EXIT_BOOT_SERVICES event group. This function signals another - event, received as Context, and returns. - - Signaling an event in this context is safe. The UEFI spec allows - gBS->SignalEvent() to return EFI_SUCCESS only; EFI_OUT_OF_RESOURCES is not - listed, hence memory is not allocated. The edk2 implementation also does not - release memory (and we only have to care about the edk2 implementation - because EDKII_IOMMU_PROTOCOL is edk2-specific anyway). - - @param[in] Event Event whose notification function is being invoked. - Event is permitted to request the queueing of this - function at TPL_CALLBACK or TPL_NOTIFY task - priority level. - - @param[in] EventToSignal Identifies the EFI_EVENT to signal. EventToSignal - is permitted to request the queueing of its - notification function only at TPL_CALLBACK level. -**/ -STATIC -VOID -EFIAPI -AmdSevExitBoot ( - IN EFI_EVENT Event, - IN VOID *EventToSignal - ) -{ - // - // (1) The NotifyFunctions of all the events in - // EFI_EVENT_GROUP_EXIT_BOOT_SERVICES will have been queued before - // AmdSevExitBoot() is entered. - // - // (2) AmdSevExitBoot() is executing minimally at TPL_CALLBACK. - // - // (3) AmdSevExitBoot() has been queued in unspecified order relative to the - // NotifyFunctions of all the other events in - // EFI_EVENT_GROUP_EXIT_BOOT_SERVICES whose NotifyTpl is the same as - // Event's. - // - // Consequences: - // - // - If Event's NotifyTpl is TPL_CALLBACK, then some other NotifyFunctions - // queued at TPL_CALLBACK may be invoked after AmdSevExitBoot() returns. - // - // - If Event's NotifyTpl is TPL_NOTIFY, then some other NotifyFunctions - // queued at TPL_NOTIFY may be invoked after AmdSevExitBoot() returns; plus - // *all* NotifyFunctions queued at TPL_CALLBACK will be invoked strictly - // after all NotifyFunctions queued at TPL_NOTIFY, including - // AmdSevExitBoot(), have been invoked. - // - // - By signaling EventToSignal here, whose NotifyTpl is TPL_CALLBACK, we - // queue EventToSignal's NotifyFunction after the NotifyFunctions of *all* - // events in EFI_EVENT_GROUP_EXIT_BOOT_SERVICES. - // - DEBUG ((DEBUG_VERBOSE, "%a\n", __FUNCTION__)); - gBS->SignalEvent (EventToSignal); -} - -/** - Notification function that is queued after the notification functions of all - events in the EFI_EVENT_GROUP_EXIT_BOOT_SERVICES event group. The same memory - map restrictions apply. - - This function unmaps all currently existing IOMMU mappings. - - @param[in] Event Event whose notification function is being invoked. Event - is permitted to request the queueing of this function - only at TPL_CALLBACK task priority level. - - @param[in] Context Ignored. -**/ -STATIC -VOID -EFIAPI -AmdSevUnmapAllMappings ( - IN EFI_EVENT Event, - IN VOID *Context - ) -{ - LIST_ENTRY *Node; - LIST_ENTRY *NextNode; - MAP_INFO *MapInfo; - - DEBUG ((DEBUG_VERBOSE, "%a\n", __FUNCTION__)); - - // - // All drivers that had set up IOMMU mappings have halted their respective - // controllers by now; tear down the mappings. - // - for (Node = GetFirstNode (&mMapInfos); Node != &mMapInfos; Node = NextNode) { - NextNode = GetNextNode (&mMapInfos, Node); - MapInfo = CR (Node, MAP_INFO, Link, MAP_INFO_SIG); - IoMmuUnmapWorker ( - &mAmdSev, // This - MapInfo, // Mapping - TRUE // MemoryMapLocked - ); - } -} - -/** - Initialize Iommu Protocol. - -**/ -EFI_STATUS -EFIAPI -AmdSevInstallIoMmuProtocol ( - VOID - ) -{ - EFI_STATUS Status; - EFI_EVENT UnmapAllMappingsEvent; - EFI_EVENT ExitBootEvent; - EFI_HANDLE Handle; - - // - // Create the "late" event whose notification function will tear down all - // left-over IOMMU mappings. - // - Status = gBS->CreateEvent ( - EVT_NOTIFY_SIGNAL, // Type - TPL_CALLBACK, // NotifyTpl - AmdSevUnmapAllMappings, // NotifyFunction - NULL, // NotifyContext - &UnmapAllMappingsEvent // Event - ); - if (EFI_ERROR (Status)) { - return Status; - } - - // - // Create the event whose notification function will be queued by - // gBS->ExitBootServices() and will signal the event created above. - // - Status = gBS->CreateEvent ( - EVT_SIGNAL_EXIT_BOOT_SERVICES, // Type - TPL_CALLBACK, // NotifyTpl - AmdSevExitBoot, // NotifyFunction - UnmapAllMappingsEvent, // NotifyContext - &ExitBootEvent // Event - ); - if (EFI_ERROR (Status)) { - goto CloseUnmapAllMappingsEvent; - } - - Handle = NULL; - Status = gBS->InstallMultipleProtocolInterfaces ( - &Handle, - &gEdkiiIoMmuProtocolGuid, &mAmdSev, - NULL - ); - if (EFI_ERROR (Status)) { - goto CloseExitBootEvent; - } - - return EFI_SUCCESS; - -CloseExitBootEvent: - gBS->CloseEvent (ExitBootEvent); - -CloseUnmapAllMappingsEvent: - gBS->CloseEvent (UnmapAllMappingsEvent); - - return Status; -} +/** @file + + The protocol provides support to allocate, free, map and umap a DMA buffer + for bus master (e.g PciHostBridge). When Memory Encryption is enabled, the DMA operations + must be performed on unencrypted buffer hence we use a bounce buffer to map + the guest buffer into an unencrypted DMA buffer. + + Copyright (c) 2017, AMD Inc. All rights reserved.
+ Copyright (c) 2017, Intel Corporation. All rights reserved.
+ + SPDX-License-Identifier: BSD-2-Clause-Patent + +**/ + +#include "IoMmu.h" + +#define MAP_INFO_SIG SIGNATURE_64 ('M', 'A', 'P', '_', 'I', 'N', 'F', 'O') + +typedef struct { + UINT64 Signature; + LIST_ENTRY Link; + EDKII_IOMMU_OPERATION Operation; + UINTN NumberOfBytes; + UINTN NumberOfPages; + EFI_PHYSICAL_ADDRESS CryptedAddress; + EFI_PHYSICAL_ADDRESS PlainTextAddress; +} MAP_INFO; + +UINTN mMemEncryptType; +// +// List of the MAP_INFO structures that have been set up by IoMmuMap() and not +// yet torn down by IoMmuUnmap(). The list represents the full set of mappings +// currently in effect. +// +STATIC LIST_ENTRY mMapInfos = INITIALIZE_LIST_HEAD_VARIABLE (mMapInfos); + +#define COMMON_BUFFER_SIG SIGNATURE_64 ('C', 'M', 'N', 'B', 'U', 'F', 'F', 'R') + +// +// ASCII names for EDKII_IOMMU_OPERATION constants, for debug logging. +// +STATIC CONST CHAR8 * CONST +mBusMasterOperationName[EdkiiIoMmuOperationMaximum] = { + "Read", + "Write", + "CommonBuffer", + "Read64", + "Write64", + "CommonBuffer64" +}; + +// +// The following structure enables Map() and Unmap() to perform in-place +// decryption and encryption, respectively, for BusMasterCommonBuffer[64] +// operations, without dynamic memory allocation or release. +// +// Both COMMON_BUFFER_HEADER and COMMON_BUFFER_HEADER.StashBuffer are allocated +// by AllocateBuffer() and released by FreeBuffer(). +// +#pragma pack (1) +typedef struct { + UINT64 Signature; + + // + // Always allocated from EfiBootServicesData type memory, and always + // encrypted. + // + VOID *StashBuffer; + + // + // Followed by the actual common buffer, starting at the next page. + // +} COMMON_BUFFER_HEADER; +#pragma pack () + +/** + Provides the controller-specific addresses required to access system memory + from a DMA bus master. On an encrypted guest, the DMA operations must be performed on + shared buffer hence we allocate a bounce buffer to map the HostAddress to a + DeviceAddress. The Encryption attribute is removed from the DeviceAddress + buffer. + + @param This The protocol instance pointer. + @param Operation Indicates if the bus master is going to read or + write to system memory. + @param HostAddress The system memory address to map to the PCI + controller. + @param NumberOfBytes On input the number of bytes to map. On output + the number of bytes that were mapped. + @param DeviceAddress The resulting map address for the bus master + PCI controller to use to access the hosts + HostAddress. + @param Mapping A resulting value to pass to Unmap(). + + @retval EFI_SUCCESS The range was mapped for the returned + NumberOfBytes. + @retval EFI_UNSUPPORTED The HostAddress cannot be mapped as a common + buffer. + @retval EFI_INVALID_PARAMETER One or more parameters are invalid. + @retval EFI_OUT_OF_RESOURCES The request could not be completed due to a + lack of resources. + @retval EFI_DEVICE_ERROR The system hardware could not map the requested + address. + +**/ +EFI_STATUS +EFIAPI +IoMmuMap ( + IN EDKII_IOMMU_PROTOCOL *This, + IN EDKII_IOMMU_OPERATION Operation, + IN VOID *HostAddress, + IN OUT UINTN *NumberOfBytes, + OUT EFI_PHYSICAL_ADDRESS *DeviceAddress, + OUT VOID **Mapping + ) +{ + EFI_STATUS Status; + MAP_INFO *MapInfo; + EFI_ALLOCATE_TYPE AllocateType; + COMMON_BUFFER_HEADER *CommonBufferHeader; + VOID *DecryptionSource; + + DEBUG (( + DEBUG_VERBOSE, + "%a: Operation=%a Host=0x%p Bytes=0x%Lx\n", + __FUNCTION__, + ((Operation >= 0 && + Operation < ARRAY_SIZE (mBusMasterOperationName)) ? + mBusMasterOperationName[Operation] : + "Invalid"), + HostAddress, + (UINT64)((NumberOfBytes == NULL) ? 0 : *NumberOfBytes) + )); + + if (HostAddress == NULL || NumberOfBytes == NULL || DeviceAddress == NULL || + Mapping == NULL) { + return EFI_INVALID_PARAMETER; + } + + // + // Allocate a MAP_INFO structure to remember the mapping when Unmap() is + // called later. + // + MapInfo = AllocatePool (sizeof (MAP_INFO)); + if (MapInfo == NULL) { + Status = EFI_OUT_OF_RESOURCES; + goto Failed; + } + + // + // Initialize the MAP_INFO structure, except the PlainTextAddress field + // + ZeroMem (&MapInfo->Link, sizeof MapInfo->Link); + MapInfo->Signature = MAP_INFO_SIG; + MapInfo->Operation = Operation; + MapInfo->NumberOfBytes = *NumberOfBytes; + MapInfo->NumberOfPages = EFI_SIZE_TO_PAGES (MapInfo->NumberOfBytes); + MapInfo->CryptedAddress = (UINTN)HostAddress; + + // + // In the switch statement below, we point "MapInfo->PlainTextAddress" to the + // plaintext buffer, according to Operation. We also set "DecryptionSource". + // + MapInfo->PlainTextAddress = MAX_ADDRESS; + AllocateType = AllocateAnyPages; + DecryptionSource = (VOID *)(UINTN)MapInfo->CryptedAddress; + switch (Operation) { + // + // For BusMasterRead[64] and BusMasterWrite[64] operations, a bounce buffer + // is necessary regardless of whether the original (crypted) buffer crosses + // the 4GB limit or not -- we have to allocate a separate plaintext buffer. + // The only variable is whether the plaintext buffer should be under 4GB. + // + case EdkiiIoMmuOperationBusMasterRead: + case EdkiiIoMmuOperationBusMasterWrite: + MapInfo->PlainTextAddress = BASE_4GB - 1; + AllocateType = AllocateMaxAddress; + // + // fall through + // + case EdkiiIoMmuOperationBusMasterRead64: + case EdkiiIoMmuOperationBusMasterWrite64: + // + // Allocate the implicit plaintext bounce buffer. + // + Status = gBS->AllocatePages ( + AllocateType, + EfiBootServicesData, + MapInfo->NumberOfPages, + &MapInfo->PlainTextAddress + ); + if (EFI_ERROR (Status)) { + goto FreeMapInfo; + } + break; + + // + // For BusMasterCommonBuffer[64] operations, a to-be-plaintext buffer and a + // stash buffer (for in-place decryption) have been allocated already, with + // AllocateBuffer(). We only check whether the address of the to-be-plaintext + // buffer is low enough for the requested operation. + // + case EdkiiIoMmuOperationBusMasterCommonBuffer: + if ((MapInfo->CryptedAddress > BASE_4GB) || + (EFI_PAGES_TO_SIZE (MapInfo->NumberOfPages) > + BASE_4GB - MapInfo->CryptedAddress)) { + // + // CommonBuffer operations cannot be remapped. If the common buffer is + // above 4GB, then it is not possible to generate a mapping, so return an + // error. + // + Status = EFI_UNSUPPORTED; + goto FreeMapInfo; + } + // + // fall through + // + case EdkiiIoMmuOperationBusMasterCommonBuffer64: + // + // The buffer at MapInfo->CryptedAddress comes from AllocateBuffer(). + // + MapInfo->PlainTextAddress = MapInfo->CryptedAddress; + // + // Stash the crypted data. + // + CommonBufferHeader = (COMMON_BUFFER_HEADER *)( + (UINTN)MapInfo->CryptedAddress - EFI_PAGE_SIZE + ); + ASSERT (CommonBufferHeader->Signature == COMMON_BUFFER_SIG); + CopyMem ( + CommonBufferHeader->StashBuffer, + (VOID *)(UINTN)MapInfo->CryptedAddress, + MapInfo->NumberOfBytes + ); + // + // Point "DecryptionSource" to the stash buffer so that we decrypt + // it to the original location, after the switch statement. + // + DecryptionSource = CommonBufferHeader->StashBuffer; + break; + + default: + // + // Operation is invalid + // + Status = EFI_INVALID_PARAMETER; + goto FreeMapInfo; + } + + // + // Clear the memory encryption mask on the plaintext buffer. + // + if (mMemEncryptType == MEM_ENCRYPT_SEV_ENABLED) { + Status = MemEncryptSevClearPageEncMask ( + 0, + MapInfo->PlainTextAddress, + MapInfo->NumberOfPages, + TRUE + ); + } else if (mMemEncryptType == MEM_ENCRYPT_TDX_ENABLED) { + Status = MemEncryptTdxClearPageEncMask ( + 0, + MapInfo->PlainTextAddress, + MapInfo->NumberOfPages, + TRUE + ); + } + ASSERT_EFI_ERROR (Status); + if (EFI_ERROR (Status)) { + CpuDeadLoop (); + } + + // + // If this is a read operation from the Bus Master's point of view, + // then copy the contents of the real buffer into the mapped buffer + // so the Bus Master can read the contents of the real buffer. + // + // For BusMasterCommonBuffer[64] operations, the CopyMem() below will decrypt + // the original data (from the stash buffer) back to the original location. + // + if (Operation == EdkiiIoMmuOperationBusMasterRead || + Operation == EdkiiIoMmuOperationBusMasterRead64 || + Operation == EdkiiIoMmuOperationBusMasterCommonBuffer || + Operation == EdkiiIoMmuOperationBusMasterCommonBuffer64) { + CopyMem ( + (VOID *) (UINTN) MapInfo->PlainTextAddress, + DecryptionSource, + MapInfo->NumberOfBytes + ); + } + + // + // Track all MAP_INFO structures. + // + InsertHeadList (&mMapInfos, &MapInfo->Link); + // + // Populate output parameters. + // + *DeviceAddress = MapInfo->PlainTextAddress; + *Mapping = MapInfo; + + DEBUG (( + DEBUG_VERBOSE, + "%a: Mapping=0x%p Device(PlainText)=0x%Lx Crypted=0x%Lx Pages=0x%Lx\n", + __FUNCTION__, + MapInfo, + MapInfo->PlainTextAddress, + MapInfo->CryptedAddress, + (UINT64)MapInfo->NumberOfPages + )); + + return EFI_SUCCESS; + +FreeMapInfo: + FreePool (MapInfo); + +Failed: + *NumberOfBytes = 0; + return Status; +} + +/** + Completes the Map() operation and releases any corresponding resources. + + This is an internal worker function that only extends the Map() API with + the MemoryMapLocked parameter. + + @param This The protocol instance pointer. + @param Mapping The mapping value returned from Map(). + @param MemoryMapLocked The function is executing on the stack of + gBS->ExitBootServices(); changes to the UEFI + memory map are forbidden. + + @retval EFI_SUCCESS The range was unmapped. + @retval EFI_INVALID_PARAMETER Mapping is not a value that was returned by + Map(). + @retval EFI_DEVICE_ERROR The data was not committed to the target system + memory. +**/ +STATIC +EFI_STATUS +EFIAPI +IoMmuUnmapWorker ( + IN EDKII_IOMMU_PROTOCOL *This, + IN VOID *Mapping, + IN BOOLEAN MemoryMapLocked + ) +{ + MAP_INFO *MapInfo; + EFI_STATUS Status; + COMMON_BUFFER_HEADER *CommonBufferHeader; + VOID *EncryptionTarget; + + DEBUG (( + DEBUG_VERBOSE, + "%a: Mapping=0x%p MemoryMapLocked=%d\n", + __FUNCTION__, + Mapping, + MemoryMapLocked + )); + + if (Mapping == NULL) { + return EFI_INVALID_PARAMETER; + } + + MapInfo = (MAP_INFO *)Mapping; + + // + // set CommonBufferHeader to suppress incorrect compiler/analyzer warnings + // + CommonBufferHeader = NULL; + + // + // For BusMasterWrite[64] operations and BusMasterCommonBuffer[64] operations + // we have to encrypt the results, ultimately to the original place (i.e., + // "MapInfo->CryptedAddress"). + // + // For BusMasterCommonBuffer[64] operations however, this encryption has to + // land in-place, so divert the encryption to the stash buffer first. + // + EncryptionTarget = (VOID *)(UINTN)MapInfo->CryptedAddress; + + switch (MapInfo->Operation) { + case EdkiiIoMmuOperationBusMasterCommonBuffer: + case EdkiiIoMmuOperationBusMasterCommonBuffer64: + ASSERT (MapInfo->PlainTextAddress == MapInfo->CryptedAddress); + + CommonBufferHeader = (COMMON_BUFFER_HEADER *)( + (UINTN)MapInfo->PlainTextAddress - EFI_PAGE_SIZE + ); + ASSERT (CommonBufferHeader->Signature == COMMON_BUFFER_SIG); + EncryptionTarget = CommonBufferHeader->StashBuffer; + // + // fall through + // + + case EdkiiIoMmuOperationBusMasterWrite: + case EdkiiIoMmuOperationBusMasterWrite64: + CopyMem ( + EncryptionTarget, + (VOID *) (UINTN) MapInfo->PlainTextAddress, + MapInfo->NumberOfBytes + ); + break; + + default: + // + // nothing to encrypt after BusMasterRead[64] operations + // + break; + } + + // + // Restore the memory encryption mask on the area we used to hold the + // plaintext. + // + if (mMemEncryptType == MEM_ENCRYPT_SEV_ENABLED) { + Status = MemEncryptSevSetPageEncMask ( + 0, + MapInfo->PlainTextAddress, + MapInfo->NumberOfPages, + TRUE + ); + } else if (mMemEncryptType == MEM_ENCRYPT_TDX_ENABLED) { + Status = MemEncryptTdxSetPageEncMask ( + 0, + MapInfo->PlainTextAddress, + MapInfo->NumberOfPages, + TRUE + ); + } + ASSERT_EFI_ERROR (Status); + if (EFI_ERROR (Status)) { + CpuDeadLoop (); + } + + // + // For BusMasterCommonBuffer[64] operations, copy the stashed data to the + // original (now encrypted) location. + // + // For all other operations, fill the late bounce buffer (which existed as + // plaintext at some point) with zeros, and then release it (unless the UEFI + // memory map is locked). + // + if (MapInfo->Operation == EdkiiIoMmuOperationBusMasterCommonBuffer || + MapInfo->Operation == EdkiiIoMmuOperationBusMasterCommonBuffer64) { + CopyMem ( + (VOID *)(UINTN)MapInfo->CryptedAddress, + CommonBufferHeader->StashBuffer, + MapInfo->NumberOfBytes + ); + } else { + ZeroMem ( + (VOID *)(UINTN)MapInfo->PlainTextAddress, + EFI_PAGES_TO_SIZE (MapInfo->NumberOfPages) + ); + if (!MemoryMapLocked) { + gBS->FreePages (MapInfo->PlainTextAddress, MapInfo->NumberOfPages); + } + } + + // + // Forget the MAP_INFO structure, then free it (unless the UEFI memory map is + // locked). + // + RemoveEntryList (&MapInfo->Link); + if (!MemoryMapLocked) { + FreePool (MapInfo); + } + + return EFI_SUCCESS; +} + +/** + Completes the Map() operation and releases any corresponding resources. + + @param This The protocol instance pointer. + @param Mapping The mapping value returned from Map(). + + @retval EFI_SUCCESS The range was unmapped. + @retval EFI_INVALID_PARAMETER Mapping is not a value that was returned by + Map(). + @retval EFI_DEVICE_ERROR The data was not committed to the target system + memory. +**/ +EFI_STATUS +EFIAPI +IoMmuUnmap ( + IN EDKII_IOMMU_PROTOCOL *This, + IN VOID *Mapping + ) +{ + return IoMmuUnmapWorker ( + This, + Mapping, + FALSE // MemoryMapLocked + ); +} + +/** + Allocates pages that are suitable for an OperationBusMasterCommonBuffer or + OperationBusMasterCommonBuffer64 mapping. + + @param This The protocol instance pointer. + @param Type This parameter is not used and must be ignored. + @param MemoryType The type of memory to allocate, + EfiBootServicesData or EfiRuntimeServicesData. + @param Pages The number of pages to allocate. + @param HostAddress A pointer to store the base system memory + address of the allocated range. + @param Attributes The requested bit mask of attributes for the + allocated range. + + @retval EFI_SUCCESS The requested memory pages were allocated. + @retval EFI_UNSUPPORTED Attributes is unsupported. The only legal + attribute bits are MEMORY_WRITE_COMBINE and + MEMORY_CACHED. + @retval EFI_INVALID_PARAMETER One or more parameters are invalid. + @retval EFI_OUT_OF_RESOURCES The memory pages could not be allocated. + +**/ +EFI_STATUS +EFIAPI +IoMmuAllocateBuffer ( + IN EDKII_IOMMU_PROTOCOL *This, + IN EFI_ALLOCATE_TYPE Type, + IN EFI_MEMORY_TYPE MemoryType, + IN UINTN Pages, + IN OUT VOID **HostAddress, + IN UINT64 Attributes + ) +{ + EFI_STATUS Status; + EFI_PHYSICAL_ADDRESS PhysicalAddress; + VOID *StashBuffer; + UINTN CommonBufferPages; + COMMON_BUFFER_HEADER *CommonBufferHeader; + + DEBUG (( + DEBUG_VERBOSE, + "%a: MemoryType=%u Pages=0x%Lx Attributes=0x%Lx\n", + __FUNCTION__, + (UINT32)MemoryType, + (UINT64)Pages, + Attributes + )); + + // + // Validate Attributes + // + if ((Attributes & EDKII_IOMMU_ATTRIBUTE_INVALID_FOR_ALLOCATE_BUFFER) != 0) { + return EFI_UNSUPPORTED; + } + + // + // Check for invalid inputs + // + if (HostAddress == NULL) { + return EFI_INVALID_PARAMETER; + } + + // + // The only valid memory types are EfiBootServicesData and + // EfiRuntimeServicesData + // + if (MemoryType != EfiBootServicesData && + MemoryType != EfiRuntimeServicesData) { + return EFI_INVALID_PARAMETER; + } + + // + // We'll need a header page for the COMMON_BUFFER_HEADER structure. + // + if (Pages > MAX_UINTN - 1) { + return EFI_OUT_OF_RESOURCES; + } + CommonBufferPages = Pages + 1; + + // + // Allocate the stash in EfiBootServicesData type memory. + // + // Map() will temporarily save encrypted data in the stash for + // BusMasterCommonBuffer[64] operations, so the data can be decrypted to the + // original location. + // + // Unmap() will temporarily save plaintext data in the stash for + // BusMasterCommonBuffer[64] operations, so the data can be encrypted to the + // original location. + // + // StashBuffer always resides in encrypted memory. + // + StashBuffer = AllocatePages (Pages); + if (StashBuffer == NULL) { + return EFI_OUT_OF_RESOURCES; + } + + PhysicalAddress = (UINTN)-1; + if ((Attributes & EDKII_IOMMU_ATTRIBUTE_DUAL_ADDRESS_CYCLE) == 0) { + // + // Limit allocations to memory below 4GB + // + PhysicalAddress = SIZE_4GB - 1; + } + Status = gBS->AllocatePages ( + AllocateMaxAddress, + MemoryType, + CommonBufferPages, + &PhysicalAddress + ); + if (EFI_ERROR (Status)) { + goto FreeStashBuffer; + } + + CommonBufferHeader = (VOID *)(UINTN)PhysicalAddress; + PhysicalAddress += EFI_PAGE_SIZE; + + CommonBufferHeader->Signature = COMMON_BUFFER_SIG; + CommonBufferHeader->StashBuffer = StashBuffer; + + *HostAddress = (VOID *)(UINTN)PhysicalAddress; + + DEBUG (( + DEBUG_VERBOSE, + "%a: Host=0x%Lx Stash=0x%p\n", + __FUNCTION__, + PhysicalAddress, + StashBuffer + )); + return EFI_SUCCESS; + +FreeStashBuffer: + FreePages (StashBuffer, Pages); + return Status; +} + +/** + Frees memory that was allocated with AllocateBuffer(). + + @param This The protocol instance pointer. + @param Pages The number of pages to free. + @param HostAddress The base system memory address of the allocated + range. + + @retval EFI_SUCCESS The requested memory pages were freed. + @retval EFI_INVALID_PARAMETER The memory range specified by HostAddress and + Pages was not allocated with AllocateBuffer(). + +**/ +EFI_STATUS +EFIAPI +IoMmuFreeBuffer ( + IN EDKII_IOMMU_PROTOCOL *This, + IN UINTN Pages, + IN VOID *HostAddress + ) +{ + UINTN CommonBufferPages; + COMMON_BUFFER_HEADER *CommonBufferHeader; + + DEBUG (( + DEBUG_VERBOSE, + "%a: Host=0x%p Pages=0x%Lx\n", + __FUNCTION__, + HostAddress, + (UINT64)Pages + )); + + CommonBufferPages = Pages + 1; + CommonBufferHeader = (COMMON_BUFFER_HEADER *)( + (UINTN)HostAddress - EFI_PAGE_SIZE + ); + + // + // Check the signature. + // + ASSERT (CommonBufferHeader->Signature == COMMON_BUFFER_SIG); + if (CommonBufferHeader->Signature != COMMON_BUFFER_SIG) { + return EFI_INVALID_PARAMETER; + } + + // + // Free the stash buffer. This buffer was always encrypted, so no need to + // zero it. + // + FreePages (CommonBufferHeader->StashBuffer, Pages); + + // + // Release the common buffer itself. Unmap() has re-encrypted it in-place, so + // no need to zero it. + // + return gBS->FreePages ((UINTN)CommonBufferHeader, CommonBufferPages); +} + + +/** + Set IOMMU attribute for a system memory. + + If the IOMMU protocol exists, the system memory cannot be used + for DMA by default. + + When a device requests a DMA access for a system memory, + the device driver need use SetAttribute() to update the IOMMU + attribute to request DMA access (read and/or write). + + The DeviceHandle is used to identify which device submits the request. + The IOMMU implementation need translate the device path to an IOMMU device + ID, and set IOMMU hardware register accordingly. + 1) DeviceHandle can be a standard PCI device. + The memory for BusMasterRead need set EDKII_IOMMU_ACCESS_READ. + The memory for BusMasterWrite need set EDKII_IOMMU_ACCESS_WRITE. + The memory for BusMasterCommonBuffer need set + EDKII_IOMMU_ACCESS_READ|EDKII_IOMMU_ACCESS_WRITE. + After the memory is used, the memory need set 0 to keep it being + protected. + 2) DeviceHandle can be an ACPI device (ISA, I2C, SPI, etc). + The memory for DMA access need set EDKII_IOMMU_ACCESS_READ and/or + EDKII_IOMMU_ACCESS_WRITE. + + @param[in] This The protocol instance pointer. + @param[in] DeviceHandle The device who initiates the DMA access + request. + @param[in] Mapping The mapping value returned from Map(). + @param[in] IoMmuAccess The IOMMU access. + + @retval EFI_SUCCESS The IoMmuAccess is set for the memory range + specified by DeviceAddress and Length. + @retval EFI_INVALID_PARAMETER DeviceHandle is an invalid handle. + @retval EFI_INVALID_PARAMETER Mapping is not a value that was returned by + Map(). + @retval EFI_INVALID_PARAMETER IoMmuAccess specified an illegal combination + of access. + @retval EFI_UNSUPPORTED DeviceHandle is unknown by the IOMMU. + @retval EFI_UNSUPPORTED The bit mask of IoMmuAccess is not supported + by the IOMMU. + @retval EFI_UNSUPPORTED The IOMMU does not support the memory range + specified by Mapping. + @retval EFI_OUT_OF_RESOURCES There are not enough resources available to + modify the IOMMU access. + @retval EFI_DEVICE_ERROR The IOMMU device reported an error while + attempting the operation. + +**/ +EFI_STATUS +EFIAPI +IoMmuSetAttribute ( + IN EDKII_IOMMU_PROTOCOL *This, + IN EFI_HANDLE DeviceHandle, + IN VOID *Mapping, + IN UINT64 IoMmuAccess + ) +{ + return EFI_UNSUPPORTED; +} + +EDKII_IOMMU_PROTOCOL mIoMmu = { + EDKII_IOMMU_PROTOCOL_REVISION, + IoMmuSetAttribute, + IoMmuMap, + IoMmuUnmap, + IoMmuAllocateBuffer, + IoMmuFreeBuffer, +}; + +/** + Notification function that is queued when gBS->ExitBootServices() signals the + EFI_EVENT_GROUP_EXIT_BOOT_SERVICES event group. This function signals another + event, received as Context, and returns. + + Signaling an event in this context is safe. The UEFI spec allows + gBS->SignalEvent() to return EFI_SUCCESS only; EFI_OUT_OF_RESOURCES is not + listed, hence memory is not allocated. The edk2 implementation also does not + release memory (and we only have to care about the edk2 implementation + because EDKII_IOMMU_PROTOCOL is edk2-specific anyway). + + @param[in] Event Event whose notification function is being invoked. + Event is permitted to request the queueing of this + function at TPL_CALLBACK or TPL_NOTIFY task + priority level. + + @param[in] EventToSignal Identifies the EFI_EVENT to signal. EventToSignal + is permitted to request the queueing of its + notification function only at TPL_CALLBACK level. +**/ +STATIC +VOID +EFIAPI +IoMmuExitBoot ( + IN EFI_EVENT Event, + IN VOID *EventToSignal + ) +{ + // + // (1) The NotifyFunctions of all the events in + // EFI_EVENT_GROUP_EXIT_BOOT_SERVICES will have been queued before + // IoMmuExitBoot() is entered. + // + // (2) IoMmuExitBoot() is executing minimally at TPL_CALLBACK. + // + // (3) IoMmuExitBoot() has been queued in unspecified order relative to the + // NotifyFunctions of all the other events in + // EFI_EVENT_GROUP_EXIT_BOOT_SERVICES whose NotifyTpl is the same as + // Event's. + // + // Consequences: + // + // - If Event's NotifyTpl is TPL_CALLBACK, then some other NotifyFunctions + // queued at TPL_CALLBACK may be invoked after IoMmuExitBoot() returns. + // + // - If Event's NotifyTpl is TPL_NOTIFY, then some other NotifyFunctions + // queued at TPL_NOTIFY may be invoked after IoMmuExitBoot() returns; plus + // *all* NotifyFunctions queued at TPL_CALLBACK will be invoked strictly + // after all NotifyFunctions queued at TPL_NOTIFY, including + // IoMmuExitBoot(), have been invoked. + // + // - By signaling EventToSignal here, whose NotifyTpl is TPL_CALLBACK, we + // queue EventToSignal's NotifyFunction after the NotifyFunctions of *all* + // events in EFI_EVENT_GROUP_EXIT_BOOT_SERVICES. + // + DEBUG ((DEBUG_VERBOSE, "%a\n", __FUNCTION__)); + gBS->SignalEvent (EventToSignal); +} + +/** + Notification function that is queued after the notification functions of all + events in the EFI_EVENT_GROUP_EXIT_BOOT_SERVICES event group. The same memory + map restrictions apply. + + This function unmaps all currently existing IOMMU mappings. + + @param[in] Event Event whose notification function is being invoked. Event + is permitted to request the queueing of this function + only at TPL_CALLBACK task priority level. + + @param[in] Context Ignored. +**/ +STATIC +VOID +EFIAPI +IoMmuUnmapAllMappings ( + IN EFI_EVENT Event, + IN VOID *Context + ) +{ + LIST_ENTRY *Node; + LIST_ENTRY *NextNode; + MAP_INFO *MapInfo; + + DEBUG ((DEBUG_VERBOSE, "%a\n", __FUNCTION__)); + + // + // All drivers that had set up IOMMU mappings have halted their respective + // controllers by now; tear down the mappings. + // + for (Node = GetFirstNode (&mMapInfos); Node != &mMapInfos; Node = NextNode) { + NextNode = GetNextNode (&mMapInfos, Node); + MapInfo = CR (Node, MAP_INFO, Link, MAP_INFO_SIG); + IoMmuUnmapWorker ( + &mIoMmu, // This + MapInfo, // Mapping + TRUE // MemoryMapLocked + ); + } +} + +/** + Initialize Iommu Protocol. + +**/ +EFI_STATUS +EFIAPI +IoMmuInstallIoMmuProtocol ( + UINTN MemEncryptType + ) +{ + EFI_STATUS Status; + EFI_EVENT UnmapAllMappingsEvent; + EFI_EVENT ExitBootEvent; + EFI_HANDLE Handle; + + mMemEncryptType = MemEncryptType; + // + // Create the "late" event whose notification function will tear down all + // left-over IOMMU mappings. + // + Status = gBS->CreateEvent ( + EVT_NOTIFY_SIGNAL, // Type + TPL_CALLBACK, // NotifyTpl + IoMmuUnmapAllMappings, // NotifyFunction + NULL, // NotifyContext + &UnmapAllMappingsEvent // Event + ); + if (EFI_ERROR (Status)) { + return Status; + } + + // + // Create the event whose notification function will be queued by + // gBS->ExitBootServices() and will signal the event created above. + // + Status = gBS->CreateEvent ( + EVT_SIGNAL_EXIT_BOOT_SERVICES, // Type + TPL_CALLBACK, // NotifyTpl + IoMmuExitBoot, // NotifyFunction + UnmapAllMappingsEvent, // NotifyContext + &ExitBootEvent // Event + ); + if (EFI_ERROR (Status)) { + goto CloseUnmapAllMappingsEvent; + } + + Handle = NULL; + Status = gBS->InstallMultipleProtocolInterfaces ( + &Handle, + &gEdkiiIoMmuProtocolGuid, &mIoMmu, + NULL + ); + if (EFI_ERROR (Status)) { + goto CloseExitBootEvent; + } + + return EFI_SUCCESS; + +CloseExitBootEvent: + gBS->CloseEvent (ExitBootEvent); + +CloseUnmapAllMappingsEvent: + gBS->CloseEvent (UnmapAllMappingsEvent); + + return Status; +} diff --git a/OvmfPkg/IoMmuDxe/AmdSevIoMmu.h b/OvmfPkg/IoMmuDxe/IoMmu.h similarity index 78% rename from OvmfPkg/IoMmuDxe/AmdSevIoMmu.h rename to OvmfPkg/IoMmuDxe/IoMmu.h index 8244f28b57fd..2ef3b756f792 100644 --- a/OvmfPkg/IoMmuDxe/AmdSevIoMmu.h +++ b/OvmfPkg/IoMmuDxe/IoMmu.h @@ -1,38 +1,40 @@ -/** @file - - The protocol provides support to allocate, free, map and umap a DMA buffer - for bus master (e.g PciHostBridge). When SEV is enabled, the DMA operations - must be performed on unencrypted buffer hence protocol clear the encryption - bit from the DMA buffer. - - Copyright (c) 2017, Intel Corporation. All rights reserved.
- Copyright (c) 2017, AMD Inc. All rights reserved.
- (C) Copyright 2017 Hewlett Packard Enterprise Development LP
- SPDX-License-Identifier: BSD-2-Clause-Patent - -**/ - -#ifndef _AMD_SEV_IOMMU_H_ -#define _AMD_SEV_IOMMU_H_ - -#include - -#include -#include -#include -#include -#include -#include - -/** - Install IOMMU protocol to provide the DMA support for PciHostBridge and - MemEncryptSevLib. - -**/ -EFI_STATUS -EFIAPI -AmdSevInstallIoMmuProtocol ( - VOID - ); - -#endif +/** @file + + The protocol provides support to allocate, free, map and umap a DMA buffer + for bus master (e.g PciHostBridge). When memory encryption is enabled, the DMA operations + must be performed on unencrypted buffer hence protocol clear the encryption + bit from the DMA buffer. + + Copyright (c) 2017, Intel Corporation. All rights reserved.
+ Copyright (c) 2017, AMD Inc. All rights reserved.
+ (C) Copyright 2017 Hewlett Packard Enterprise Development LP
+ SPDX-License-Identifier: BSD-2-Clause-Patent + +**/ + +#ifndef _AMD_SEV_IOMMU_H_ +#define _AMD_SEV_IOMMU_H_ + +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +/** + Install IOMMU protocol to provide the DMA support for PciHostBridge and + MemEncryptLib. + +**/ +EFI_STATUS +EFIAPI +IoMmuInstallIoMmuProtocol ( + UINTN MemEncryptType + ); + +#endif diff --git a/OvmfPkg/IoMmuDxe/IoMmuDxe.c b/OvmfPkg/IoMmuDxe/IoMmuDxe.c index 13df8ba874c5..d511b2b82192 100644 --- a/OvmfPkg/IoMmuDxe/IoMmuDxe.c +++ b/OvmfPkg/IoMmuDxe/IoMmuDxe.c @@ -1,15 +1,16 @@ /** @file IoMmuDxe driver installs EDKII_IOMMU_PROTOCOL to provide the support for DMA - operations when SEV is enabled. + operations when memory encryption is enabled. + Copyright (c) 2020, Intel Corporation. All rights reserved.
Copyright (c) 2017, AMD Inc. All rights reserved.
SPDX-License-Identifier: BSD-2-Clause-Patent **/ -#include "AmdSevIoMmu.h" +#include "IoMmu.h" EFI_STATUS EFIAPI @@ -20,13 +21,21 @@ IoMmuDxeEntryPoint ( { EFI_STATUS Status; EFI_HANDLE Handle; + UINTN Type; // - // When SEV is enabled, install IoMmu protocol otherwise install the + // When memory encryption is enabled, install IoMmu protocol otherwise install the // placeholder protocol so that other dependent module can run. // + Type = MEM_ENCRYPT_NONE_ENABLED; if (MemEncryptSevIsEnabled ()) { - Status = AmdSevInstallIoMmuProtocol (); + Type = MEM_ENCRYPT_SEV_ENABLED; + } else if (MemEncryptTdxIsEnabled ()) { + Type = MEM_ENCRYPT_TDX_ENABLED; + } + + if (Type != MEM_ENCRYPT_NONE_ENABLED) { + Status = IoMmuInstallIoMmuProtocol (Type); } else { Handle = NULL; diff --git a/OvmfPkg/IoMmuDxe/IoMmuDxe.inf b/OvmfPkg/IoMmuDxe/IoMmuDxe.inf index 2ebd74e5558c..2aa8413cfe23 100644 --- a/OvmfPkg/IoMmuDxe/IoMmuDxe.inf +++ b/OvmfPkg/IoMmuDxe/IoMmuDxe.inf @@ -18,8 +18,8 @@ ENTRY_POINT = IoMmuDxeEntryPoint [Sources] - AmdSevIoMmu.c - AmdSevIoMmu.h + IoMmu.c + IoMmu.h IoMmuDxe.c [Packages] @@ -32,6 +32,7 @@ BaseMemoryLib DebugLib MemEncryptSevLib + MemEncryptTdxLib MemoryAllocationLib UefiBootServicesTableLib UefiDriverEntryPoint diff --git a/OvmfPkg/Library/BaseMemEncryptTdxLib/BaseMemEncryptTdxLib.inf b/OvmfPkg/Library/BaseMemEncryptTdxLib/BaseMemEncryptTdxLib.inf new file mode 100644 index 000000000000..41438394da0d --- /dev/null +++ b/OvmfPkg/Library/BaseMemEncryptTdxLib/BaseMemEncryptTdxLib.inf @@ -0,0 +1,46 @@ +## @file +# Library for Memory Encryption +# +# Copyright (c) 2020, Intel Corporation. All rights reserved.
+# Copyright (c) 2017 Advanced Micro Devices. All rights reserved.
+# +# SPDX-License-Identifier: BSD-2-Clause-Patent +# +# +## + +[Defines] + INF_VERSION = 1.25 + BASE_NAME = MemEncryptTdxLibNull + FILE_GUID = 7E6651B2-B775-4593-A410-FC05B8C61993 + MODULE_TYPE = BASE + VERSION_STRING = 1.0 + LIBRARY_CLASS = MemEncryptTdxLib|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 = X64 +# + +[Packages] + MdeModulePkg/MdeModulePkg.dec + MdePkg/MdePkg.dec + OvmfPkg/OvmfPkg.dec + UefiCpuPkg/UefiCpuPkg.dec + +[Sources] + VirtualMemory.h + MemoryEncryption.c + +[LibraryClasses] + BaseLib + CacheMaintenanceLib + CpuLib + DebugLib + MemoryAllocationLib + PcdLib + TdxLib + TdxProbeLib + diff --git a/OvmfPkg/Library/BaseMemEncryptTdxLib/BaseMemEncryptTdxLibNull.inf b/OvmfPkg/Library/BaseMemEncryptTdxLib/BaseMemEncryptTdxLibNull.inf new file mode 100644 index 000000000000..e13670cfeae6 --- /dev/null +++ b/OvmfPkg/Library/BaseMemEncryptTdxLib/BaseMemEncryptTdxLibNull.inf @@ -0,0 +1,36 @@ +## @file +# Library for Memory Encryption +# +# Copyright (c) 2020, Intel Corporation. All rights reserved.
+# Copyright (c) 2017 Advanced Micro Devices. All rights reserved.
+# +# SPDX-License-Identifier: BSD-2-Clause-Patent +# +# +## + +[Defines] + INF_VERSION = 1.25 + BASE_NAME = MemEncryptTdxLib + FILE_GUID = 3C69C4CA-DE46-44D7-8AA5-6EE51A4E3EA7 + MODULE_TYPE = BASE + VERSION_STRING = 1.0 + LIBRARY_CLASS = MemEncryptTdxLib|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 = X64 IA32 +# + +[Packages] + MdePkg/MdePkg.dec + OvmfPkg/OvmfPkg.dec + +[Sources] + BaseMemoryEncryptionNull.c + +[LibraryClasses] + BaseLib + diff --git a/OvmfPkg/Library/BaseMemEncryptTdxLib/BaseMemoryEncryptionNull.c b/OvmfPkg/Library/BaseMemEncryptTdxLib/BaseMemoryEncryptionNull.c new file mode 100644 index 000000000000..689ae39da555 --- /dev/null +++ b/OvmfPkg/Library/BaseMemEncryptTdxLib/BaseMemoryEncryptionNull.c @@ -0,0 +1,123 @@ +/** @file + + Virtual Memory Management Services to set or clear the memory encryption + + Copyright (c) 2006 - 2018, Intel Corporation. All rights reserved.
+ Copyright (c) 2017, AMD Incorporated. All rights reserved.
+ + SPDX-License-Identifier: BSD-2-Clause-Patent + + Code is derived from MdeModulePkg/Core/DxeIplPeim/X64/VirtualMemory.c + +**/ + +#include +#include +#include +#include + +/** + Returns boolean to indicate whether to indicate which, if any, memory encryption is enabled + + @param[in] Type Bitmask of encryption technologies to check is enabled + + @retval TRUE The encryption type(s) are enabled + @retval FALSE The encryption type(s) are not enabled +**/ +BOOLEAN +EFIAPI +MemEncryptTdxIsEnabled ( + VOID + ) +{ + return FALSE; +} + +/** + This function clears memory encryption bit for the memory region specified by + BaseAddress and NumPages from the current page table context. + + @param[in] Cr3BaseAddress Cr3 Base Address (if zero then use + current CR3) + @param[in] BaseAddress The physical address that is the start + address of a memory region. + @param[in] NumPages 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 +MemEncryptTdxClearPageEncMask ( + IN PHYSICAL_ADDRESS Cr3BaseAddress, + IN PHYSICAL_ADDRESS BaseAddress, + IN UINTN NumPages, + IN BOOLEAN Flush + ) +{ + return EFI_UNSUPPORTED; +} + +/** + This function sets memory encryption bit for the memory region specified by + BaseAddress and NumPages from the current page table context. + + @param[in] Cr3BaseAddress Cr3 Base Address (if zero then use + current CR3) + @param[in] BaseAddress The physical address that is the start + address of a memory region. + @param[in] NumPages The number of pages from start memory + region. + @param[in] Flush Flush the caches before setting 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 Setting the memory encryption attribute + is not supported +**/ +RETURN_STATUS +EFIAPI +MemEncryptTdxSetPageEncMask ( + IN PHYSICAL_ADDRESS Cr3BaseAddress, + IN PHYSICAL_ADDRESS BaseAddress, + IN UINTN NumPages, + IN BOOLEAN Flush + ) +{ + return EFI_UNSUPPORTED; +} + + +/** + Locate the page range that covers the initial (pre-SMBASE-relocation) SMRAM + Save State Map. + + @param[out] BaseAddress The base address of the lowest-address page that + covers the initial SMRAM Save State Map. + + @param[out] NumberOfPages The number of pages in the page range that covers + the initial SMRAM Save State Map. + + @retval RETURN_SUCCESS BaseAddress and NumberOfPages have been set on + output. + + @retval RETURN_UNSUPPORTED SMM is unavailable. +**/ +RETURN_STATUS +EFIAPI +MemEncryptLocateInitialSmramSaveStateMapPages ( + OUT UINTN *BaseAddress, + OUT UINTN *NumberOfPages + ) +{ + return EFI_UNSUPPORTED; +} + diff --git a/OvmfPkg/Library/BaseMemEncryptTdxLib/MemoryEncryption.c b/OvmfPkg/Library/BaseMemEncryptTdxLib/MemoryEncryption.c new file mode 100644 index 000000000000..b71e1c6843c9 --- /dev/null +++ b/OvmfPkg/Library/BaseMemEncryptTdxLib/MemoryEncryption.c @@ -0,0 +1,949 @@ +/** @file + + Virtual Memory Management Services to set or clear the memory encryption + + Copyright (c) 2006 - 2018, Intel Corporation. All rights reserved.
+ Copyright (c) 2017, AMD Incorporated. All rights reserved.
+ + SPDX-License-Identifier: BSD-2-Clause-Patent + + Code is derived from MdeModulePkg/Core/DxeIplPeim/X64/VirtualMemory.c + +**/ + +#include +#include +#include +#include +#include +#include +#include "VirtualMemory.h" +#include +#include +#include + +typedef enum { + SetSharedBit, + ClearSharedBit +} TDX_PAGETABLE_MODE; + +STATIC PAGE_TABLE_POOL *mPageTablePool = NULL; + +/** + Returns boolean to indicate whether to indicate which, if any, memory encryption is enabled + + @param[in] Type Bitmask of encryption technologies to check is enabled + + @retval TRUE The encryption type(s) are enabled + @retval FALSE The encryption type(s) are not enabled +**/ +BOOLEAN +EFIAPI +MemEncryptTdxIsEnabled ( + VOID + ) +{ + // + // If query any encrpytion type this library doesn't support, return FALSE + // + return ProbeTdGuest(); +} + +/** + Get the memory encryption mask + + @param[out] EncryptionMask contains the pte mask. + +**/ +STATIC +UINT64 +GetMemEncryptionAddressMask ( + VOID + ) +{ + return TdSharedPageMask(); +} + +/** + Initialize a buffer pool for page table use only. + + To reduce the potential split operation on page table, the pages reserved for + page table should be allocated in the times of PAGE_TABLE_POOL_UNIT_PAGES and + at the boundary of PAGE_TABLE_POOL_ALIGNMENT. So the page pool is always + initialized with number of pages greater than or equal to the given + PoolPages. + + Once the pages in the pool are used up, this method should be called again to + reserve at least another PAGE_TABLE_POOL_UNIT_PAGES. Usually this won't + happen often in practice. + + @param[in] PoolPages The least page number of the pool to be created. + + @retval TRUE The pool is initialized successfully. + @retval FALSE The memory is out of resource. +**/ +STATIC +BOOLEAN +InitializePageTablePool ( + IN UINTN PoolPages + ) +{ + VOID *Buffer; + + // + // Always reserve at least PAGE_TABLE_POOL_UNIT_PAGES, including one page for + // header. + // + PoolPages += 1; // Add one page for header. + PoolPages = ((PoolPages - 1) / PAGE_TABLE_POOL_UNIT_PAGES + 1) * + PAGE_TABLE_POOL_UNIT_PAGES; + Buffer = AllocateAlignedPages (PoolPages, PAGE_TABLE_POOL_ALIGNMENT); + if (Buffer == NULL) { + DEBUG ((DEBUG_ERROR, "ERROR: Out of aligned pages\r\n")); + return FALSE; + } + + // + // Link all pools into a list for easier track later. + // + if (mPageTablePool == NULL) { + mPageTablePool = Buffer; + mPageTablePool->NextPool = mPageTablePool; + } else { + ((PAGE_TABLE_POOL *)Buffer)->NextPool = mPageTablePool->NextPool; + mPageTablePool->NextPool = Buffer; + mPageTablePool = Buffer; + } + + // + // Reserve one page for pool header. + // + mPageTablePool->FreePages = PoolPages - 1; + mPageTablePool->Offset = EFI_PAGES_TO_SIZE (1); + + return TRUE; +} + +/** + This API provides a way to allocate memory for page table. + + This API can be called more than once to allocate memory for page tables. + + Allocates the number of 4KB pages and returns a pointer to the allocated + buffer. The buffer returned is aligned on a 4KB boundary. + + If Pages is 0, then NULL is returned. + If there is not enough memory remaining to satisfy the request, then NULL is + returned. + + @param Pages The number of 4 KB pages to allocate. + + @return A pointer to the allocated buffer or NULL if allocation fails. + +**/ +STATIC +VOID * +EFIAPI +AllocatePageTableMemory ( + IN UINTN Pages + ) +{ + VOID *Buffer; + + if (Pages == 0) { + return NULL; + } + + // + // Renew the pool if necessary. + // + if (mPageTablePool == NULL || + Pages > mPageTablePool->FreePages) { + if (!InitializePageTablePool (Pages)) { + return NULL; + } + } + + Buffer = (UINT8 *)mPageTablePool + mPageTablePool->Offset; + + mPageTablePool->Offset += EFI_PAGES_TO_SIZE (Pages); + mPageTablePool->FreePages -= Pages; + + DEBUG (( + DEBUG_VERBOSE, + "%a:%a: Buffer=0x%Lx Pages=%ld\n", + gEfiCallerBaseName, + __FUNCTION__, + Buffer, + Pages + )); + + return Buffer; +} + + +/** + 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, + IN UINT64 AddressEncMask + ) +{ + PHYSICAL_ADDRESS PhysicalAddress4K; + UINTN IndexOfPageTableEntries; + PAGE_TABLE_4K_ENTRY *PageTableEntry, *PageTableEntry1; + + PageTableEntry = AllocatePageTableMemory(1); + + PageTableEntry1 = PageTableEntry; + + ASSERT (PageTableEntry != NULL); + + 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); +} + +/** + Set one page of page table pool memory to be read-only. + + @param[in] PageTableBase Base address of page table (CR3). + @param[in] Address Start address of a page to be set as read-only. + @param[in] Level4Paging Level 4 paging flag. + +**/ +STATIC +VOID +SetPageTablePoolReadOnly ( + IN UINTN PageTableBase, + IN EFI_PHYSICAL_ADDRESS Address, + IN BOOLEAN Level4Paging + ) +{ + UINTN Index; + UINTN EntryIndex; + UINT64 AddressEncMask; + UINT64 ActiveAddressEncMask; + EFI_PHYSICAL_ADDRESS PhysicalAddress; + UINT64 *PageTable; + UINT64 *NewPageTable; + UINT64 PageAttr; + UINT64 LevelSize[5]; + UINT64 LevelMask[5]; + UINTN LevelShift[5]; + UINTN Level; + UINT64 PoolUnitSize; + + ASSERT (PageTableBase != 0); + + // + // Since the page table is always from page table pool, which is always + // located at the boundary of PcdPageTablePoolAlignment, we just need to + // set the whole pool unit to be read-only. + // + Address = Address & PAGE_TABLE_POOL_ALIGN_MASK; + + LevelShift[1] = PAGING_L1_ADDRESS_SHIFT; + LevelShift[2] = PAGING_L2_ADDRESS_SHIFT; + LevelShift[3] = PAGING_L3_ADDRESS_SHIFT; + LevelShift[4] = PAGING_L4_ADDRESS_SHIFT; + + LevelMask[1] = PAGING_4K_ADDRESS_MASK_64; + LevelMask[2] = PAGING_2M_ADDRESS_MASK_64; + LevelMask[3] = PAGING_1G_ADDRESS_MASK_64; + LevelMask[4] = PAGING_1G_ADDRESS_MASK_64; + + LevelSize[1] = SIZE_4KB; + LevelSize[2] = SIZE_2MB; + LevelSize[3] = SIZE_1GB; + LevelSize[4] = SIZE_512GB; + + AddressEncMask = GetMemEncryptionAddressMask() & + PAGING_1G_ADDRESS_MASK_64; + PageTable = (UINT64 *)(UINTN)PageTableBase; + PoolUnitSize = PAGE_TABLE_POOL_UNIT_SIZE; + + for (Level = (Level4Paging) ? 4 : 3; Level > 0; --Level) { + Index = ((UINTN)RShiftU64 (Address, LevelShift[Level])); + Index &= PAGING_PAE_INDEX_MASK; + + PageAttr = PageTable[Index]; + ActiveAddressEncMask = GetMemEncryptionAddressMask() & PageAttr; + + if ((PageAttr & IA32_PG_PS) == 0) { + // + // Go to next level of table. + // + PageTable = (UINT64 *)(UINTN)(PageAttr & ~AddressEncMask & + PAGING_4K_ADDRESS_MASK_64); + continue; + } + + if (PoolUnitSize >= LevelSize[Level]) { + // + // Clear R/W bit if current page granularity is not larger than pool unit + // size. + // + if ((PageAttr & IA32_PG_RW) != 0) { + while (PoolUnitSize > 0) { + // + // PAGE_TABLE_POOL_UNIT_SIZE and PAGE_TABLE_POOL_ALIGNMENT are fit in + // one page (2MB). Then we don't need to update attributes for pages + // crossing page directory. ASSERT below is for that purpose. + // + ASSERT (Index < EFI_PAGE_SIZE/sizeof (UINT64)); + + PageTable[Index] &= ~(UINT64)IA32_PG_RW; + PoolUnitSize -= LevelSize[Level]; + + ++Index; + } + } + + break; + + } else { + // + // The smaller granularity of page must be needed. + // + ASSERT (Level > 1); + + NewPageTable = AllocatePageTableMemory (1); + ASSERT (NewPageTable != NULL); + + PhysicalAddress = PageAttr & LevelMask[Level]; + for (EntryIndex = 0; + EntryIndex < EFI_PAGE_SIZE/sizeof (UINT64); + ++EntryIndex) { + NewPageTable[EntryIndex] = PhysicalAddress | ActiveAddressEncMask | + IA32_PG_P | IA32_PG_RW; + if (Level > 2) { + NewPageTable[EntryIndex] |= IA32_PG_PS; + } + PhysicalAddress += LevelSize[Level - 1]; + } + + PageTable[Index] = (UINT64)(UINTN)NewPageTable | ActiveAddressEncMask | + IA32_PG_P | IA32_PG_RW; + PageTable = NewPageTable; + } + } +} + +/** + Prevent the memory pages used for page table from been overwritten. + + @param[in] PageTableBase Base address of page table (CR3). + @param[in] Level4Paging Level 4 paging flag. + +**/ +STATIC +VOID +EnablePageTableProtection ( + IN UINTN PageTableBase, + IN BOOLEAN Level4Paging + ) +{ + PAGE_TABLE_POOL *HeadPool; + PAGE_TABLE_POOL *Pool; + UINT64 PoolSize; + EFI_PHYSICAL_ADDRESS Address; + + if (mPageTablePool == NULL) { + return; + } + + // + // SetPageTablePoolReadOnly might update mPageTablePool. It's safer to + // remember original one in advance. + // + HeadPool = mPageTablePool; + Pool = HeadPool; + do { + Address = (EFI_PHYSICAL_ADDRESS)(UINTN)Pool; + PoolSize = Pool->Offset + EFI_PAGES_TO_SIZE (Pool->FreePages); + + // + // The size of one pool must be multiple of PAGE_TABLE_POOL_UNIT_SIZE, + // which is one of page size of the processor (2MB by default). Let's apply + // the protection to them one by one. + // + while (PoolSize > 0) { + SetPageTablePoolReadOnly(PageTableBase, Address, Level4Paging); + Address += PAGE_TABLE_POOL_UNIT_SIZE; + PoolSize -= PAGE_TABLE_POOL_UNIT_SIZE; + } + + Pool = Pool->NextPool; + } while (Pool != HeadPool); + +} + + +/** + 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; + UINT64 ActiveAddressEncMask; + + PageDirectoryEntry = AllocatePageTableMemory(1); + + AddressEncMask = GetMemEncryptionAddressMask (); + ASSERT (PageDirectoryEntry != NULL); + + ActiveAddressEncMask = *PageEntry1G & AddressEncMask; + // + // Fill in 1G page entry. + // + *PageEntry1G = ((UINT64)(UINTN)PageDirectoryEntry | + IA32_PG_P | IA32_PG_RW | ActiveAddressEncMask); + + 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, + ActiveAddressEncMask + ); + } else { + // + // Fill in the Page Directory entries + // + PageDirectoryEntry->Uint64 = (UINT64) PhysicalAddress2M | ActiveAddressEncMask; + 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 +SetOrClearSharedBit( + IN OUT UINT64* PageTablePointer, + IN TDX_PAGETABLE_MODE Mode, + IN PHYSICAL_ADDRESS PhysicalAddress, + IN UINT64 Length + ) +{ + UINT64 AddressEncMask; + UINT64 Status; + + AddressEncMask = GetMemEncryptionAddressMask (); + + // + // Set or clear page table entry. Also, set shared bit in physical address, before calling MapGPA + // + if (Mode == SetSharedBit) { + *PageTablePointer |= AddressEncMask; + PhysicalAddress |= AddressEncMask; + } else { + *PageTablePointer &= ~AddressEncMask; + PhysicalAddress &= ~AddressEncMask; + } + + Status = TdVmCall(TDVMCALL_MAPGPA, PhysicalAddress, Length, 0, 0, NULL); + + // + // If changing shared to private, must accept-page again + // + if (Mode == ClearSharedBit) { + TdAcceptPages(PhysicalAddress, Length / EFI_PAGE_SIZE, EFI_PAGE_SIZE); + } + + DEBUG (( + DEBUG_VERBOSE, + "%a:%a: pte=0x%Lx AddressEncMask=0x%Lx Mode=0x%x MapGPA Status=0x%x\n", + gEfiCallerBaseName, + __FUNCTION__, + *PageTablePointer, + AddressEncMask, + Mode, Status)); +} + +/** + Check the WP status in CR0 register. This bit is used to lock or unlock write + access to pages marked as read-only. + + @retval TRUE Write protection is enabled. + @retval FALSE Write protection is disabled. +**/ +STATIC +BOOLEAN +IsReadOnlyPageWriteProtected ( + VOID + ) +{ + return ((AsmReadCr0 () & BIT16) != 0); +} + + +/** + Disable Write Protect on pages marked as read-only. +**/ +STATIC +VOID +DisableReadOnlyPageWriteProtect ( + VOID + ) +{ + AsmWriteCr0 (AsmReadCr0() & ~BIT16); +} + +/** + Enable Write Protect on pages marked as read-only. +**/ +VOID +EnableReadOnlyPageWriteProtect ( + VOID + ) +{ + AsmWriteCr0 (AsmReadCr0() | BIT16); +} + +/** + This function either sets or clears memory encryption 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 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] Cr3BaseAddress Cr3 Base Address (if zero then use + current CR3) + @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] CacheFlush 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 +RETURN_STATUS +EFIAPI +SetMemorySharedOrPrivate ( + IN PHYSICAL_ADDRESS Cr3BaseAddress, + IN PHYSICAL_ADDRESS PhysicalAddress, + IN UINTN Length, + IN TDX_PAGETABLE_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; + UINT64 ActiveEncMask; + BOOLEAN IsWpEnabled; + RETURN_STATUS Status; + IA32_CR4 Cr4; + BOOLEAN Page5LevelSupport; + + // + // Set PageMapLevel4Entry to suppress incorrect compiler/analyzer warnings. + // + PageMapLevel4Entry = NULL; + + DEBUG (( + DEBUG_VERBOSE, + "%a:%a: Cr3Base=0x%Lx Physical=0x%Lx Length=0x%Lx Mode=%a CacheFlush=%u\n", + gEfiCallerBaseName, + __FUNCTION__, + Cr3BaseAddress, + PhysicalAddress, + (UINT64)Length, + (Mode == SetSharedBit) ? "Shared" : "Private", + (UINT32)CacheFlush + )); + + // + // Check if we have a valid memory encryption mask + // + AddressEncMask = GetMemEncryptionAddressMask (); + + PgTableMask = AddressEncMask | EFI_PAGE_MASK; + + if (Length == 0) { + return RETURN_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); + } + + // + // Make sure that the page table is changeable. + // + IsWpEnabled = IsReadOnlyPageWriteProtected (); + if (IsWpEnabled) { + DisableReadOnlyPageWriteProtect (); + } + + // + // If Cr3BaseAddress is not specified then read the current CR3 + // + if (Cr3BaseAddress == 0) { + Cr3BaseAddress = AsmReadCr3(); + } + // + // CPU will already have LA57 enabled so just check CR4 + // + Cr4.UintN = AsmReadCr4 (); + + Page5LevelSupport = (Cr4.Bits.LA57 ? TRUE : FALSE); + // + // If 5-level pages, adjust Cr3BaseAddress to point to first 4-level page directory, + // we will only have 1 + // + if (Page5LevelSupport) { + Cr3BaseAddress = *(UINT64 *)Cr3BaseAddress & ~PgTableMask; + } + + Status = EFI_SUCCESS; + + while (Length) + { + PageMapLevel4Entry = (VOID*) (Cr3BaseAddress & ~PgTableMask); + PageMapLevel4Entry += PML4_OFFSET(PhysicalAddress); + if (!PageMapLevel4Entry->Bits.Present) { + DEBUG (( + DEBUG_ERROR, + "%a:%a: bad PML4 for Physical=0x%Lx\n", + gEfiCallerBaseName, + __FUNCTION__, + PhysicalAddress + )); + Status = RETURN_NO_MAPPING; + goto Done; + } + + PageDirectory1GEntry = (VOID *)( + (PageMapLevel4Entry->Bits.PageTableBaseAddress << + 12) & ~PgTableMask + ); + PageDirectory1GEntry += PDP_OFFSET(PhysicalAddress); + if (!PageDirectory1GEntry->Bits.Present) { + DEBUG (( + DEBUG_ERROR, + "%a:%a: bad PDPE for Physical=0x%Lx\n", + gEfiCallerBaseName, + __FUNCTION__, + PhysicalAddress + )); + Status = RETURN_NO_MAPPING; + goto Done; + } + + // + // 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) { + SetOrClearSharedBit(&PageDirectory1GEntry->Uint64, Mode, PhysicalAddress, BIT30); + DEBUG (( + DEBUG_VERBOSE, + "%a:%a: updated 1GB entry for Physical=0x%Lx\n", + gEfiCallerBaseName, + __FUNCTION__, + PhysicalAddress + )); + PhysicalAddress += BIT30; + Length -= BIT30; + } else { + // + // We must split the page + // + DEBUG (( + DEBUG_VERBOSE, + "%a:%a: splitting 1GB page for Physical=0x%Lx\n", + gEfiCallerBaseName, + __FUNCTION__, + PhysicalAddress + )); + 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_ERROR, + "%a:%a: bad PDE for Physical=0x%Lx\n", + gEfiCallerBaseName, + __FUNCTION__, + PhysicalAddress + )); + Status = RETURN_NO_MAPPING; + goto Done; + } + // + // 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) { + SetOrClearSharedBit (&PageDirectory2MEntry->Uint64, Mode, PhysicalAddress, BIT21); + PhysicalAddress += BIT21; + Length -= BIT21; + } else { + // + // We must split up this page into 4K pages + // + DEBUG (( + DEBUG_VERBOSE, + "%a:%a: splitting 2MB page for Physical=0x%Lx\n", + gEfiCallerBaseName, + __FUNCTION__, + PhysicalAddress + )); + + ActiveEncMask = PageDirectory2MEntry->Uint64 & AddressEncMask; + + Split2MPageTo4K ( + (UINT64)PageDirectory2MEntry->Bits.PageTableBaseAddress << 21, + (UINT64 *)PageDirectory2MEntry, + 0, + 0, + ActiveEncMask + ); + 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_ERROR, + "%a:%a: bad PTE for Physical=0x%Lx\n", + gEfiCallerBaseName, + __FUNCTION__, + PhysicalAddress + )); + Status = RETURN_NO_MAPPING; + goto Done; + } + SetOrClearSharedBit (&PageTableEntry->Uint64, Mode, PhysicalAddress, EFI_PAGE_SIZE); + PhysicalAddress += EFI_PAGE_SIZE; + Length -= EFI_PAGE_SIZE; + } + } + } + + // + // Protect the page table by marking the memory used for page table to be + // read-only. + // + if (IsWpEnabled) { + EnablePageTableProtection ((UINTN)PageMapLevel4Entry, TRUE); + } + + // + // Flush TLB + // + CpuFlushTlb(); + +Done: + // + // Restore page table write protection, if any. + // + if (IsWpEnabled) { + EnableReadOnlyPageWriteProtect (); + } + + return Status; +} + +/** + This function clears memory encryption bit for the memory region specified by + BaseAddress and NumPages from the current page table context. + + @param[in] Cr3BaseAddress Cr3 Base Address (if zero then use + current CR3) + @param[in] BaseAddress The physical address that is the start + address of a memory region. + @param[in] NumPages 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 +MemEncryptTdxClearPageEncMask ( + IN PHYSICAL_ADDRESS Cr3BaseAddress, + IN PHYSICAL_ADDRESS BaseAddress, + IN UINTN NumPages, + IN BOOLEAN Flush + ) +{ + return SetMemorySharedOrPrivate ( + Cr3BaseAddress, + BaseAddress, + EFI_PAGES_TO_SIZE (NumPages), + SetSharedBit, + Flush + ); +} + +/** + This function sets memory encryption bit for the memory region specified by + BaseAddress and NumPages from the current page table context. + + @param[in] Cr3BaseAddress Cr3 Base Address (if zero then use + current CR3) + @param[in] BaseAddress The physical address that is the start + address of a memory region. + @param[in] NumPages The number of pages from start memory + region. + @param[in] Flush Flush the caches before setting 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 Setting the memory encryption attribute + is not supported +**/ +RETURN_STATUS +EFIAPI +MemEncryptTdxSetPageEncMask ( + IN PHYSICAL_ADDRESS Cr3BaseAddress, + IN PHYSICAL_ADDRESS BaseAddress, + IN UINTN NumPages, + IN BOOLEAN Flush + ) +{ + return SetMemorySharedOrPrivate ( + Cr3BaseAddress, + BaseAddress, + EFI_PAGES_TO_SIZE (NumPages), + ClearSharedBit, + Flush + ); +} diff --git a/OvmfPkg/Library/BaseMemEncryptTdxLib/VirtualMemory.h b/OvmfPkg/Library/BaseMemEncryptTdxLib/VirtualMemory.h new file mode 100644 index 000000000000..eca4fe77987d --- /dev/null +++ b/OvmfPkg/Library/BaseMemEncryptTdxLib/VirtualMemory.h @@ -0,0 +1,181 @@ +/** @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.
+ + SPDX-License-Identifier: BSD-2-Clause-Patent + + 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 IA32_PG_PS BIT7 + +#define PAGING_PAE_INDEX_MASK 0x1FF + +#define PAGING_4K_ADDRESS_MASK_64 0x000FFFFFFFFFF000ull +#define PAGING_2M_ADDRESS_MASK_64 0x000FFFFFFFE00000ull +#define PAGING_1G_ADDRESS_MASK_64 0x000FFFFFC0000000ull + +#define PAGING_L1_ADDRESS_SHIFT 12 +#define PAGING_L2_ADDRESS_SHIFT 21 +#define PAGING_L3_ADDRESS_SHIFT 30 +#define PAGING_L4_ADDRESS_SHIFT 39 + +#define PAGING_PML4E_NUMBER 4 + +#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 + +#define PAGE_TABLE_POOL_ALIGNMENT BASE_2MB +#define PAGE_TABLE_POOL_UNIT_SIZE SIZE_2MB +#define PAGE_TABLE_POOL_UNIT_PAGES \ + EFI_SIZE_TO_PAGES (PAGE_TABLE_POOL_UNIT_SIZE) +#define PAGE_TABLE_POOL_ALIGN_MASK \ + (~(EFI_PHYSICAL_ADDRESS)(PAGE_TABLE_POOL_ALIGNMENT - 1)) + +typedef struct { + VOID *NextPool; + UINTN Offset; + UINTN FreePages; +} PAGE_TABLE_POOL; + +#endif diff --git a/OvmfPkg/Library/HashLibBaseCryptoRouterTdx/HashLibBaseCryptoRouter.c b/OvmfPkg/Library/HashLibBaseCryptoRouterTdx/HashLibBaseCryptoRouter.c new file mode 100644 index 000000000000..0cf2ae73eaad --- /dev/null +++ b/OvmfPkg/Library/HashLibBaseCryptoRouterTdx/HashLibBaseCryptoRouter.c @@ -0,0 +1,240 @@ +/** @file + This library is BaseCrypto router. + +Copyright (c) 2013 - 2021, Intel Corporation. All rights reserved.
+SPDX-License-Identifier: BSD-2-Clause-Patent + +**/ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "HashLibBaseCryptoRouterCommon.h" + +#define TDX_HASH_COUNT 1 +HASH_INTERFACE mHashInterface[TDX_HASH_COUNT] = {{{0}, NULL, NULL, NULL}}; + +UINTN mHashInterfaceCount = 0; +HASH_HANDLE mHashCtx[TDX_HASH_COUNT] = {0}; + +/** + Start hash sequence. + + @param HashHandle Hash handle. + + @retval EFI_SUCCESS Hash sequence start and HandleHandle returned. + @retval EFI_OUT_OF_RESOURCES No enough resource to start hash. +**/ +EFI_STATUS +EFIAPI +HashStart ( + OUT HASH_HANDLE *HashHandle + ) +{ + HASH_HANDLE *HashCtx; + + if (mHashInterfaceCount == 0) { + return EFI_UNSUPPORTED; + } + + HashCtx = mHashCtx; + mHashInterface[0].HashInit (&HashCtx[0]); + + *HashHandle = (HASH_HANDLE)HashCtx; + + return EFI_SUCCESS; +} + +/** + Update hash sequence data. + + @param HashHandle Hash handle. + @param DataToHash Data to be hashed. + @param DataToHashLen Data size. + + @retval EFI_SUCCESS Hash sequence updated. +**/ +EFI_STATUS +EFIAPI +HashUpdate ( + IN HASH_HANDLE HashHandle, + IN VOID *DataToHash, + IN UINTN DataToHashLen + ) +{ + HASH_HANDLE *HashCtx; + + if (mHashInterfaceCount == 0) { + return EFI_UNSUPPORTED; + } + + HashCtx = (HASH_HANDLE *)HashHandle; + mHashInterface[0].HashUpdate (HashCtx[0], DataToHash, DataToHashLen); + + return EFI_SUCCESS; +} + +/** + MRTD => PCR[0] + RTMR[0] => PCR[1,7] + RTMR[1] => PCR[2,3,4,5,6] + RTMR[2] => PCR[8~15] + RTMR[3] => NA + +**/ +UINT8 GetMappedRtmrIndex(UINT32 PCRIndex) +{ + UINT8 RtmrIndex; + + ASSERT (PCRIndex <= 16 && PCRIndex >= 0); + RtmrIndex = 0; + if (PCRIndex == 1 || PCRIndex == 7) { + RtmrIndex = 0; + } else if (PCRIndex >= 2 && PCRIndex <= 6) { + RtmrIndex = 1; + } else if (PCRIndex >= 8 && PCRIndex <= 15) { + RtmrIndex = 2; + } + + return RtmrIndex; +} + +/** + Hash sequence complete and extend to PCR. + + @param HashHandle Hash handle. + @param PcrIndex PCR to be extended. + @param DataToHash Data to be hashed. + @param DataToHashLen Data size. + @param DigestList Digest list. + + @retval EFI_SUCCESS Hash sequence complete and DigestList is returned. +**/ +EFI_STATUS +EFIAPI +HashCompleteAndExtend ( + IN HASH_HANDLE HashHandle, + IN TPMI_DH_PCR PcrIndex, + IN VOID *DataToHash, + IN UINTN DataToHashLen, + OUT TPML_DIGEST_VALUES *DigestList + ) +{ + TPML_DIGEST_VALUES Digest; + HASH_HANDLE *HashCtx; + EFI_STATUS Status; + + if (mHashInterfaceCount == 0) { + return EFI_UNSUPPORTED; + } + + HashCtx = (HASH_HANDLE *)HashHandle; + ZeroMem (DigestList, sizeof(*DigestList)); + + mHashInterface[0].HashUpdate (HashCtx[0], DataToHash, DataToHashLen); + mHashInterface[0].HashFinal (HashCtx[0], &Digest); + Tpm2SetHashToDigestList (DigestList, &Digest); + + ASSERT(DigestList->count == 1 && DigestList->digests[0].hashAlg == TPM_ALG_SHA384); + + Status = TdExtendRtmr ( + (UINT32*)DigestList->digests[0].digest.sha384, + SHA384_DIGEST_SIZE, + GetMappedRtmrIndex(PcrIndex) + ); + return Status; +} + +/** + Hash data and extend to PCR. + + @param PcrIndex PCR to be extended. + @param DataToHash Data to be hashed. + @param DataToHashLen Data size. + @param DigestList Digest list. + + @retval EFI_SUCCESS Hash data and DigestList is returned. +**/ +EFI_STATUS +EFIAPI +HashAndExtend ( + IN TPMI_DH_PCR PcrIndex, + IN VOID *DataToHash, + IN UINTN DataToHashLen, + OUT TPML_DIGEST_VALUES *DigestList + ) +{ + HASH_HANDLE HashHandle; + EFI_STATUS Status; + + if (mHashInterfaceCount == 0) { + return EFI_UNSUPPORTED; + } + + HashStart (&HashHandle); + HashUpdate (HashHandle, DataToHash, DataToHashLen); + Status = HashCompleteAndExtend (HashHandle, PcrIndex, NULL, 0, DigestList); + + DEBUG((DEBUG_INFO, "Td: HashAndExtend: %d, %p, 0x%x, %r\n", + PcrIndex, DataToHash, DataToHashLen, Status)); + + return Status; +} + +/** + This service register Hash. + + @param HashInterface Hash interface + + @retval EFI_SUCCESS This hash interface is registered successfully. + @retval EFI_UNSUPPORTED System does not support register this interface. + @retval EFI_ALREADY_STARTED System already register this interface. +**/ +EFI_STATUS +EFIAPI +RegisterHashInterfaceLib ( + IN HASH_INTERFACE *HashInterface + ) +{ + UINTN Index; + UINT32 HashMask; + + // + // Check allow + // + HashMask = Tpm2GetHashMaskFromAlgo (&HashInterface->HashGuid); + ASSERT (HashMask == HASH_ALG_SHA384); + + if (HashMask != HASH_ALG_SHA384) { + return EFI_UNSUPPORTED; + } + + DEBUG((DEBUG_INFO, "TD: Hash is registered. mask = 0x%x, guid = %g\n", HashMask, HashInterface->HashGuid)); + + if (mHashInterfaceCount >= sizeof(mHashInterface)/sizeof(mHashInterface[0])) { + return EFI_OUT_OF_RESOURCES; + } + + // + // Check duplication + // + for (Index = 0; Index < mHashInterfaceCount; Index++) { + if (CompareGuid (&mHashInterface[Index].HashGuid, &HashInterface->HashGuid)) { + DEBUG ((DEBUG_ERROR, "Hash Interface (%g) has been registered\n", &HashInterface->HashGuid)); + return EFI_ALREADY_STARTED; + } + } + + CopyMem (&mHashInterface[mHashInterfaceCount], HashInterface, sizeof(*HashInterface)); + mHashInterfaceCount ++; + + return EFI_SUCCESS; +} + diff --git a/OvmfPkg/Library/HashLibBaseCryptoRouterTdx/HashLibBaseCryptoRouter.inf b/OvmfPkg/Library/HashLibBaseCryptoRouterTdx/HashLibBaseCryptoRouter.inf new file mode 100644 index 000000000000..d8124b214b2b --- /dev/null +++ b/OvmfPkg/Library/HashLibBaseCryptoRouterTdx/HashLibBaseCryptoRouter.inf @@ -0,0 +1,45 @@ +## @file +# Provides hash service by registered hash handler in Td guest +# +# This library is BaseCrypto router. It will redirect hash request to each individual +# hash handler registered, such as SHA1, SHA256. Platform can use PcdTpm2HashMask to +# mask some hash engines. +# +# Copyright (c) 2020 - 2021, Intel Corporation. All rights reserved.
+# SPDX-License-Identifier: BSD-2-Clause-Patent +# +## + +[Defines] + INF_VERSION = 0x00010005 + BASE_NAME = HashLibBaseCryptoRouter + MODULE_UNI_FILE = HashLibBaseCryptoRouter.uni + FILE_GUID = 77F6EA3E-1ABA-4467-A447-926E8CEB2D13 + MODULE_TYPE = BASE + VERSION_STRING = 1.0 + LIBRARY_CLASS = HashLib + +# +# The following information is for reference only and not required by the build tools. +# +# VALID_ARCHITECTURES = X64 +# + +[Sources] + HashLibBaseCryptoRouterCommon.h + HashLibBaseCryptoRouterCommon.c + HashLibBaseCryptoRouter.c + +[Packages] + MdePkg/MdePkg.dec + SecurityPkg/SecurityPkg.dec + OvmfPkg/OvmfPkg.dec + +[LibraryClasses] + BaseLib + BaseMemoryLib + DebugLib + PcdLib + TdxProbeLib + TdxLib + diff --git a/OvmfPkg/Library/HashLibBaseCryptoRouterTdx/HashLibBaseCryptoRouter.uni b/OvmfPkg/Library/HashLibBaseCryptoRouterTdx/HashLibBaseCryptoRouter.uni new file mode 100644 index 000000000000..29fdd4255ca6 --- /dev/null +++ b/OvmfPkg/Library/HashLibBaseCryptoRouterTdx/HashLibBaseCryptoRouter.uni @@ -0,0 +1,18 @@ +// /** @file +// Provides hash service by registered hash handler +// +// This library is BaseCrypto router. It will redirect hash request to each individual +// hash handler registered, such as SHA1, SHA256. Platform can use PcdTpm2HashMask to +// mask some hash engines. +// +// Copyright (c) 2013 - 2016, Intel Corporation. All rights reserved.
+// +// SPDX-License-Identifier: BSD-2-Clause-Patent +// +// **/ + + +#string STR_MODULE_ABSTRACT #language en-US "Provides hash service by registered hash handler" + +#string STR_MODULE_DESCRIPTION #language en-US "This library is BaseCrypto router. It will redirect hash request to each individual hash handler registered, such as SHA1, SHA256. Platform can use PcdTpm2HashMask to mask some hash engines." + diff --git a/OvmfPkg/Library/HashLibBaseCryptoRouterTdx/HashLibBaseCryptoRouterCommon.c b/OvmfPkg/Library/HashLibBaseCryptoRouterTdx/HashLibBaseCryptoRouterCommon.c new file mode 100644 index 000000000000..6a58697d977a --- /dev/null +++ b/OvmfPkg/Library/HashLibBaseCryptoRouterTdx/HashLibBaseCryptoRouterCommon.c @@ -0,0 +1,67 @@ +/** @file + This is BaseCrypto router support function. + +Copyright (c) 2013 - 2016, Intel Corporation. All rights reserved.
+SPDX-License-Identifier: BSD-2-Clause-Patent + +**/ + +#include +#include +#include +#include +#include +#include +#include + +typedef struct { + EFI_GUID Guid; + UINT32 Mask; +} TPM2_HASH_MASK; + +TPM2_HASH_MASK mTpm2HashMask[] = { + {HASH_ALGORITHM_SHA384_GUID, HASH_ALG_SHA384}, +}; + +/** + The function get hash mask info from algorithm. + + @param HashGuid Hash Guid + + @return HashMask +**/ +UINT32 +EFIAPI +Tpm2GetHashMaskFromAlgo ( + IN EFI_GUID *HashGuid + ) +{ + UINTN Index; + for (Index = 0; Index < sizeof(mTpm2HashMask)/sizeof(mTpm2HashMask[0]); Index++) { + if (CompareGuid (HashGuid, &mTpm2HashMask[Index].Guid)) { + return mTpm2HashMask[Index].Mask; + } + } + return 0; +} + +/** + The function set digest to digest list. + + @param DigestList digest list + @param Digest digest data +**/ +VOID +EFIAPI +Tpm2SetHashToDigestList ( + IN OUT TPML_DIGEST_VALUES *DigestList, + IN TPML_DIGEST_VALUES *Digest + ) +{ + CopyMem ( + &DigestList->digests[DigestList->count], + &Digest->digests[0], + sizeof(Digest->digests[0]) + ); + DigestList->count ++; +} diff --git a/OvmfPkg/Library/HashLibBaseCryptoRouterTdx/HashLibBaseCryptoRouterCommon.h b/OvmfPkg/Library/HashLibBaseCryptoRouterTdx/HashLibBaseCryptoRouterCommon.h new file mode 100644 index 000000000000..6af53aa206a7 --- /dev/null +++ b/OvmfPkg/Library/HashLibBaseCryptoRouterTdx/HashLibBaseCryptoRouterCommon.h @@ -0,0 +1,38 @@ +/** @file + This is BaseCrypto router support function definition. + +Copyright (c) 2013 - 2016, Intel Corporation. All rights reserved.
+SPDX-License-Identifier: BSD-2-Clause-Patent + +**/ + +#ifndef _HASH_LIB_BASE_CRYPTO_ROUTER_COMMON_H_ +#define _HASH_LIB_BASE_CRYPTO_ROUTER_COMMON_H_ + +/** + The function get hash mask info from algorithm. + + @param HashGuid Hash Guid + + @return HashMask +**/ +UINT32 +EFIAPI +Tpm2GetHashMaskFromAlgo ( + IN EFI_GUID *HashGuid + ); + +/** + The function set digest to digest list. + + @param DigestList digest list + @param Digest digest data +**/ +VOID +EFIAPI +Tpm2SetHashToDigestList ( + IN OUT TPML_DIGEST_VALUES *DigestList, + IN TPML_DIGEST_VALUES *Digest + ); + +#endif diff --git a/OvmfPkg/Library/PrePiLibTdx/FwVol.c b/OvmfPkg/Library/PrePiLibTdx/FwVol.c new file mode 100644 index 000000000000..474d48306464 --- /dev/null +++ b/OvmfPkg/Library/PrePiLibTdx/FwVol.c @@ -0,0 +1,978 @@ +/** @file + Implementation of the 6 PEI Ffs (FV) APIs in library form. + + This code only knows about a FV if it has a EFI_HOB_TYPE_FV entry in the HOB list + + Copyright (c) 2008 - 2009, Apple Inc. All rights reserved.
+ + SPDX-License-Identifier: BSD-2-Clause-Patent + +**/ + +#include +#include + +#define GET_OCCUPIED_SIZE(ActualSize, Alignment) \ + (ActualSize) + (((Alignment) - ((ActualSize) & ((Alignment) - 1))) & ((Alignment) - 1)) + +#define MAX_FV_IMAGES 8 +/** + Returns the highest bit set of the State field + + @param ErasePolarity Erase Polarity as defined by EFI_FVB2_ERASE_POLARITY + in the Attributes field. + @param FfsHeader Pointer to FFS File Header + + + @retval the highest bit in the State field + +**/ +STATIC +EFI_FFS_FILE_STATE +GetFileState( + IN UINT8 ErasePolarity, + IN EFI_FFS_FILE_HEADER *FfsHeader + ) +{ + EFI_FFS_FILE_STATE FileState; + EFI_FFS_FILE_STATE HighestBit; + + FileState = FfsHeader->State; + + if (ErasePolarity != 0) { + FileState = (EFI_FFS_FILE_STATE)~FileState; + } + + HighestBit = 0x80; + while (HighestBit != 0 && (HighestBit & FileState) == 0) { + HighestBit >>= 1; + } + + return HighestBit; +} + + +/** + Calculates the checksum of the header of a file. + The header is a zero byte checksum, so zero means header is good + + @param FfsHeader Pointer to FFS File Header + + @retval Checksum of the header + +**/ +STATIC +UINT8 +CalculateHeaderChecksum ( + IN EFI_FFS_FILE_HEADER *FileHeader + ) +{ + UINT8 *Ptr; + UINTN Index; + UINT8 Sum; + + Sum = 0; + Ptr = (UINT8 *)FileHeader; + + for (Index = 0; Index < sizeof(EFI_FFS_FILE_HEADER) - 3; Index += 4) { + Sum = (UINT8)(Sum + Ptr[Index]); + Sum = (UINT8)(Sum + Ptr[Index+1]); + Sum = (UINT8)(Sum + Ptr[Index+2]); + Sum = (UINT8)(Sum + Ptr[Index+3]); + } + + for (; Index < sizeof(EFI_FFS_FILE_HEADER); Index++) { + Sum = (UINT8)(Sum + Ptr[Index]); + } + + // + // State field (since this indicates the different state of file). + // + Sum = (UINT8)(Sum - FileHeader->State); + // + // Checksum field of the file is not part of the header checksum. + // + Sum = (UINT8)(Sum - FileHeader->IntegrityCheck.Checksum.File); + + return Sum; +} + + +/** + Given a FileHandle return the VolumeHandle + + @param FileHandle File handle to look up + @param VolumeHandle Match for FileHandle + + @retval TRUE VolumeHandle is valid + +**/ +STATIC +BOOLEAN +EFIAPI +FileHandleToVolume ( + IN EFI_PEI_FILE_HANDLE FileHandle, + OUT EFI_PEI_FV_HANDLE *VolumeHandle + ) +{ + EFI_FIRMWARE_VOLUME_HEADER *FwVolHeader; + EFI_PEI_HOB_POINTERS Hob; + + Hob.Raw = GetHobList (); + if (Hob.Raw == NULL) { + return FALSE; + } + + do { + Hob.Raw = GetNextHob (EFI_HOB_TYPE_FV, Hob.Raw); + if (Hob.Raw != NULL) { + FwVolHeader = (EFI_FIRMWARE_VOLUME_HEADER *)(UINTN)(Hob.FirmwareVolume->BaseAddress); + if (((UINT64) (UINTN) FileHandle > (UINT64) (UINTN) FwVolHeader ) && \ + ((UINT64) (UINTN) FileHandle <= ((UINT64) (UINTN) FwVolHeader + FwVolHeader->FvLength - 1))) { + *VolumeHandle = (EFI_PEI_FV_HANDLE)FwVolHeader; + return TRUE; + } + + Hob.Raw = GetNextHob (EFI_HOB_TYPE_FV, GET_NEXT_HOB (Hob)); + } + } while (Hob.Raw != NULL); + + return FALSE; +} + + + +/** + Given the input file pointer, search for the next matching file in the + FFS volume as defined by SearchType. The search starts from FileHeader inside + the Firmware Volume defined by FwVolHeader. + + @param FileHandle File handle to look up + @param VolumeHandle Match for FileHandle + + +**/ +EFI_STATUS +FindFileEx ( + IN CONST EFI_PEI_FV_HANDLE FvHandle, + IN CONST EFI_GUID *FileName, OPTIONAL + IN EFI_FV_FILETYPE SearchType, + IN OUT EFI_PEI_FILE_HANDLE *FileHandle + ) +{ + EFI_FIRMWARE_VOLUME_HEADER *FwVolHeader; + EFI_FFS_FILE_HEADER **FileHeader; + EFI_FFS_FILE_HEADER *FfsFileHeader; + EFI_FIRMWARE_VOLUME_EXT_HEADER *FwVolExHeaderInfo; + UINT32 FileLength; + UINT32 FileOccupiedSize; + UINT32 FileOffset; + UINT64 FvLength; + UINT8 ErasePolarity; + UINT8 FileState; + + FwVolHeader = (EFI_FIRMWARE_VOLUME_HEADER *)FvHandle; + FileHeader = (EFI_FFS_FILE_HEADER **)FileHandle; + + FvLength = FwVolHeader->FvLength; + if (FwVolHeader->Attributes & EFI_FVB2_ERASE_POLARITY) { + ErasePolarity = 1; + } else { + ErasePolarity = 0; + } + + // + // If FileHeader is not specified (NULL) or FileName is not NULL, + // start with the first file in the firmware volume. Otherwise, + // start from the FileHeader. + // + if ((*FileHeader == NULL) || (FileName != NULL)) { + FfsFileHeader = (EFI_FFS_FILE_HEADER *)((UINT8 *)FwVolHeader + FwVolHeader->HeaderLength); + if (FwVolHeader->ExtHeaderOffset != 0) { + FwVolExHeaderInfo = (EFI_FIRMWARE_VOLUME_EXT_HEADER *)(((UINT8 *)FwVolHeader) + FwVolHeader->ExtHeaderOffset); + FfsFileHeader = (EFI_FFS_FILE_HEADER *)(((UINT8 *)FwVolExHeaderInfo) + FwVolExHeaderInfo->ExtHeaderSize); + } + } else { + // + // Length is 24 bits wide so mask upper 8 bits + // FileLength is adjusted to FileOccupiedSize as it is 8 byte aligned. + // + FileLength = *(UINT32 *)(*FileHeader)->Size & 0x00FFFFFF; + FileOccupiedSize = GET_OCCUPIED_SIZE (FileLength, 8); + FfsFileHeader = (EFI_FFS_FILE_HEADER *)((UINT8 *)*FileHeader + FileOccupiedSize); + } + + // FFS files begin with a header that is aligned on an 8-byte boundary + FfsFileHeader = ALIGN_POINTER (FfsFileHeader, 8); + + FileOffset = (UINT32) ((UINT8 *)FfsFileHeader - (UINT8 *)FwVolHeader); + ASSERT (FileOffset <= 0xFFFFFFFF); + + while (FileOffset < (FvLength - sizeof (EFI_FFS_FILE_HEADER))) { + // + // Get FileState which is the highest bit of the State + // + FileState = GetFileState (ErasePolarity, FfsFileHeader); + + switch (FileState) { + + case EFI_FILE_HEADER_INVALID: + FileOffset += sizeof(EFI_FFS_FILE_HEADER); + FfsFileHeader = (EFI_FFS_FILE_HEADER *)((UINT8 *)FfsFileHeader + sizeof(EFI_FFS_FILE_HEADER)); + break; + + case EFI_FILE_DATA_VALID: + case EFI_FILE_MARKED_FOR_UPDATE: + if (CalculateHeaderChecksum (FfsFileHeader) != 0) { + ASSERT (FALSE); + *FileHeader = NULL; + return EFI_NOT_FOUND; + } + + FileLength = *(UINT32 *)(FfsFileHeader->Size) & 0x00FFFFFF; + FileOccupiedSize = GET_OCCUPIED_SIZE(FileLength, 8); + + if (FileName != NULL) { + if (CompareGuid (&FfsFileHeader->Name, (EFI_GUID*)FileName)) { + *FileHeader = FfsFileHeader; + return EFI_SUCCESS; + } + } else if (((SearchType == FfsFileHeader->Type) || (SearchType == EFI_FV_FILETYPE_ALL)) && + (FfsFileHeader->Type != EFI_FV_FILETYPE_FFS_PAD)) { + *FileHeader = FfsFileHeader; + return EFI_SUCCESS; + } + + FileOffset += FileOccupiedSize; + FfsFileHeader = (EFI_FFS_FILE_HEADER *)((UINT8 *)FfsFileHeader + FileOccupiedSize); + break; + + case EFI_FILE_DELETED: + FileLength = *(UINT32 *)(FfsFileHeader->Size) & 0x00FFFFFF; + FileOccupiedSize = GET_OCCUPIED_SIZE(FileLength, 8); + FileOffset += FileOccupiedSize; + FfsFileHeader = (EFI_FFS_FILE_HEADER *)((UINT8 *)FfsFileHeader + FileOccupiedSize); + break; + + default: + *FileHeader = NULL; + return EFI_NOT_FOUND; + } + } + + + *FileHeader = NULL; + return EFI_NOT_FOUND; +} + + +/** + Go through the file to search SectionType section, + when meeting an encapsuled section. + + @param SectionType - Filter to find only section of this type. + @param Section - From where to search. + @param SectionSize - The file size to search. + @param OutputBuffer - Pointer to the section to search. + + @retval EFI_SUCCESS +**/ +EFI_STATUS +FfsProcessSection ( + IN EFI_SECTION_TYPE SectionType, + IN EFI_COMMON_SECTION_HEADER *Section, + IN UINTN SectionSize, + IN OUT UINT32 *SectionDatas, + IN OUT UINT32 *SectionCnt + ) +{ + EFI_STATUS Status; + UINT32 SectionLength; + UINT32 ParsedLength; + EFI_COMPRESSION_SECTION *CompressionSection; + EFI_COMPRESSION_SECTION2 *CompressionSection2; + UINT32 DstBufferSize; + VOID *ScratchBuffer; + UINT32 ScratchBufferSize; + VOID *DstBuffer; + CHAR8 *CompressedData; + UINT32 CompressedDataLength; + UINT16 SectionAttribute; + UINT32 AuthenticationStatus; + UINT32 Cnt; + UINT32 MaxCnt; + + Cnt = 0; + MaxCnt = *SectionCnt; + ParsedLength = 0; + Status = EFI_NOT_FOUND; + + while (ParsedLength < SectionSize && Cnt < MaxCnt) { + if (IS_SECTION2 (Section)) { + ASSERT (SECTION2_SIZE (Section) > 0x00FFFFFF); + } + + if (Section->Type == SectionType) { + + if (IS_SECTION2 (Section)) { + *(SectionDatas + Cnt++) = (UINT32)(UINTN)(VOID *)((UINT8 *) Section + sizeof (EFI_COMMON_SECTION_HEADER2)); + } else { + *(SectionDatas + Cnt++) = (UINT32)(UINTN)(VOID *)((UINT8 *) Section + sizeof (EFI_COMMON_SECTION_HEADER)); + } + DEBUG((DEBUG_INFO, "Find Section with type(0x%x) size(0x%x)\n", SectionType, *(UINT32*)(Section->Size) & 0x00ffffff)); + + } else if ((Section->Type == EFI_SECTION_COMPRESSION) || (Section->Type == EFI_SECTION_GUID_DEFINED)) { + + if (Section->Type == EFI_SECTION_COMPRESSION) { + if (IS_SECTION2 (Section)) { + CompressionSection2 = (EFI_COMPRESSION_SECTION2 *) Section; + SectionLength = SECTION2_SIZE (Section); + + if (CompressionSection2->CompressionType != EFI_STANDARD_COMPRESSION) { + return EFI_UNSUPPORTED; + } + + CompressedData = (CHAR8 *) ((EFI_COMPRESSION_SECTION2 *) Section + 1); + CompressedDataLength = (UINT32) SectionLength - sizeof (EFI_COMPRESSION_SECTION2); + } else { + CompressionSection = (EFI_COMPRESSION_SECTION *) Section; + SectionLength = SECTION_SIZE (Section); + + if (CompressionSection->CompressionType != EFI_STANDARD_COMPRESSION) { + return EFI_UNSUPPORTED; + } + + CompressedData = (CHAR8 *) ((EFI_COMPRESSION_SECTION *) Section + 1); + CompressedDataLength = (UINT32) SectionLength - sizeof (EFI_COMPRESSION_SECTION); + } + + Status = UefiDecompressGetInfo ( + CompressedData, + CompressedDataLength, + &DstBufferSize, + &ScratchBufferSize + ); + } else if (Section->Type == EFI_SECTION_GUID_DEFINED) { + Status = ExtractGuidedSectionGetInfo ( + Section, + &DstBufferSize, + &ScratchBufferSize, + &SectionAttribute + ); + } + + if (EFI_ERROR (Status)) { + // + // GetInfo failed + // + DEBUG ((EFI_D_ERROR, "Decompress GetInfo Failed - %r\n", Status)); + return EFI_NOT_FOUND; + } + // + // Allocate scratch buffer + // + ScratchBuffer = (VOID *)(UINTN)AllocatePages (EFI_SIZE_TO_PAGES (ScratchBufferSize)); + if (ScratchBuffer == NULL) { + return EFI_OUT_OF_RESOURCES; + } + // + // Allocate destination buffer, extra one page for adjustment + // + DstBuffer = (VOID *)(UINTN)AllocatePages (EFI_SIZE_TO_PAGES (DstBufferSize) + 1); + if (DstBuffer == NULL) { + return EFI_OUT_OF_RESOURCES; + } + // + // DstBuffer still is one section. Adjust DstBuffer offset, skip EFI section header + // to make section data at page alignment. + // + if (IS_SECTION2 (Section)) + DstBuffer = (UINT8 *)DstBuffer + EFI_PAGE_SIZE - sizeof (EFI_COMMON_SECTION_HEADER2); + else + DstBuffer = (UINT8 *)DstBuffer + EFI_PAGE_SIZE - sizeof (EFI_COMMON_SECTION_HEADER); + // + // Call decompress function + // + if (Section->Type == EFI_SECTION_COMPRESSION) { + if (IS_SECTION2 (Section)) { + CompressedData = (CHAR8 *) ((EFI_COMPRESSION_SECTION2 *) Section + 1); + } + else { + CompressedData = (CHAR8 *) ((EFI_COMPRESSION_SECTION *) Section + 1); + } + + Status = UefiDecompress ( + CompressedData, + DstBuffer, + ScratchBuffer + ); + } else if (Section->Type == EFI_SECTION_GUID_DEFINED) { + Status = ExtractGuidedSectionDecode ( + Section, + &DstBuffer, + ScratchBuffer, + &AuthenticationStatus + ); + } + + if (EFI_ERROR (Status)) { + // + // Decompress failed + // + DEBUG ((EFI_D_ERROR, "Decompress Failed - %r\n", Status)); + return EFI_NOT_FOUND; + } else { + DEBUG((DEBUG_INFO, "Decompress success\n")); + return FfsProcessSection ( + SectionType, + DstBuffer, + DstBufferSize, + SectionDatas, + SectionCnt + ); + } + } + + if (IS_SECTION2 (Section)) { + SectionLength = SECTION2_SIZE (Section); + } else { + SectionLength = SECTION_SIZE (Section); + } + // + // SectionLength is adjusted it is 4 byte aligned. + // Go to the next section + // + SectionLength = GET_OCCUPIED_SIZE (SectionLength, 4); + ASSERT (SectionLength != 0); + ParsedLength += SectionLength; + Section = (EFI_COMMON_SECTION_HEADER *)((UINT8 *)Section + SectionLength); + } + + *SectionCnt = Cnt; + + return Cnt == 0 ? EFI_NOT_FOUND : EFI_SUCCESS; +} + + + +/** + This service enables discovery sections of a given type within a valid FFS file. + + @param SearchType The value of the section type to find. + @param FfsFileHeader A pointer to the file header that contains the set of sections to + be searched. + @param SectionData A pointer to the discovered section, if successful. + + @retval EFI_SUCCESS The section was found. + @retval EFI_NOT_FOUND The section was not found. + +**/ +EFI_STATUS +EFIAPI +FfsFindSectionData ( + IN EFI_SECTION_TYPE SectionType, + IN EFI_PEI_FILE_HANDLE FileHandle, + OUT VOID **SectionData + ) +{ + EFI_FFS_FILE_HEADER *FfsFileHeader; + UINT32 FileSize; + EFI_COMMON_SECTION_HEADER *Section; + EFI_STATUS Status; + UINT32 SectionDatas[1]; + UINT32 MaxSectionCnt; + + FfsFileHeader = (EFI_FFS_FILE_HEADER *)(FileHandle); + MaxSectionCnt = 1; + + // + // Size is 24 bits wide so mask upper 8 bits. + // Does not include FfsFileHeader header size + // FileSize is adjusted to FileOccupiedSize as it is 8 byte aligned. + // + Section = (EFI_COMMON_SECTION_HEADER *)(FfsFileHeader + 1); + FileSize = *(UINT32 *)(FfsFileHeader->Size) & 0x00FFFFFF; + FileSize -= sizeof (EFI_FFS_FILE_HEADER); + + Status = FfsProcessSection ( + SectionType, + Section, + FileSize, + SectionDatas, + &MaxSectionCnt + ); + + if(Status == EFI_SUCCESS){ + ASSERT(MaxSectionCnt == 1); + *SectionData = (VOID *)(UINTN)SectionDatas[0]; + } + + return Status; +} + +EFI_STATUS +EFIAPI +FfsFindSectionDataEx ( + IN EFI_SECTION_TYPE SectionType, + IN EFI_PEI_FILE_HANDLE FileHandle, + OUT UINT32 *SectionDatas, + OUT UINT32 *SectionCnt + ) +{ + EFI_FFS_FILE_HEADER *FfsFileHeader; + UINT32 FileSize; + EFI_COMMON_SECTION_HEADER *Section; + + FfsFileHeader = (EFI_FFS_FILE_HEADER *)(FileHandle); + + // + // Size is 24 bits wide so mask upper 8 bits. + // Does not include FfsFileHeader header size + // FileSize is adjusted to FileOccupiedSize as it is 8 byte aligned. + // + Section = (EFI_COMMON_SECTION_HEADER *)(FfsFileHeader + 1); + FileSize = *(UINT32 *)(FfsFileHeader->Size) & 0x00FFFFFF; + FileSize -= sizeof (EFI_FFS_FILE_HEADER); + + return FfsProcessSection ( + SectionType, + Section, + FileSize, + SectionDatas, + SectionCnt + ); +} + + +/** + This service enables discovery of additional firmware files. + + @param SearchType A filter to find files only of this type. + @param FwVolHeader Pointer to the firmware volume header of the volume to search. + This parameter must point to a valid FFS volume. + @param FileHeader Pointer to the current file from which to begin searching. + + @retval EFI_SUCCESS The file was found. + @retval EFI_NOT_FOUND The file was not found. + @retval EFI_NOT_FOUND The header checksum was not zero. + +**/ +EFI_STATUS +EFIAPI +FfsFindNextFile ( + IN UINT8 SearchType, + IN EFI_PEI_FV_HANDLE VolumeHandle, + IN OUT EFI_PEI_FILE_HANDLE *FileHandle + ) +{ + return FindFileEx (VolumeHandle, NULL, SearchType, FileHandle); +} + + +/** + This service enables discovery of additional firmware volumes. + + @param Instance This instance of the firmware volume to find. The value 0 is the + Boot Firmware Volume (BFV). + @param FwVolHeader Pointer to the firmware volume header of the volume to return. + + @retval EFI_SUCCESS The volume was found. + @retval EFI_NOT_FOUND The volume was not found. + +**/ +EFI_STATUS +EFIAPI +FfsFindNextVolume ( + IN UINTN Instance, + IN OUT EFI_PEI_FV_HANDLE *VolumeHandle + ) +{ + EFI_PEI_HOB_POINTERS Hob; + + + Hob.Raw = GetHobList (); + if (Hob.Raw == NULL) { + return EFI_NOT_FOUND; + } + + do { + Hob.Raw = GetNextHob (EFI_HOB_TYPE_FV, Hob.Raw); + if (Hob.Raw != NULL) { + if (Instance-- == 0) { + *VolumeHandle = (EFI_PEI_FV_HANDLE)(UINTN)(Hob.FirmwareVolume->BaseAddress); + return EFI_SUCCESS; + } + + Hob.Raw = GetNextHob (EFI_HOB_TYPE_FV, GET_NEXT_HOB (Hob)); + } + } while (Hob.Raw != NULL); + + return EFI_NOT_FOUND; + +} + + +/** + Find a file in the volume by name + + @param FileName A pointer to the name of the file to + find within the firmware volume. + + @param VolumeHandle The firmware volume to search FileHandle + Upon exit, points to the found file's + handle or NULL if it could not be found. + + @retval EFI_SUCCESS File was found. + + @retval EFI_NOT_FOUND File was not found. + + @retval EFI_INVALID_PARAMETER VolumeHandle or FileHandle or + FileName was NULL. + +**/ +EFI_STATUS +EFIAPI +FfsFindFileByName ( + IN CONST EFI_GUID *FileName, + IN EFI_PEI_FV_HANDLE VolumeHandle, + OUT EFI_PEI_FILE_HANDLE *FileHandle + ) +{ + EFI_STATUS Status; + if ((VolumeHandle == NULL) || (FileName == NULL) || (FileHandle == NULL)) { + return EFI_INVALID_PARAMETER; + } + Status = FindFileEx (VolumeHandle, FileName, 0, FileHandle); + if (Status == EFI_NOT_FOUND) { + *FileHandle = NULL; + } + return Status; +} + + + + +/** + Get information about the file by name. + + @param FileHandle Handle of the file. + + @param FileInfo Upon exit, points to the file's + information. + + @retval EFI_SUCCESS File information returned. + + @retval EFI_INVALID_PARAMETER If FileHandle does not + represent a valid file. + + @retval EFI_INVALID_PARAMETER If FileInfo is NULL. + +**/ +EFI_STATUS +EFIAPI +FfsGetFileInfo ( + IN EFI_PEI_FILE_HANDLE FileHandle, + OUT EFI_FV_FILE_INFO *FileInfo + ) +{ + UINT8 FileState; + UINT8 ErasePolarity; + EFI_FFS_FILE_HEADER *FileHeader; + EFI_PEI_FV_HANDLE VolumeHandle; + + if ((FileHandle == NULL) || (FileInfo == NULL)) { + return EFI_INVALID_PARAMETER; + } + + VolumeHandle = 0; + // + // Retrieve the FirmwareVolume which the file resides in. + // + if (!FileHandleToVolume(FileHandle, &VolumeHandle)) { + return EFI_INVALID_PARAMETER; + } + + if (((EFI_FIRMWARE_VOLUME_HEADER*)VolumeHandle)->Attributes & EFI_FVB2_ERASE_POLARITY) { + ErasePolarity = 1; + } else { + ErasePolarity = 0; + } + + // + // Get FileState which is the highest bit of the State + // + FileState = GetFileState (ErasePolarity, (EFI_FFS_FILE_HEADER*)FileHandle); + + switch (FileState) { + case EFI_FILE_DATA_VALID: + case EFI_FILE_MARKED_FOR_UPDATE: + break; + default: + return EFI_INVALID_PARAMETER; + } + + FileHeader = (EFI_FFS_FILE_HEADER *)FileHandle; + CopyMem (&FileInfo->FileName, &FileHeader->Name, sizeof(EFI_GUID)); + FileInfo->FileType = FileHeader->Type; + FileInfo->FileAttributes = FileHeader->Attributes; + FileInfo->BufferSize = ((*(UINT32 *)FileHeader->Size) & 0x00FFFFFF) - sizeof (EFI_FFS_FILE_HEADER); + FileInfo->Buffer = (FileHeader + 1); + return EFI_SUCCESS; +} + + +/** + Get Information about the volume by name + + @param VolumeHandle Handle of the volume. + + @param VolumeInfo Upon exit, points to the volume's + information. + + @retval EFI_SUCCESS File information returned. + + @retval EFI_INVALID_PARAMETER If FileHandle does not + represent a valid file. + + @retval EFI_INVALID_PARAMETER If FileInfo is NULL. + +**/ +EFI_STATUS +EFIAPI +FfsGetVolumeInfo ( + IN EFI_PEI_FV_HANDLE VolumeHandle, + OUT EFI_FV_INFO *VolumeInfo + ) +{ + EFI_FIRMWARE_VOLUME_HEADER FwVolHeader; + EFI_FIRMWARE_VOLUME_EXT_HEADER *FwVolExHeaderInfo; + + if (VolumeInfo == NULL) { + return EFI_INVALID_PARAMETER; + } + + // + // VolumeHandle may not align at 8 byte, + // but FvLength is UINT64 type, which requires FvHeader align at least 8 byte. + // So, Copy FvHeader into the local FvHeader structure. + // + CopyMem (&FwVolHeader, VolumeHandle, sizeof (EFI_FIRMWARE_VOLUME_HEADER)); + // + // Check Fv Image Signature + // + if (FwVolHeader.Signature != EFI_FVH_SIGNATURE) { + return EFI_INVALID_PARAMETER; + } + VolumeInfo->FvAttributes = FwVolHeader.Attributes; + VolumeInfo->FvStart = (VOID *) VolumeHandle; + VolumeInfo->FvSize = FwVolHeader.FvLength; + CopyMem (&VolumeInfo->FvFormat, &FwVolHeader.FileSystemGuid, sizeof(EFI_GUID)); + + if (FwVolHeader.ExtHeaderOffset != 0) { + FwVolExHeaderInfo = (EFI_FIRMWARE_VOLUME_EXT_HEADER*)(((UINT8 *)VolumeHandle) + FwVolHeader.ExtHeaderOffset); + CopyMem (&VolumeInfo->FvName, &FwVolExHeaderInfo->FvName, sizeof(EFI_GUID)); + } + return EFI_SUCCESS; +} + + + +/** + Search through every FV until you find a file of type FileType + + @param FileType File handle of a Fv type file. + @param Volumehandle On success Volume Handle of the match + @param FileHandle On success File Handle of the match + + @retval EFI_NOT_FOUND FV image can't be found. + @retval EFI_SUCCESS Successfully found FileType + +**/ +EFI_STATUS +EFIAPI +FfsAnyFvFindFirstFile ( + IN EFI_FV_FILETYPE FileType, + OUT EFI_PEI_FV_HANDLE *VolumeHandle, + OUT EFI_PEI_FILE_HANDLE *FileHandle + ) +{ + EFI_STATUS Status; + UINTN Instance; + + // + // Search every FV for the DXE Core + // + Instance = 0; + *FileHandle = NULL; + + while (1) + { + Status = FfsFindNextVolume (Instance++, VolumeHandle); + if (EFI_ERROR (Status)) + { + break; + } + + Status = FfsFindNextFile (FileType, *VolumeHandle, FileHandle); + if (!EFI_ERROR (Status)) + { + break; + } + } + + DEBUG((DEBUG_INFO, "FfsAnyFvFindFirstFile with FileType = 0x%x (DXE_CORE == 0x05). %r\n", FileType, Status)); + + return Status; +} + +EFI_STATUS +EFIAPI +FfsAnyFvFindFileByName ( + IN CONST EFI_GUID *Name, + OUT EFI_PEI_FV_HANDLE *VolumeHandle, + OUT EFI_PEI_FILE_HANDLE *FileHandle + ) +{ + EFI_STATUS Status; + UINTN Instance; + + // + // Search every FV for the DXE Core + // + Instance = 0; + *FileHandle = NULL; + + while (1) + { + Status = FfsFindNextVolume (Instance++, VolumeHandle); + if (EFI_ERROR (Status)) + { + break; + } + + Status = FfsFindFileByName (Name, *VolumeHandle, FileHandle); + if (!EFI_ERROR (Status)) + { + break; + } + } + + DEBUG((DEBUG_INFO, "FfsAnyFvFindFileByName with name = %g, %r\n", Name, Status)); + + return Status; +} + + +/** + Get Fv image from the FV type file, then add FV & FV2 Hob. + + @param FileHandle File handle of a Fv type file. + + + @retval EFI_NOT_FOUND FV image can't be found. + @retval EFI_SUCCESS Successfully to process it. + +**/ +EFI_STATUS +EFIAPI +FfsProcessFvFile ( + IN EFI_PEI_FILE_HANDLE FvFileHandle + ) +{ + EFI_STATUS Status; + EFI_FV_INFO FvImageInfo; + UINT32 FvAlignment; + VOID *FvBuffer; + EFI_PEI_HOB_POINTERS HobFv2; + UINT32 FvImagesCnt; + UINT32 FvImageHandles[MAX_FV_IMAGES]; + UINT32 Index; + + // + // Check if this EFI_FV_FILETYPE_FIRMWARE_VOLUME_IMAGE file has already + // been extracted. + // + HobFv2.Raw = GetHobList (); + while ((HobFv2.Raw = GetNextHob (EFI_HOB_TYPE_FV2, HobFv2.Raw)) != NULL) { + + if (CompareGuid (&(((EFI_FFS_FILE_HEADER *)FvFileHandle)->Name), &HobFv2.FirmwareVolume2->FileName)) { + // + // this FILE has been dispatched, it will not be dispatched again. + // + return EFI_SUCCESS; + } + HobFv2.Raw = GET_NEXT_HOB (HobFv2); + } + + // + // Find FvImage in FvFile + // In FvFile there may be multi FvImages + // + FvImagesCnt = MAX_FV_IMAGES; + Status = FfsFindSectionDataEx (EFI_SECTION_FIRMWARE_VOLUME_IMAGE, + FvFileHandle, + FvImageHandles, + &FvImagesCnt); + if (EFI_ERROR (Status)) { + ASSERT(FALSE); + return Status; + } + + DEBUG((DEBUG_INFO, "Collect FvImageInfo\n")); + for(Index=0; Index < FvImagesCnt; Index++){ + + // + // Collect FvImage Info. + // + + ZeroMem (&FvImageInfo, sizeof (FvImageInfo)); + Status = FfsGetVolumeInfo ((EFI_PEI_FV_HANDLE)((UINTN)FvImageHandles[Index]), &FvImageInfo); + ASSERT_EFI_ERROR(Status); + DEBUG((DEBUG_INFO, " Fv Name=%g, Format=%g, Size=0x%x\n", FvImageInfo.FvName, FvImageInfo.FvFormat, FvImageInfo.FvSize)); + + // + // FvAlignment must be more than 8 bytes required by FvHeader structure. + // + FvAlignment = 1 << ((FvImageInfo.FvAttributes & EFI_FVB2_ALIGNMENT) >> 16); + if (FvAlignment < 8) { + FvAlignment = 8; + } + + // + // Check FvImage + // + if ((UINTN) FvImageInfo.FvStart % FvAlignment != 0) { + FvBuffer = AllocateAlignedPages (EFI_SIZE_TO_PAGES ((UINT32) FvImageInfo.FvSize), FvAlignment); + if (FvBuffer == NULL) { + return EFI_OUT_OF_RESOURCES; + } + CopyMem (FvBuffer, FvImageInfo.FvStart, (UINTN) FvImageInfo.FvSize); + // + // Update FvImageInfo after reload FvImage to new aligned memory + // + FfsGetVolumeInfo ((EFI_PEI_FV_HANDLE) FvBuffer, &FvImageInfo); + } + + + // + // Inform HOB consumer phase, i.e. DXE core, the existence of this FV + // + BuildFvHob ((EFI_PHYSICAL_ADDRESS) (UINTN) FvImageInfo.FvStart, FvImageInfo.FvSize); + + // + // Makes the encapsulated volume show up in DXE phase to skip processing of + // encapsulated file again. + // + BuildFv2Hob ( + (EFI_PHYSICAL_ADDRESS) (UINTN) FvImageInfo.FvStart, + FvImageInfo.FvSize, + &FvImageInfo.FvName, + &(((EFI_FFS_FILE_HEADER *)FvFileHandle)->Name) + ); + } + + return EFI_SUCCESS; +} + + + diff --git a/OvmfPkg/Library/PrePiLibTdx/PrePi.h b/OvmfPkg/Library/PrePiLibTdx/PrePi.h new file mode 100644 index 000000000000..340daef6ed09 --- /dev/null +++ b/OvmfPkg/Library/PrePiLibTdx/PrePi.h @@ -0,0 +1,29 @@ +/** @file + Library that helps implement monolithic PEI (i.e. PEI part of SEC) + + Copyright (c) 2008 - 2009, Apple Inc. All rights reserved.
+ + SPDX-License-Identifier: BSD-2-Clause-Patent + +**/ + +#ifndef _PI_PEI_H_ +#define _PI_PEI_H_ + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#endif diff --git a/OvmfPkg/Library/PrePiLibTdx/PrePiLib.c b/OvmfPkg/Library/PrePiLibTdx/PrePiLib.c new file mode 100644 index 000000000000..1ff83b28d1c4 --- /dev/null +++ b/OvmfPkg/Library/PrePiLibTdx/PrePiLib.c @@ -0,0 +1,251 @@ +/** @file + + Copyright (c) 2008 - 2009, Apple Inc. All rights reserved.
+ + SPDX-License-Identifier: BSD-2-Clause-Patent + +**/ + +#include + +// +// Hack to work in NT32 +// +EFI_STATUS + +EFIAPI + +SecWinNtPeiLoadFile ( + IN VOID *Pe32Data, + IN EFI_PHYSICAL_ADDRESS *ImageAddress, + IN UINT64 *ImageSize, + IN EFI_PHYSICAL_ADDRESS *EntryPoint + ); + +STATIC +VOID* +EFIAPI +AllocateCodePages ( + IN UINTN Pages + ) +{ + VOID *Alloc; + EFI_PEI_HOB_POINTERS Hob; + + Alloc = AllocatePages (Pages); + if (Alloc == NULL) { + return NULL; + } + + // find the HOB we just created, and change the type to EfiBootServicesCode + Hob.Raw = GetFirstHob (EFI_HOB_TYPE_MEMORY_ALLOCATION); + while (Hob.Raw != NULL) { + if (Hob.MemoryAllocation->AllocDescriptor.MemoryBaseAddress == (UINTN)Alloc) { + Hob.MemoryAllocation->AllocDescriptor.MemoryType = EfiBootServicesCode; + return Alloc; + } + Hob.Raw = GetNextHob (EFI_HOB_TYPE_MEMORY_ALLOCATION, GET_NEXT_HOB (Hob)); + } + + ASSERT (FALSE); + + FreePages (Alloc, Pages); + return NULL; +} + + +EFI_STATUS +EFIAPI +LoadPeCoffImage ( + IN VOID *PeCoffImage, + OUT EFI_PHYSICAL_ADDRESS *ImageAddress, + OUT UINT64 *ImageSize, + OUT EFI_PHYSICAL_ADDRESS *EntryPoint + ) +{ + RETURN_STATUS Status; + PE_COFF_LOADER_IMAGE_CONTEXT ImageContext; + VOID *Buffer; + + ZeroMem (&ImageContext, sizeof (ImageContext)); + + ImageContext.Handle = PeCoffImage; + ImageContext.ImageRead = PeCoffLoaderImageReadFromMemory; + + Status = PeCoffLoaderGetImageInfo (&ImageContext); + ASSERT_EFI_ERROR (Status); + + + // + // Allocate Memory for the image + // + Buffer = AllocateCodePages (EFI_SIZE_TO_PAGES((UINT32)ImageContext.ImageSize)); + ASSERT (Buffer != 0); + + + ImageContext.ImageAddress = (EFI_PHYSICAL_ADDRESS)(UINTN)Buffer; + + // + // Load the image to our new buffer + // + Status = PeCoffLoaderLoadImage (&ImageContext); + ASSERT_EFI_ERROR (Status); + + // + // Relocate the image in our new buffer + // + Status = PeCoffLoaderRelocateImage (&ImageContext); + ASSERT_EFI_ERROR (Status); + + + *ImageAddress = ImageContext.ImageAddress; + *ImageSize = ImageContext.ImageSize; + *EntryPoint = ImageContext.EntryPoint; + + // + // Flush not needed for all architectures. We could have a processor specific + // function in this library that does the no-op if needed. + // + InvalidateInstructionCacheRange ((VOID *)(UINTN)*ImageAddress, (UINTN)*ImageSize); + + return Status; +} + + + +typedef +VOID +(EFIAPI *DXE_CORE_ENTRY_POINT) ( + IN VOID *HobStart + ); + +EFI_STATUS +EFIAPI +LoadDxeCoreFromFfsFile ( + IN EFI_PEI_FILE_HANDLE FileHandle, + IN UINTN StackSize + ) +{ + EFI_STATUS Status; + VOID *PeCoffImage; + EFI_PHYSICAL_ADDRESS ImageAddress; + UINT64 ImageSize; + EFI_PHYSICAL_ADDRESS EntryPoint; + VOID *BaseOfStack; + VOID *TopOfStack; + VOID *Hob; + EFI_FV_FILE_INFO FvFileInfo; + + Status = FfsFindSectionData (EFI_SECTION_PE32, FileHandle, &PeCoffImage); + if (EFI_ERROR (Status)) { + return Status; + } + + + Status = LoadPeCoffImage (PeCoffImage, &ImageAddress, &ImageSize, &EntryPoint); +// For NT32 Debug Status = SecWinNtPeiLoadFile (PeCoffImage, &ImageAddress, &ImageSize, &EntryPoint); + ASSERT_EFI_ERROR (Status); + + // + // Extract the DxeCore GUID file name. + // + Status = FfsGetFileInfo (FileHandle, &FvFileInfo); + ASSERT_EFI_ERROR (Status); + + BuildModuleHob (&FvFileInfo.FileName, (EFI_PHYSICAL_ADDRESS)(UINTN)ImageAddress, EFI_SIZE_TO_PAGES ((UINT32) ImageSize) * EFI_PAGE_SIZE, EntryPoint); + + DEBUG ((EFI_D_INFO | EFI_D_LOAD, "Loading DxeCore at 0x%10p EntryPoint=0x%10p\n", (VOID *)(UINTN)ImageAddress, (VOID *)(UINTN)EntryPoint)); + + Hob = GetHobList (); + if (StackSize == 0) { + // User the current stack + + ((DXE_CORE_ENTRY_POINT)(UINTN)EntryPoint) (Hob); + } else { + + // + // Allocate 128KB for the Stack + // + BaseOfStack = AllocatePages (EFI_SIZE_TO_PAGES (StackSize)); + ASSERT (BaseOfStack != NULL); + + // + // Compute the top of the stack we were allocated. Pre-allocate a UINTN + // for safety. + // + TopOfStack = (VOID *) ((UINTN) BaseOfStack + EFI_SIZE_TO_PAGES (StackSize) * EFI_PAGE_SIZE - CPU_STACK_ALIGNMENT); + TopOfStack = ALIGN_POINTER (TopOfStack, CPU_STACK_ALIGNMENT); + + // + // Update the contents of BSP stack HOB to reflect the real stack info passed to DxeCore. + // + UpdateStackHob ((EFI_PHYSICAL_ADDRESS)(UINTN) BaseOfStack, StackSize); + + SwitchStack ( + (SWITCH_STACK_ENTRY_POINT)(UINTN)EntryPoint, + Hob, + NULL, + TopOfStack + ); + + } + + // Should never get here as DXE Core does not return + DEBUG ((EFI_D_ERROR, "DxeCore returned\n")); + ASSERT (FALSE); + + return EFI_DEVICE_ERROR; +} + + + +EFI_STATUS +EFIAPI +LoadDxeCoreFromFv ( + IN UINTN *FvInstance, OPTIONAL + IN UINTN StackSize + ) +{ + EFI_STATUS Status; + EFI_PEI_FV_HANDLE VolumeHandle; + EFI_PEI_FILE_HANDLE FileHandle = NULL; + + if (FvInstance != NULL) { + // + // Caller passed in a specific FV to try, so only try that one + // + Status = FfsFindNextVolume (*FvInstance, &VolumeHandle); + if (!EFI_ERROR (Status)) { + Status = FfsFindNextFile (EFI_FV_FILETYPE_DXE_CORE, VolumeHandle, &FileHandle); + } + } else { + Status = FfsAnyFvFindFirstFile (EFI_FV_FILETYPE_DXE_CORE, &VolumeHandle, &FileHandle); + } + + if (!EFI_ERROR (Status)) { + return LoadDxeCoreFromFfsFile (FileHandle, StackSize); + } + + return Status; +} + + +EFI_STATUS +EFIAPI +DecompressFirstFv ( + VOID + ) +{ + EFI_STATUS Status; + EFI_PEI_FV_HANDLE VolumeHandle; + EFI_PEI_FILE_HANDLE FileHandle; + + Status = FfsAnyFvFindFirstFile (EFI_FV_FILETYPE_FIRMWARE_VOLUME_IMAGE, &VolumeHandle, &FileHandle); + if (!EFI_ERROR (Status)) { + Status = FfsProcessFvFile (FileHandle); + } + + return Status; +} + + diff --git a/OvmfPkg/Library/PrePiLibTdx/PrePiLibTdx.inf b/OvmfPkg/Library/PrePiLibTdx/PrePiLibTdx.inf new file mode 100644 index 000000000000..09cdef3a31b8 --- /dev/null +++ b/OvmfPkg/Library/PrePiLibTdx/PrePiLibTdx.inf @@ -0,0 +1,51 @@ +#/** @file +# Component description file for TDX Pre PI Library +# +# LIbrary helps you build a platform that skips PEI and loads DXE Core +# directly. Helps building HOBs, reading data from the FV, and doing +# decompression. +# +# Copyright (c) 2018, Intel Corporation. All rights reserved.
+# Copyright (c) 2008, Apple Inc. All rights reserved.
+# +# SPDX-License-Identifier: BSD-2-Clause-Patent +# +# +#**/ + +[Defines] + INF_VERSION = 0x00010005 + BASE_NAME = PrePiLibTdx + FILE_GUID = 1F3A3278-82EB-4C0D-86F1-5BCDA5846CB2 + MODULE_TYPE = BASE + VERSION_STRING = 1.0 + LIBRARY_CLASS = PrePiLib + + +# +# VALID_ARCHITECTURES = X64 +# + +[Sources.common] + PrePi.h + FwVol.c + PrePiLib.c + +[Packages] + MdePkg/MdePkg.dec + OvmfPkg/OvmfPkg.dec + MdeModulePkg/MdeModulePkg.dec + +[LibraryClasses] + BaseLib + DebugLib + BaseMemoryLib + UefiDecompressLib + PeCoffLib + CacheMaintenanceLib + PrintLib + SerialPortLib + TimerLib + PerformanceLib + HobLib + ExtractGuidedSectionLib diff --git a/OvmfPkg/Library/QemuFwCfgLib/QemuFwCfgDxe.c b/OvmfPkg/Library/QemuFwCfgLib/QemuFwCfgDxe.c index 0182c9235cac..9f8ae1227b16 100644 --- a/OvmfPkg/Library/QemuFwCfgLib/QemuFwCfgDxe.c +++ b/OvmfPkg/Library/QemuFwCfgLib/QemuFwCfgDxe.c @@ -19,6 +19,7 @@ #include #include #include +#include #include #include "QemuFwCfgLibInternal.h" @@ -85,7 +86,7 @@ QemuFwCfgInitialize ( DEBUG ((DEBUG_INFO, "QemuFwCfg interface (DMA) is supported.\n")); } - if (mQemuFwCfgDmaSupported && MemEncryptSevIsEnabled ()) { + if (mQemuFwCfgDmaSupported && (MemEncryptSevIsEnabled () || (MemEncryptTdxIsEnabled ()))) { EFI_STATUS Status; // @@ -96,7 +97,7 @@ QemuFwCfgInitialize ( (VOID **)&mIoMmuProtocol); if (EFI_ERROR (Status)) { DEBUG ((DEBUG_ERROR, - "QemuFwCfgSevDma %a:%a Failed to locate IOMMU protocol.\n", + "QemuFwCfgDma %a:%a Failed to locate IOMMU protocol.\n", gEfiCallerBaseName, __FUNCTION__)); ASSERT (FALSE); CpuDeadLoop (); @@ -371,10 +372,10 @@ InternalQemuFwCfgDmaBytes ( DataBuffer = Buffer; // - // When SEV is enabled, map Buffer to DMA address before issuing the DMA + // When memory encryption is enabled, map Buffer to DMA address before issuing the DMA // request // - if (MemEncryptSevIsEnabled ()) { + if (MemEncryptSevIsEnabled() || MemEncryptTdxIsEnabled ()) { VOID *AccessBuffer; EFI_PHYSICAL_ADDRESS DataBufferAddress; diff --git a/OvmfPkg/Library/QemuFwCfgLib/QemuFwCfgDxeLib.inf b/OvmfPkg/Library/QemuFwCfgLib/QemuFwCfgDxeLib.inf index 48899ff1236a..b89a12ca31cb 100644 --- a/OvmfPkg/Library/QemuFwCfgLib/QemuFwCfgDxeLib.inf +++ b/OvmfPkg/Library/QemuFwCfgLib/QemuFwCfgDxeLib.inf @@ -42,6 +42,7 @@ DebugLib IoLib MemoryAllocationLib + MemEncryptTdxLib MemEncryptSevLib [Protocols] diff --git a/OvmfPkg/Library/QemuFwCfgS3Lib/DxeQemuFwCfgS3LibFwCfg.inf b/OvmfPkg/Library/QemuFwCfgS3Lib/DxeQemuFwCfgS3LibFwCfg.inf index 10f41d64c6a1..da342286db27 100644 --- a/OvmfPkg/Library/QemuFwCfgS3Lib/DxeQemuFwCfgS3LibFwCfg.inf +++ b/OvmfPkg/Library/QemuFwCfgS3Lib/DxeQemuFwCfgS3LibFwCfg.inf @@ -36,6 +36,7 @@ MemoryAllocationLib QemuFwCfgLib UefiBootServicesTableLib + TdxProbeLib [Protocols] gEfiS3SaveStateProtocolGuid ## SOMETIMES_CONSUMES diff --git a/OvmfPkg/Library/QemuFwCfgS3Lib/PeiQemuFwCfgS3LibFwCfg.inf b/OvmfPkg/Library/QemuFwCfgS3Lib/PeiQemuFwCfgS3LibFwCfg.inf index 8268828a7615..0b2643091050 100644 --- a/OvmfPkg/Library/QemuFwCfgS3Lib/PeiQemuFwCfgS3LibFwCfg.inf +++ b/OvmfPkg/Library/QemuFwCfgS3Lib/PeiQemuFwCfgS3LibFwCfg.inf @@ -37,3 +37,4 @@ [LibraryClasses] DebugLib QemuFwCfgLib + TdxProbeLib diff --git a/OvmfPkg/Library/QemuFwCfgS3Lib/QemuFwCfgS3PeiDxe.c b/OvmfPkg/Library/QemuFwCfgS3Lib/QemuFwCfgS3PeiDxe.c index 5557c70aa3ad..4fca19edf0b4 100644 --- a/OvmfPkg/Library/QemuFwCfgS3Lib/QemuFwCfgS3PeiDxe.c +++ b/OvmfPkg/Library/QemuFwCfgS3Lib/QemuFwCfgS3PeiDxe.c @@ -9,6 +9,7 @@ #include #include +#include /** Determine if S3 support is explicitly enabled. @@ -27,16 +28,23 @@ QemuFwCfgS3Enabled ( VOID ) { - RETURN_STATUS Status; - FIRMWARE_CONFIG_ITEM FwCfgItem; - UINTN FwCfgSize; - UINT8 SystemStates[6]; + RETURN_STATUS Status; + FIRMWARE_CONFIG_ITEM FwCfgItem; + UINTN FwCfgSize; + UINT8 SystemStates[6]; - Status = QemuFwCfgFindFile ("etc/system-states", &FwCfgItem, &FwCfgSize); - if (Status != RETURN_SUCCESS || FwCfgSize != sizeof SystemStates) { + if (ProbeTdGuest()) { + return FALSE; + + } else { + + Status = QemuFwCfgFindFile ("etc/system-states", &FwCfgItem, &FwCfgSize); + if (Status != RETURN_SUCCESS || FwCfgSize != sizeof SystemStates) { + return FALSE; + } + QemuFwCfgSelectItem (FwCfgItem); + QemuFwCfgReadBytes (sizeof SystemStates, SystemStates); + return (BOOLEAN) (SystemStates[3] & BIT7); } - QemuFwCfgSelectItem (FwCfgItem); - QemuFwCfgReadBytes (sizeof SystemStates, SystemStates); - return (BOOLEAN) (SystemStates[3] & BIT7); } diff --git a/OvmfPkg/Library/TdvfPlatformLibQemu/Platform.c b/OvmfPkg/Library/TdvfPlatformLibQemu/Platform.c new file mode 100644 index 000000000000..1c9aa92f9319 --- /dev/null +++ b/OvmfPkg/Library/TdvfPlatformLibQemu/Platform.c @@ -0,0 +1,270 @@ +/**@file + Platform PEI driver + + Copyright (c) 2006 - 2016, Intel Corporation. All rights reserved.
+ Copyright (c) 2011, Andrei Warkentin + + SPDX-License-Identifier: BSD-2-Clause-Patent + +**/ + +// +// The package level header files this module uses +// +#include + +// +// The Library classes this module consumes +// +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + + +// +// Host Bridge DID Address +// +#define HOSTBRIDGE_DID \ + PCI_LIB_ADDRESS (0, 0, 0, PCI_DEVICE_ID_OFFSET) + +// +// Values we program into the PM base address registers +// +#define PIIX4_PMBA_VALUE 0xB000 +#define ICH9_PMBASE_VALUE 0x0600 + +EFI_STATUS +GetNamedFwCfgBoolean ( + IN CHAR8 *FwCfgFileName, + OUT BOOLEAN *Setting + ) +{ + EFI_STATUS Status; + FIRMWARE_CONFIG_ITEM FwCfgItem; + UINTN FwCfgSize; + UINT8 Value[3]; + + Status = QemuFwCfgFindFile (FwCfgFileName, &FwCfgItem, &FwCfgSize); + if (EFI_ERROR (Status)) { + return Status; + } + if (FwCfgSize > sizeof Value) { + return EFI_BAD_BUFFER_SIZE; + } + QemuFwCfgSelectItem (FwCfgItem); + QemuFwCfgReadBytes (FwCfgSize, Value); + + if ((FwCfgSize == 1) || + (FwCfgSize == 2 && Value[1] == '\n') || + (FwCfgSize == 3 && Value[1] == '\r' && Value[2] == '\n')) { + switch (Value[0]) { + case '0': + case 'n': + case 'N': + *Setting = FALSE; + return EFI_SUCCESS; + + case '1': + case 'y': + case 'Y': + *Setting = TRUE; + return EFI_SUCCESS; + + default: + break; + } + } + return EFI_PROTOCOL_ERROR; +} + +VOID +PciExBarInitialization ( + VOID + ) +{ + union { + UINT64 Uint64; + UINT32 Uint32[2]; + } PciExBarBase; + + // + // We only support the 256MB size for the MMCONFIG area: + // 256 buses * 32 devices * 8 functions * 4096 bytes config space. + // + // The masks used below enforce the Q35 requirements that the MMCONFIG area + // be (a) correctly aligned -- here at 256 MB --, (b) located under 64 GB. + // + // Note that (b) also ensures that the minimum address width we have + // determined in AddressWidthInitialization(), i.e., 36 bits, will suffice + // for DXE's page tables to cover the MMCONFIG area. + // + PciExBarBase.Uint64 = FixedPcdGet64 (PcdPciExpressBaseAddress); + ASSERT ((PciExBarBase.Uint32[1] & MCH_PCIEXBAR_HIGHMASK) == 0); + ASSERT ((PciExBarBase.Uint32[0] & MCH_PCIEXBAR_LOWMASK) == 0); + + // + // Clear the PCIEXBAREN bit first, before programming the high register. + // + PciWrite32 (DRAMC_REGISTER_Q35 (MCH_PCIEXBAR_LOW), 0); + + // + // Program the high register. Then program the low register, setting the + // MMCONFIG area size and enabling decoding at once. + // + PciWrite32 (DRAMC_REGISTER_Q35 (MCH_PCIEXBAR_HIGH), PciExBarBase.Uint32[1]); + PciWrite32 ( + DRAMC_REGISTER_Q35 (MCH_PCIEXBAR_LOW), + PciExBarBase.Uint32[0] | MCH_PCIEXBAR_BUS_FF | MCH_PCIEXBAR_EN + ); +} + +VOID +MiscInitialization ( + EFI_HOB_PLATFORM_INFO *PlatformInfoHob + ) +{ + RETURN_STATUS Status; + FIRMWARE_CONFIG_ITEM FwCfgItem; + UINTN FwCfgSize; + UINTN PmCmd; + UINTN Pmba; + UINT32 PmbaAndVal; + UINT32 PmbaOrVal; + UINTN AcpiCtlReg; + UINT8 AcpiEnBit; + // + // Disable A20 Mask + // + IoOr8 (0x92, BIT1); + + // + // Determine platform type and save Host Bridge DID to PCD + // + switch (PlatformInfoHob->HostBridgePciDevId) { + case INTEL_82441_DEVICE_ID: + PmCmd = POWER_MGMT_REGISTER_PIIX4 (PCI_COMMAND_OFFSET); + Pmba = POWER_MGMT_REGISTER_PIIX4 (PIIX4_PMBA); + PmbaAndVal = ~(UINT32)PIIX4_PMBA_MASK; + PmbaOrVal = PIIX4_PMBA_VALUE; + AcpiCtlReg = POWER_MGMT_REGISTER_PIIX4 (PIIX4_PMREGMISC); + AcpiEnBit = PIIX4_PMREGMISC_PMIOSE; + break; + case INTEL_Q35_MCH_DEVICE_ID: + PmCmd = POWER_MGMT_REGISTER_Q35 (PCI_COMMAND_OFFSET); + Pmba = POWER_MGMT_REGISTER_Q35 (ICH9_PMBASE); + PmbaAndVal = ~(UINT32)ICH9_PMBASE_MASK; + PmbaOrVal = ICH9_PMBASE_VALUE; + AcpiCtlReg = POWER_MGMT_REGISTER_Q35 (ICH9_ACPI_CNTL); + AcpiEnBit = ICH9_ACPI_CNTL_ACPI_EN; + break; + default: + DEBUG ((EFI_D_ERROR, "%a: Unknown Host Bridge Device ID: 0x%04x\n", + __FUNCTION__, PlatformInfoHob->HostBridgePciDevId)); + ASSERT (FALSE); + return; + } + // + // If the appropriate IOspace enable bit is set, assume the ACPI PMBA + // has been configured and skip the setup here. + // This matches the logic in AcpiTimerLibConstructor (). + // + if ((PciRead8 (AcpiCtlReg) & AcpiEnBit) == 0) { + // + // The PEI phase should be exited with fully accessibe ACPI PM IO space: + // 1. set PMBA + // + PciAndThenOr32 (Pmba, PmbaAndVal, PmbaOrVal); + + // + // 2. set PCICMD/IOSE + // + PciOr8 (PmCmd, EFI_PCI_COMMAND_IO_SPACE); + + // + // 3. set ACPI PM IO enable bit (PMREGMISC:PMIOSE or ACPI_CNTL:ACPI_EN) + // + PciOr8 (AcpiCtlReg, AcpiEnBit); + } + + if (PlatformInfoHob->HostBridgePciDevId == INTEL_Q35_MCH_DEVICE_ID) { + // + // Set Root Complex Register Block BAR + // + PciWrite32 ( + POWER_MGMT_REGISTER_Q35 (ICH9_RCBA), + ICH9_ROOT_COMPLEX_BASE | ICH9_RCBA_EN + ); + + // + // Set PCI Express Register Range Base Address + // + PciExBarInitialization (); + } + + // + // check for overrides + // + Status = QemuFwCfgFindFile ("etc/system-states", &FwCfgItem, &FwCfgSize); + if (Status != RETURN_SUCCESS || FwCfgSize != sizeof PlatformInfoHob->SystemStates) { + DEBUG ((DEBUG_INFO, "ACPI using S3/S4 defaults\n")); + return; + } + QemuFwCfgSelectItem (FwCfgItem); + QemuFwCfgReadBytes (sizeof PlatformInfoHob->SystemStates, PlatformInfoHob->SystemStates); +} + +/** + Perform Platform initialization. +**/ +VOID +EFIAPI +TdvfPlatformInitialize ( + EFI_HOB_PLATFORM_INFO *PlatformInfoHob +) +{ + DEBUG ((DEBUG_INFO, "Qemu Platform Loaded\n")); + + PlatformInfoHob->HostBridgePciDevId = PciRead16 (HOSTBRIDGE_DID); + + if (PlatformInfoHob->HostBridgePciDevId == INTEL_Q35_MCH_DEVICE_ID) { + BuildResourceDescriptorHob ( + EFI_RESOURCE_IO, + EFI_RESOURCE_ATTRIBUTE_PRESENT | + EFI_RESOURCE_ATTRIBUTE_INITIALIZED, + 0x6000, + 0xa000 + ); + } else { + BuildResourceDescriptorHob ( + EFI_RESOURCE_IO, + EFI_RESOURCE_ATTRIBUTE_PRESENT | + EFI_RESOURCE_ATTRIBUTE_INITIALIZED, + 0xc000, + 0x4000 + ); + } + + MiscInitialization (PlatformInfoHob); + + GetNamedFwCfgBoolean ("opt/ovmf/PcdSetNxForStack", &PlatformInfoHob->SetNxForStack); +} diff --git a/OvmfPkg/Library/TdvfPlatformLibQemu/TdvfPlatformLibQemuSec.inf b/OvmfPkg/Library/TdvfPlatformLibQemu/TdvfPlatformLibQemuSec.inf new file mode 100644 index 000000000000..dd60d4d79da2 --- /dev/null +++ b/OvmfPkg/Library/TdvfPlatformLibQemu/TdvfPlatformLibQemuSec.inf @@ -0,0 +1,49 @@ +## @file +# +# Tdvf Platform Lib for the QEMU VMM +# +# Copyright (C) 2013, Red Hat, Inc. +# Copyright (c) 2008 - 2012, Intel Corporation. All rights reserved.
+# Copyright (c) 2017, AMD Incorporated. All rights reserved.
+# +# SPDX-License-Identifier: BSD-2-Clause-Patent +# +## + +[Defines] + INF_VERSION = 0x00010005 + BASE_NAME = TdvfPlatformLibQemuSec + FILE_GUID = 44cabe70-fcfb-11ea-8b6e-0800200c9a66 + MODULE_TYPE = BASE + VERSION_STRING = 1.0 + LIBRARY_CLASS = TdvfPlatformLib|SEC + +# +# The following information is for reference only and not required by the build tools. +# +# VALID_ARCHITECTURES = X64 +# + +[Sources] + Platform.c + +[Packages] + MdePkg/MdePkg.dec + UefiCpuPkg/UefiCpuPkg.dec + MdeModulePkg/MdeModulePkg.dec + OvmfPkg/OvmfPkg.dec + +[Guids] + +[LibraryClasses] + BaseLib + BaseMemoryLib + DebugLib + IoLib + PcdLib + HobLib + PciLib + QemuFwCfgLib + +[Pcd] + gEfiMdePkgTokenSpaceGuid.PcdPciExpressBaseAddress diff --git a/OvmfPkg/Library/TdxProbeLib/TdxProbeLib.c b/OvmfPkg/Library/TdxProbeLib/TdxProbeLib.c new file mode 100644 index 000000000000..6f4220607a48 --- /dev/null +++ b/OvmfPkg/Library/TdxProbeLib/TdxProbeLib.c @@ -0,0 +1,40 @@ +/** @file + instance of TdxProbeLib in OvmfPkg. + + Copyright (c) 2021, Intel Corporation. All rights reserved.
+ SPDX-License-Identifier: BSD-2-Clause-Patent + +**/ + + +#include +#include + +BOOLEAN mTdGuest = FALSE; +BOOLEAN mTdGuestProbed = FALSE; + +#define TDX_WORK_AREA_OFFSET 0x10 + +/** + Probe whether it is TD guest or Non-TD guest. + + @return TRUE TD guest + @return FALSE Non-TD guest +**/ +BOOLEAN +EFIAPI +ProbeTdGuest ( + VOID) +{ + UINT8 * TdxWorkArea; + + if (mTdGuestProbed) { + return mTdGuest; + } + + TdxWorkArea = (UINT8 *)((UINTN)(FixedPcdGet32 (PcdTdMailboxBase) + TDX_WORK_AREA_OFFSET)); + mTdGuest = *TdxWorkArea != 0; + mTdGuestProbed = TRUE; + + return mTdGuest; +} diff --git a/OvmfPkg/Library/TdxProbeLib/TdxProbeLib.inf b/OvmfPkg/Library/TdxProbeLib/TdxProbeLib.inf new file mode 100644 index 000000000000..7e36735bfeff --- /dev/null +++ b/OvmfPkg/Library/TdxProbeLib/TdxProbeLib.inf @@ -0,0 +1,31 @@ +## @file +# Tdx Probe library instance in OvmfPkg +# +# Copyright (c) 2020 - 2021, Intel Corporation. All rights reserved.
+# SPDX-License-Identifier: BSD-2-Clause-Patent +# +## + +[Defines] + INF_VERSION = 0x00010005 + BASE_NAME = TdxProbeLib + FILE_GUID = 26BF0B58-6E9D-4375-A363-52FD83FB82CE + MODULE_TYPE = BASE + VERSION_STRING = 1.0 + LIBRARY_CLASS = TdxProbeLib + +# +# The following information is for reference only and not required by the build tools. +# +# VALID_ARCHITECTURES = X64 IA32 +# + +[Sources] + TdxProbeLib.c + +[Packages] + MdePkg/MdePkg.dec + OvmfPkg/OvmfPkg.dec + +[Pcd] + gUefiOvmfPkgTokenSpaceGuid.PcdTdMailboxBase diff --git a/OvmfPkg/Library/TdxStartupLib/DxeLoad.c b/OvmfPkg/Library/TdxStartupLib/DxeLoad.c new file mode 100644 index 000000000000..a3734964fad3 --- /dev/null +++ b/OvmfPkg/Library/TdxStartupLib/DxeLoad.c @@ -0,0 +1,325 @@ +/** @file + Responsibility of this file is to load the DXE Core from a Firmware Volume. + +Copyright (c) 2016 HP Development Company, L.P. +Copyright (c) 2006 - 2020, Intel Corporation. All rights reserved.
+SPDX-License-Identifier: BSD-2-Clause-Patent + +**/ + +#include "TdxStartupInternal.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "X64/PageTables.h" +#include + +#define PCD_PEIM_GUID { \ + 0x9b3ada4f, 0xae56, 0x4c24, {0x8d, 0xea, 0xf0, 0x3b, 0x75, 0x58, 0xae, 0x50} \ + } + +EFI_GUID mPcdPeimGuid = PCD_PEIM_GUID; + +#define STACK_SIZE 0x20000 + +EFI_MEMORY_TYPE_INFORMATION mDefaultMemoryTypeInformation[] = { + { EfiACPIMemoryNVS, 0x004 }, + { EfiACPIReclaimMemory, 0x008 }, + { EfiReservedMemoryType, 0x004 }, + { EfiRuntimeServicesData, 0x024 }, + { EfiRuntimeServicesCode, 0x030 }, + { EfiBootServicesCode, 0x180 }, + { EfiBootServicesData, 0xF00 }, + { EfiMaxMemoryType, 0x000 } +}; + + +/** + Transfers control to DxeCore. + + This function performs a CPU architecture specific operations to execute + the entry point of DxeCore + + @param DxeCoreEntryPoint The entry point of DxeCore. + +**/ +VOID +HandOffToDxeCore ( + IN EFI_PHYSICAL_ADDRESS DxeCoreEntryPoint + ) +{ + VOID *BaseOfStack; + VOID *TopOfStack; + UINTN PageTables; + + // + // Clear page 0 and mark it as allocated if NULL pointer detection is enabled. + // + if (IsNullDetectionEnabled ()) { + ClearFirst4KPage (GetHobList ()); + BuildMemoryAllocationHob (0, EFI_PAGES_TO_SIZE (1), EfiBootServicesData); + } + + + // + // Allocate 128KB for the Stack + // + BaseOfStack = AllocatePages (EFI_SIZE_TO_PAGES (STACK_SIZE)); + ASSERT (BaseOfStack != NULL); + + // + // Compute the top of the stack we were allocated. Pre-allocate a UINTN + // for safety. + // + TopOfStack = (VOID *) ((UINTN) BaseOfStack + EFI_SIZE_TO_PAGES (STACK_SIZE) * EFI_PAGE_SIZE - CPU_STACK_ALIGNMENT); + TopOfStack = ALIGN_POINTER (TopOfStack, CPU_STACK_ALIGNMENT); + + DEBUG((DEBUG_INFO, "BaseOfStack=0x%x, TopOfStack=0x%x\n", BaseOfStack, TopOfStack)); + + PageTables = 0; + if (FeaturePcdGet (PcdDxeIplBuildPageTables)) { + // + // Create page table and save PageMapLevel4 to CR3 + // + PageTables = CreateIdentityMappingPageTables ((EFI_PHYSICAL_ADDRESS) (UINTN) BaseOfStack, + STACK_SIZE); + } else { + // + // Set NX for stack feature also require PcdDxeIplBuildPageTables be TRUE + // for the DxeIpl and the DxeCore are both X64. + // + ASSERT (FixedPcdGetBool (PcdTdxSetNxForStack) == FALSE); + ASSERT (FixedPcdGetBool (PcdCpuStackGuard) == FALSE); + } + + if (FeaturePcdGet (PcdDxeIplBuildPageTables)) { + AsmWriteCr3 (PageTables); + } + + // + // Update the contents of BSP stack HOB to reflect the real stack info passed to DxeCore. + // + UpdateStackHob ((EFI_PHYSICAL_ADDRESS)(UINTN) BaseOfStack, STACK_SIZE); + + DEBUG ((DEBUG_INFO, "SwitchStack then Jump to DxeCore\n")); + // + // Transfer the control to the entry point of DxeCore. + // + SwitchStack ( + (SWITCH_STACK_ENTRY_POINT)(UINTN)DxeCoreEntryPoint, + GetHobList(), + NULL, + TopOfStack + ); +} + +EFI_STATUS +FindPcdPeim ( + IN INTN FvInstance, + IN OUT EFI_PEI_FILE_HANDLE *FileHandle + ) +{ + EFI_STATUS Status; + EFI_PEI_FV_HANDLE VolumeHandle; + + ASSERT (FileHandle != NULL); + ASSERT (FvInstance != -1); + *FileHandle = NULL; + + // + // Caller passed in a specific FV to try, so only try that one + // + Status = FfsFindNextVolume (FvInstance, &VolumeHandle); + if (!EFI_ERROR (Status)) { + Status = FfsFindNextFile (EFI_FV_FILETYPE_FIRMWARE_VOLUME_IMAGE, VolumeHandle, FileHandle); + + if (*FileHandle) { + // Assume the FV that contains multiple compressed FVs. + // So decompress the compressed FVs + Status = FfsProcessFvFile (*FileHandle); + ASSERT_EFI_ERROR (Status); + + Status = FfsAnyFvFindFileByName (&mPcdPeimGuid, &VolumeHandle, FileHandle); + } + } + + return Status; +} + +EFI_STATUS +EFIAPI +InitPeimPcd ( + IN INTN FvInstance) +{ + EFI_STATUS Status; + PEI_PCD_DATABASE *Database; + PEI_PCD_DATABASE *PeiPcdDbBinary; + EFI_PEI_FILE_HANDLE FileHandle; + + // + // Find the PcdPeim and initialize the Pcd Database + // + Status = FindPcdPeim (FvInstance, &FileHandle); + ASSERT_EFI_ERROR (Status); + + Status = FfsFindSectionData (EFI_SECTION_RAW, FileHandle, (VOID**)(UINTN)&PeiPcdDbBinary); + if (EFI_ERROR (Status)) { + return Status; + } + Database = BuildGuidHob (&gPcdDataBaseHobGuid, PeiPcdDbBinary->Length + PeiPcdDbBinary->UninitDataBaseSize); + + ZeroMem (Database, PeiPcdDbBinary->Length + PeiPcdDbBinary->UninitDataBaseSize); + + // + // PeiPcdDbBinary is smaller than Database + // + CopyMem (Database, PeiPcdDbBinary, PeiPcdDbBinary->Length); + + return Status; +} + + +/** + Searches DxeCore in all firmware Volumes and loads the first + instance that contains DxeCore. + + @return FileHandle of DxeCore to load DxeCore. + +**/ +EFI_STATUS +FindDxeCore ( + IN INTN FvInstance, + IN OUT EFI_PEI_FILE_HANDLE *FileHandle + ) +{ + EFI_STATUS Status; + EFI_PEI_FV_HANDLE VolumeHandle; + + ASSERT (FileHandle != NULL); + *FileHandle = NULL; + + if (FvInstance != -1) { + // + // Caller passed in a specific FV to try, so only try that one + // + Status = FfsFindNextVolume (FvInstance, &VolumeHandle); + if (!EFI_ERROR (Status)) { + Status = FfsFindNextFile (EFI_FV_FILETYPE_FIRMWARE_VOLUME_IMAGE, VolumeHandle, FileHandle); + + if (*FileHandle) { + // Assume the FV that contains multiple compressed FVs. + // So decompress the compressed FVs + Status = FfsProcessFvFile (*FileHandle); + ASSERT_EFI_ERROR (Status); + + Status = FfsAnyFvFindFirstFile (EFI_FV_FILETYPE_DXE_CORE, &VolumeHandle, FileHandle); + } + } + } else { + // Assume the FV that contains the SEC (our code) also contains a compressed FV. + Status = DecompressFirstFv (); + ASSERT_EFI_ERROR (Status); + Status = FfsAnyFvFindFirstFile (EFI_FV_FILETYPE_DXE_CORE, &VolumeHandle, FileHandle); + } + + return Status; +} + +/** + This function finds DXE Core in the firmware volume and transfer the control to + DXE core. + + @return EFI_SUCCESS DXE core was successfully loaded. + @return EFI_OUT_OF_RESOURCES There are not enough resources to load DXE core. + +**/ +EFI_STATUS +EFIAPI +DxeLoadCore ( + IN INTN FvInstance + ) +{ + EFI_STATUS Status; + EFI_FV_FILE_INFO DxeCoreFileInfo; + EFI_PHYSICAL_ADDRESS DxeCoreAddress; + UINT64 DxeCoreSize; + EFI_PHYSICAL_ADDRESS DxeCoreEntryPoint; + EFI_PEI_FILE_HANDLE FileHandle; + VOID *PeCoffImage; + + + // + // Create Memory Type Information HOB + // + BuildGuidDataHob ( + &gEfiMemoryTypeInformationGuid, + mDefaultMemoryTypeInformation, + sizeof (mDefaultMemoryTypeInformation) + ); + + // + // Look in all the FVs present and find the DXE Core FileHandle + // + Status = FindDxeCore (FvInstance, &FileHandle); + ASSERT_EFI_ERROR (Status); + + // + // Load the DXE Core from a Firmware Volume. + // + Status = FfsFindSectionData (EFI_SECTION_PE32, FileHandle, &PeCoffImage); + if (EFI_ERROR (Status)) { + return Status; + } + + Status = LoadPeCoffImage (PeCoffImage, &DxeCoreAddress, &DxeCoreSize, &DxeCoreEntryPoint); + ASSERT_EFI_ERROR (Status); + + // + // Extract the DxeCore GUID file name. + // + Status = FfsGetFileInfo (FileHandle, &DxeCoreFileInfo); + ASSERT_EFI_ERROR (Status); + + // + // Add HOB for the DXE Core + // + BuildModuleHob ( + &DxeCoreFileInfo.FileName, + DxeCoreAddress, + ALIGN_VALUE (DxeCoreSize, EFI_PAGE_SIZE), + DxeCoreEntryPoint + ); + + DEBUG ((DEBUG_INFO | DEBUG_LOAD, "Loading DXE CORE at 0x%11p EntryPoint=0x%11p\n", + (VOID *)(UINTN)DxeCoreAddress, FUNCTION_ENTRY_POINT (DxeCoreEntryPoint))); + + // + // Initialize PeimPcd database + // + Status = InitPeimPcd (FvInstance); + ASSERT_EFI_ERROR (Status); + + // Transfer control to the DXE Core + // The hand off state is simply a pointer to the HOB list + // + HandOffToDxeCore (DxeCoreEntryPoint); + + // + // If we get here, then the DXE Core returned. This is an error + // DxeCore should not return. + // + ASSERT (FALSE); + CpuDeadLoop (); + + return EFI_OUT_OF_RESOURCES; +} + + diff --git a/OvmfPkg/Library/TdxStartupLib/Hob.c b/OvmfPkg/Library/TdxStartupLib/Hob.c new file mode 100644 index 000000000000..321e4aeb4935 --- /dev/null +++ b/OvmfPkg/Library/TdxStartupLib/Hob.c @@ -0,0 +1,451 @@ +/** @file + Main SEC phase code. Handles initial TDX Hob List Processing + + Copyright (c) 2008, Intel Corporation. All rights reserved.
+ (C) Copyright 2016 Hewlett Packard Enterprise Development LP
+ + SPDX-License-Identifier: BSD-2-Clause-Patent + +**/ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "TdxStartupInternal.h" + +VOID +EFIAPI +DEBUG_HOBLIST ( + IN CONST VOID *HobStart + ) +{ + EFI_PEI_HOB_POINTERS Hob; + Hob.Raw = (UINT8 *) HobStart; + // + // Parse the HOB list until end of list or matching type is found. + // + while (!END_OF_HOB_LIST (Hob)) { + DEBUG ((DEBUG_INFO, "HOB(%p) : %x %x\n", Hob, Hob.Header->HobType, Hob.Header->HobLength)); + switch (Hob.Header->HobType) { + case EFI_HOB_TYPE_RESOURCE_DESCRIPTOR: + DEBUG ((DEBUG_INFO, "\t: %x %x %llx %llx\n", + Hob.ResourceDescriptor->ResourceType, + Hob.ResourceDescriptor->ResourceAttribute, + Hob.ResourceDescriptor->PhysicalStart, + Hob.ResourceDescriptor->ResourceLength)); + + break; + case EFI_HOB_TYPE_MEMORY_ALLOCATION: + DEBUG ((DEBUG_INFO, "\t: %llx %llx %x\n", + Hob.MemoryAllocation->AllocDescriptor.MemoryBaseAddress, + Hob.MemoryAllocation->AllocDescriptor.MemoryLength, + Hob.MemoryAllocation->AllocDescriptor.MemoryType)); + break; + default: + break; + } + Hob.Raw = GET_NEXT_HOB (Hob); + } +} + +/** + Check the value whether in the valid list. + + @param[in] Value - A value + @param[in] ValidList - A pointer to valid list + @param[in] ValidListLength - Length of valid list + + @retval TRUE - The value is in valid list. + @retval FALSE - The value is not in valid list. + +**/ +BOOLEAN +EFIAPI +IsInValidList ( + IN UINT32 Value, + IN UINT32 *ValidList, + IN UINT32 ValidListLength +) { + UINT32 index; + + if (ValidList == NULL) { + return FALSE; + } + + for (index = 0; index < ValidListLength; index ++) { + if (ValidList[index] == Value) { + return TRUE; + } + } + + return FALSE; +} + +/** + Check the integrity of VMM Hob List. + + @param[in] VmmHobList - A pointer to Hob List + + @retval TRUE - The Hob List is valid. + @retval FALSE - The Hob List is invalid. + +**/ +BOOLEAN +EFIAPI +ValidateHobList ( + IN CONST VOID *VmmHobList + ) +{ + EFI_PEI_HOB_POINTERS Hob; + UINT32 EFI_BOOT_MODE_LIST[12] = { BOOT_WITH_FULL_CONFIGURATION, + BOOT_WITH_MINIMAL_CONFIGURATION, + BOOT_ASSUMING_NO_CONFIGURATION_CHANGES, + BOOT_WITH_FULL_CONFIGURATION_PLUS_DIAGNOSTICS, + BOOT_WITH_DEFAULT_SETTINGS, + BOOT_ON_S4_RESUME, + BOOT_ON_S5_RESUME, + BOOT_WITH_MFG_MODE_SETTINGS, + BOOT_ON_S2_RESUME, + BOOT_ON_S3_RESUME, + BOOT_ON_FLASH_UPDATE, + BOOT_IN_RECOVERY_MODE + }; + + UINT32 EFI_RESOURCE_TYPE_LIST[8] = { EFI_RESOURCE_SYSTEM_MEMORY, + EFI_RESOURCE_MEMORY_MAPPED_IO, + EFI_RESOURCE_IO, + EFI_RESOURCE_FIRMWARE_DEVICE, + EFI_RESOURCE_MEMORY_MAPPED_IO_PORT, + EFI_RESOURCE_MEMORY_RESERVED, + EFI_RESOURCE_IO_RESERVED, + EFI_RESOURCE_MAX_MEMORY_TYPE + }; + + if (VmmHobList == NULL) { + DEBUG ((DEBUG_ERROR, "HOB: HOB data pointer is NULL\n")); + return FALSE; + } + + Hob.Raw = (UINT8 *) VmmHobList; + + // + // Parse the HOB list until end of list or matching type is found. + // + while (!END_OF_HOB_LIST (Hob)) { + if (Hob.Header->Reserved != (UINT32) 0) { + DEBUG ((DEBUG_ERROR, "HOB: Hob header Reserved filed should be zero\n")); + return FALSE; + } + + if (Hob.Header->HobLength == 0) { + DEBUG ((DEBUG_ERROR, "HOB: Hob header LEANGTH should not be zero\n")); + return FALSE; + } + + switch (Hob.Header->HobType) { + case EFI_HOB_TYPE_HANDOFF: + if (Hob.Header->HobLength != sizeof(EFI_HOB_HANDOFF_INFO_TABLE)) { + DEBUG ((DEBUG_ERROR, "HOB: Hob length is not equal corresponding hob structure. Type: 0x%04x\n", EFI_HOB_TYPE_HANDOFF)); + return FALSE; + } + + if (IsInValidList (Hob.HandoffInformationTable->BootMode, EFI_BOOT_MODE_LIST, 12) == FALSE) { + DEBUG ((DEBUG_ERROR, "HOB: Unknow HandoffInformationTable BootMode type. Type: 0x%08x\n", Hob.HandoffInformationTable->BootMode)); + return FALSE; + } + + if ((Hob.HandoffInformationTable->EfiFreeMemoryTop % 4096) != 0) { + DEBUG ((DEBUG_ERROR, "HOB: HandoffInformationTable EfiFreeMemoryTop address must be 4-KB aligned to meet page restrictions of UEFI.\ + Address: 0x%016lx\n", Hob.HandoffInformationTable->EfiFreeMemoryTop)); + return FALSE; + } + + break; + case EFI_HOB_TYPE_RESOURCE_DESCRIPTOR: + if (Hob.Header->HobLength != sizeof(EFI_HOB_RESOURCE_DESCRIPTOR)) { + DEBUG ((DEBUG_ERROR, "HOB: Hob length is not equal corresponding hob structure. Type: 0x%04x\n", EFI_HOB_TYPE_RESOURCE_DESCRIPTOR)); + return FALSE; + } + + if (IsInValidList (Hob.ResourceDescriptor->ResourceType, EFI_RESOURCE_TYPE_LIST, 8) == FALSE) { + DEBUG ((DEBUG_ERROR, "HOB: Unknow ResourceDescriptor ResourceType type. Type: 0x%08x\n", Hob.ResourceDescriptor->ResourceType)); + return FALSE; + } + + if ((Hob.ResourceDescriptor->ResourceAttribute & (~(EFI_RESOURCE_ATTRIBUTE_PRESENT | + EFI_RESOURCE_ATTRIBUTE_INITIALIZED | + EFI_RESOURCE_ATTRIBUTE_TESTED | + EFI_RESOURCE_ATTRIBUTE_READ_PROTECTED | + EFI_RESOURCE_ATTRIBUTE_WRITE_PROTECTED | + EFI_RESOURCE_ATTRIBUTE_EXECUTION_PROTECTED | + EFI_RESOURCE_ATTRIBUTE_PERSISTENT | + EFI_RESOURCE_ATTRIBUTE_SINGLE_BIT_ECC | + EFI_RESOURCE_ATTRIBUTE_MULTIPLE_BIT_ECC | + EFI_RESOURCE_ATTRIBUTE_ECC_RESERVED_1 | + EFI_RESOURCE_ATTRIBUTE_ECC_RESERVED_2 | + EFI_RESOURCE_ATTRIBUTE_UNCACHEABLE | + EFI_RESOURCE_ATTRIBUTE_WRITE_COMBINEABLE | + EFI_RESOURCE_ATTRIBUTE_WRITE_THROUGH_CACHEABLE | + EFI_RESOURCE_ATTRIBUTE_WRITE_BACK_CACHEABLE | + EFI_RESOURCE_ATTRIBUTE_16_BIT_IO | + EFI_RESOURCE_ATTRIBUTE_32_BIT_IO | + EFI_RESOURCE_ATTRIBUTE_64_BIT_IO | + EFI_RESOURCE_ATTRIBUTE_UNCACHED_EXPORTED | + EFI_RESOURCE_ATTRIBUTE_READ_PROTECTABLE | + EFI_RESOURCE_ATTRIBUTE_WRITE_PROTECTABLE | + EFI_RESOURCE_ATTRIBUTE_EXECUTION_PROTECTABLE | + EFI_RESOURCE_ATTRIBUTE_PERSISTABLE | + EFI_RESOURCE_ATTRIBUTE_READ_ONLY_PROTECTED | + EFI_RESOURCE_ATTRIBUTE_READ_ONLY_PROTECTABLE | + EFI_RESOURCE_ATTRIBUTE_MORE_RELIABLE | + EFI_RESOURCE_ATTRIBUTE_ENCRYPTED))) != 0) { + DEBUG ((DEBUG_ERROR, "HOB: Unknow ResourceDescriptor ResourceAttribute type. Type: 0x%08x\n", Hob.ResourceDescriptor->ResourceAttribute)); + return FALSE; + } + + break; + // EFI_HOB_GUID_TYPE is variable length data, so skip check + case EFI_HOB_TYPE_GUID_EXTENSION: + break; + case EFI_HOB_TYPE_FV: + if (Hob.Header->HobLength != sizeof (EFI_HOB_FIRMWARE_VOLUME)) { + DEBUG ((DEBUG_ERROR, "HOB: Hob length is not equal corresponding hob structure. Type: 0x%04x\n", EFI_HOB_TYPE_FV)); + return FALSE; + } + break; + case EFI_HOB_TYPE_FV2: + if (Hob.Header->HobLength != sizeof(EFI_HOB_FIRMWARE_VOLUME2)) { + DEBUG ((DEBUG_ERROR, "HOB: Hob length is not equal corresponding hob structure. Type: 0x%04x\n", EFI_HOB_TYPE_FV2)); + return FALSE; + } + break; + case EFI_HOB_TYPE_FV3: + if (Hob.Header->HobLength != sizeof(EFI_HOB_FIRMWARE_VOLUME3)) { + DEBUG ((DEBUG_ERROR, "HOB: Hob length is not equal corresponding hob structure. Type: 0x%04x\n", EFI_HOB_TYPE_FV3)); + return FALSE; + } + break; + case EFI_HOB_TYPE_CPU: + if (Hob.Header->HobLength != sizeof(EFI_HOB_CPU)) { + DEBUG ((DEBUG_ERROR, "HOB: Hob length is not equal corresponding hob structure. Type: 0x%04x\n", EFI_HOB_TYPE_CPU)); + return FALSE; + } + + for (UINT32 index = 0; index < 6; index ++) { + if (Hob.Cpu->Reserved[index] != 0) { + DEBUG ((DEBUG_ERROR, "HOB: Cpu Reserved field will always be set to zero.\n")); + return FALSE; + } + } + break; + default: + DEBUG ((DEBUG_ERROR, "HOB: Hob type is not know. Type: 0x%04x\n", Hob.Header->HobType)); + return FALSE; + } + // Get next HOB + Hob.Raw = (UINT8 *) (Hob.Raw + Hob.Header->HobLength); + } + return TRUE; +} + +/** + Processing the incoming HobList for the TD + + Firmware must parse list, and accept the pages of memory before their can be + use by the guest. + + In addition, the hob list will be measured so it cannot be modified. + After accepting the pages, the firmware will use the largest memory resource + region as it's initial memory pool. It will initialize a new hoblist there, and the pre-DXE + memory allocation will use that for allocations. + + Also, it will location the highest memory region < 4GIG. It will use this to allocate + ACPI NVS memory for mailbox and spinloop for AP use. The APs will be relocated there + until the guest OS wakes them up. + + @param[in] VmmHobList The Hoblist pass the firmware + +**/ +VOID +EFIAPI +ProcessHobList ( + IN CONST VOID *VmmHobList + ) +{ + EFI_PEI_HOB_POINTERS Hob; + EFI_PHYSICAL_ADDRESS PhysicalEnd; + EFI_PHYSICAL_ADDRESS PhysicalStart; + UINT64 Length; + EFI_HOB_RESOURCE_DESCRIPTOR *LowMemoryResource = NULL; + + ASSERT (VmmHobList != NULL); + Hob.Raw = (UINT8 *) VmmHobList; + + // + // Parse the HOB list until end of list or matching type is found. + // + while (!END_OF_HOB_LIST (Hob)) { + + if (Hob.Header->HobType == EFI_HOB_TYPE_RESOURCE_DESCRIPTOR) { + DEBUG ((DEBUG_INFO, "\nResourceType: 0x%x\n", Hob.ResourceDescriptor->ResourceType)); + + // + // FixMe: + // We only accept the ResourceType (EFI_RESOURCE_UNACCEPTED_MEMORY) + // But this ResourceType is still in upstream. + // So now we accept the ResourceType (EFI_RESOURCE_SYSTEM_MEMORY) without + // EFI_RESOURCE_ATTRIBUTE_ENCRYPTED. + // After EFI_RESOURCE_UNACCEPTED_MEMORY is in EDK2, then we switch to it. + // + if (Hob.ResourceDescriptor->ResourceType == EFI_RESOURCE_SYSTEM_MEMORY) { + + DEBUG ((DEBUG_INFO, "ResourceAttribute: 0x%x\n", Hob.ResourceDescriptor->ResourceAttribute)); + DEBUG ((DEBUG_INFO, "PhysicalStart: 0x%llx\n", Hob.ResourceDescriptor->PhysicalStart)); + DEBUG ((DEBUG_INFO, "ResourceLength: 0x%llx\n", Hob.ResourceDescriptor->ResourceLength)); + DEBUG ((DEBUG_INFO, "Owner: %g\n\n", &Hob.ResourceDescriptor->Owner)); + + PhysicalEnd = Hob.ResourceDescriptor->PhysicalStart + Hob.ResourceDescriptor->ResourceLength; + + if (PhysicalEnd <= BASE_4GB) { + if ((LowMemoryResource == NULL) || (Hob.ResourceDescriptor->ResourceLength > LowMemoryResource->ResourceLength)) { + LowMemoryResource = Hob.ResourceDescriptor; + } + } + + MpAcceptMemoryResourceRange ( + Hob.ResourceDescriptor->PhysicalStart, + PhysicalEnd); + } + } + Hob.Raw = GET_NEXT_HOB (Hob); + } + + ASSERT (LowMemoryResource != NULL); + + PhysicalStart = LowMemoryResource->PhysicalStart; + Length = LowMemoryResource->ResourceLength; + + // + // HobLib doesn't like HobStart at address 0 so adjust is needed + // + if (PhysicalStart == 0) { + PhysicalStart += EFI_PAGE_SIZE; + Length -= EFI_PAGE_SIZE; + } + + + HobConstructor ( + (VOID *) PhysicalStart, + Length, + (VOID *) PhysicalStart, + (VOID *) (PhysicalStart + Length) + ); + + PrePeiSetHobList ((VOID *)(UINT64)PhysicalStart); +} + +/** + Transfer the incoming HobList for the TD to the final HobList for Dxe + + @param[in] VmmHobList The Hoblist pass the firmware + +**/ +VOID +EFIAPI +TransferHobList ( + IN CONST VOID *VmmHobList + ) +{ + EFI_PEI_HOB_POINTERS Hob; + EFI_RESOURCE_ATTRIBUTE_TYPE ResourceAttribute; + EFI_PHYSICAL_ADDRESS PhysicalEnd; + + Hob.Raw = (UINT8 *) VmmHobList; + while (!END_OF_HOB_LIST (Hob)) { + switch (Hob.Header->HobType) { + case EFI_HOB_TYPE_RESOURCE_DESCRIPTOR: + ResourceAttribute = Hob.ResourceDescriptor->ResourceAttribute; + PhysicalEnd = Hob.ResourceDescriptor->PhysicalStart + Hob.ResourceDescriptor->ResourceLength; + + // + // We mark each resource that we issue AcceptPage to with EFI_RESOURCE_SYSTEM_MEMORY + // + if ((Hob.ResourceDescriptor->ResourceType == EFI_RESOURCE_SYSTEM_MEMORY) && + (PhysicalEnd <= BASE_4GB)) { + ResourceAttribute |= EFI_RESOURCE_ATTRIBUTE_ENCRYPTED; + } + BuildResourceDescriptorHob ( + Hob.ResourceDescriptor->ResourceType, + ResourceAttribute, + Hob.ResourceDescriptor->PhysicalStart, + Hob.ResourceDescriptor->ResourceLength); + break; + case EFI_HOB_TYPE_MEMORY_ALLOCATION: + BuildMemoryAllocationHob ( + Hob.MemoryAllocation->AllocDescriptor.MemoryBaseAddress, + Hob.MemoryAllocation->AllocDescriptor.MemoryLength, + Hob.MemoryAllocation->AllocDescriptor.MemoryType); + break; + } + Hob.Raw = GET_NEXT_HOB (Hob); + } + DEBUG_HOBLIST (GetHobList ()); +} + +/** + Create a log event for the Hoblist passed from the VMM. + + This function will create a unique GUID hob entry will be + found from the TCG driver building the event log. + This module will generate the measurement with the data in + this hob, and log the event. + + @param[in] VmmHobList The Hoblist pass the firmware + +**/ +VOID +EFIAPI +LogHobList ( + IN CONST VOID *VmmHobList + ) +{ + EFI_PEI_HOB_POINTERS Hob; + TDX_HANDOFF_TABLE_POINTERS2 HandoffTables; + EFI_STATUS Status; + + Hob.Raw = (UINT8 *) VmmHobList; + + // + // Parse the HOB list until end of list. + // + while (!END_OF_HOB_LIST (Hob)) { + Hob.Raw = GET_NEXT_HOB (Hob); + } + + // + // Init the log event for HOB measurement + // + + HandoffTables.TableDescriptionSize = sizeof (HandoffTables.TableDescription); + CopyMem (HandoffTables.TableDescription, HANDOFF_TABLE_DESC, sizeof (HandoffTables.TableDescription)); + HandoffTables.NumberOfTables = 1; + CopyGuid (&(HandoffTables.TableEntry[0].VendorGuid), &gUefiOvmfPkgTokenSpaceGuid); + HandoffTables.TableEntry[0].VendorTable = (VOID *) VmmHobList; + + Status = CreateTdxExtendEvent ( + 1, // PCRIndex + EV_EFI_HANDOFF_TABLES2, // EventType + (VOID *)&HandoffTables, // EventData + sizeof (HandoffTables), // EventSize + (UINT8*) (UINTN) VmmHobList, // HashData + (UINTN) ((UINT8 *)Hob.Raw - (UINT8 *)VmmHobList) // HashDataLen + ); + + ASSERT_EFI_ERROR (Status); +} diff --git a/OvmfPkg/Library/TdxStartupLib/Mp.c b/OvmfPkg/Library/TdxStartupLib/Mp.c new file mode 100644 index 000000000000..01ede34bb4ac --- /dev/null +++ b/OvmfPkg/Library/TdxStartupLib/Mp.c @@ -0,0 +1,299 @@ +/** @file + Main SEC MP code. + + Copyright (c) 2008, Intel Corporation. All rights reserved.
+ (C) Copyright 2016 Hewlett Packard Enterprise Development LP
+ + SPDX-License-Identifier: BSD-2-Clause-Patent + +**/ + +#include +#include +#include +#include +#include +#include +#include "TdxStartupInternal.h" + +#define ALIGNED_2MB_MASK 0x1fffff + +/** + This function will be called by BSP to wakeup APs the are spinning on mailbox + in protected mode + + @param[in] Command Command to send APs + @param[in] WakeupVector If used, address for APs to start executing + @param[in] WakeArgsX Args to pass to APs for excuting commands +**/ +VOID +EFIAPI +MpSendWakeupCommand( + IN UINT16 Command, + IN UINT64 WakeupVector, + IN UINT64 WakeupArgs1, + IN UINT64 WakeupArgs2, + IN UINT64 WakeupArgs3, + IN UINT64 WakeupArgs4 +) +{ + volatile MP_WAKEUP_MAILBOX *MailBox; + + MailBox = (volatile MP_WAKEUP_MAILBOX *)GetMailBox(); + MailBox->ApicId = MP_CPU_PROTECTED_MODE_MAILBOX_APICID_INVALID; + MailBox->WakeUpVector = 0; + MailBox->Command = MpProtectedModeWakeupCommandNoop; + MailBox->ApicId = MP_CPU_PROTECTED_MODE_MAILBOX_APICID_BROADCAST; + MailBox->WakeUpVector = WakeupVector; + MailBox->WakeUpArgs1 = WakeupArgs1; + MailBox->WakeUpArgs2 = WakeupArgs2; + MailBox->WakeUpArgs3 = WakeupArgs3; + MailBox->WakeUpArgs4 = WakeupArgs4; + AsmCpuid (0x01, NULL, NULL, NULL, NULL); + MailBox->Command = Command; + AsmCpuid (0x01, NULL, NULL, NULL, NULL); + return; +} + +VOID +EFIAPI +MpSerializeStart ( + VOID + ) +{ + volatile MP_WAKEUP_MAILBOX *MailBox; + UINT32 NumOfCpus; + + NumOfCpus = GetNumCpus(); + MailBox = (volatile MP_WAKEUP_MAILBOX *)GetMailBox(); + + DEBUG((DEBUG_VERBOSE, "Waiting for APs to arriving. NumOfCpus=%d, MailBox=%p\n", NumOfCpus, MailBox)); + while (MailBox->NumCpusArriving != ( NumOfCpus -1 )) { + CpuPause(); + } + DEBUG((DEBUG_VERBOSE, "Releasing APs\n")); + MailBox->NumCpusExiting = NumOfCpus; + InterlockedIncrement ((UINT32 *) &MailBox->NumCpusArriving); +} + +VOID +EFIAPI +MpSerializeEnd ( + VOID + ) +{ + volatile MP_WAKEUP_MAILBOX *MailBox; + + MailBox = (volatile MP_WAKEUP_MAILBOX *)GetMailBox(); + DEBUG((DEBUG_VERBOSE, "Waiting for APs to finish\n")); + while (MailBox->NumCpusExiting != 1 ) { + CpuPause(); + } + DEBUG((DEBUG_VERBOSE, "Restarting APs\n")); + MailBox->Command = MpProtectedModeWakeupCommandNoop; + MailBox->NumCpusArriving = 0; + InterlockedDecrement ((UINT32 *) &MailBox->NumCpusExiting); +} + +EFI_STATUS +EFIAPI +BspAcceptMemoryResourceRange ( + IN EFI_PHYSICAL_ADDRESS StartAddress, + IN UINT64 Length, + IN UINT64 AcceptChunkSize, + IN UINT64 AcceptPageSize + ) +{ + EFI_STATUS Status; + UINT64 Pages; + UINT64 Stride; + EFI_PHYSICAL_ADDRESS PhysicalAddress; + volatile MP_WAKEUP_MAILBOX *MailBox; + + Status = EFI_SUCCESS; + PhysicalAddress = StartAddress; + Stride = GetNumCpus() * AcceptChunkSize; + MailBox = (volatile MP_WAKEUP_MAILBOX *)GetMailBox(); + + while (!EFI_ERROR(Status) && PhysicalAddress < StartAddress + Length) { + // + // Decrease size of near end of resource if needed. + // + Pages = RShiftU64(MIN(AcceptChunkSize, Length), EFI_PAGE_SHIFT); + + MailBox->Tallies[0] += (UINT32)Pages; + + Status = TdAcceptPages (PhysicalAddress, Pages, AcceptPageSize); + // + // Bump address to next chunk this cpu is responisble for + // + PhysicalAddress += Stride; + } + + return Status; +} + +/** + This function will be called to accept pages. BSP and APs are invokded + to do the task together. + + TDCALL(ACCEPT_PAGE) supports the accept page size of 4k and 2M. To + simplify the implementation, the Memory to be accpeted is splitted + into 3 parts: + ----------------- <-- StartAddress1 (not 2M aligned) + | part 1 | Length1 < 2M + |---------------| <-- StartAddress2 (2M aligned) + | | Length2 = Integer multiples of 2M + | part 2 | + | | + |---------------| <-- StartAddress3 + | part 3 | Length3 < 2M + |---------------| + + part 1) will be accepted in 4k and by BSP. + Part 2) will be accepted in 2M and by BSP/AP. + Part 3) will be accepted in 4k and by BSP. + + @param[in] PhysicalAddress Start physical adress + @param[in] PhysicalEnd End physical address +**/ +VOID +EFIAPI +MpAcceptMemoryResourceRange ( + IN EFI_PHYSICAL_ADDRESS PhysicalAddress, + IN EFI_PHYSICAL_ADDRESS PhysicalEnd + ) +{ + EFI_STATUS Status; + UINT64 AcceptChunkSize; + UINT64 AcceptPageSize; + UINT64 StartAddress1; + UINT64 StartAddress2; + UINT64 StartAddress3; + UINT64 TotalLength; + UINT64 Length1; + UINT64 Length2; + UINT64 Length3; + volatile MP_WAKEUP_MAILBOX *MailBox; + + MailBox = (volatile MP_WAKEUP_MAILBOX *)GetMailBox (); + AcceptChunkSize = FixedPcdGet64 (PcdTdxAcceptChunkSize); + AcceptPageSize = FixedPcdGet64 (PcdTdxAcceptPageSize); + TotalLength = PhysicalEnd - PhysicalAddress; + StartAddress1 = 0; + StartAddress2 = 0; + StartAddress3 = 0; + Length1 = 0; + Length2 = 0; + Length3 = 0; + + if (AcceptPageSize == SIZE_4KB || TotalLength <= SIZE_2MB) { + // + // if total length is less than 2M, then we accept pages in 4k + // + StartAddress1 = 0; + Length1 = 0; + StartAddress2 = PhysicalAddress; + Length2 = PhysicalEnd - PhysicalAddress; + StartAddress3 = 0; + Length3 = 0; + } else if (AcceptPageSize == SIZE_2MB) { + // + // Total length is bigger than 2M and Page Accept size 2M is supported. + // + if ((PhysicalAddress & ALIGNED_2MB_MASK) == 0) { + // + // Start address is 2M aligned + // + StartAddress1 = 0; + Length1 = 0; + StartAddress2 = PhysicalAddress; + Length2 = TotalLength & ALIGNED_2MB_MASK; + + if (TotalLength > Length2) { + // + // There is remaining part 3) + // + StartAddress3 = StartAddress2 + Length2; + Length3 = TotalLength - Length2; + ASSERT (Length3 < SIZE_2MB); + } + } else { + // + // Start address is not 2M aligned and total length is bigger than 2M. + // + StartAddress1 = PhysicalAddress; + ASSERT (TotalLength > SIZE_2MB); + Length1 = SIZE_2MB - (PhysicalAddress & ALIGNED_2MB_MASK); + if (TotalLength - Length1 < SIZE_2MB) { + // + // The Part 2) length is less than 2MB, so let's accept all the + // memory in 4K + // + Length1 = TotalLength; + + } else { + StartAddress2 = PhysicalAddress + Length1; + Length2 = (TotalLength - Length1) & ALIGNED_2MB_MASK; + StartAddress3 = StartAddress2 + Length2; + Length3 = TotalLength - Length1 - Length2; + ASSERT (Length3 < SIZE_2MB); + } + } + } + + DEBUG ((DEBUG_INFO, "TdAccept: 0x%llx - 0x%llx\n", PhysicalAddress, TotalLength)); + DEBUG ((DEBUG_INFO, " Part1: 0x%llx - 0x%llx\n", StartAddress1, Length1)); + DEBUG ((DEBUG_INFO, " Part2: 0x%llx - 0x%llx\n", StartAddress2, Length2)); + DEBUG ((DEBUG_INFO, " Part3: 0x%llx - 0x%llx\n", StartAddress3, Length3)); + + MpSerializeStart(); + + if (Length2 > 0) { + MpSendWakeupCommand ( + MpProtectedModeWakeupCommandAcceptPages, + 0, + StartAddress2, + StartAddress2 + Length2, + AcceptChunkSize, + AcceptPageSize); + + Status = BspAcceptMemoryResourceRange ( + StartAddress2, + Length2, + AcceptChunkSize, + AcceptPageSize); + ASSERT (!EFI_ERROR (Status)); + } + + if (Length1 > 0) { + Status = BspAcceptMemoryResourceRange ( + StartAddress1, + Length1, + AcceptChunkSize, + SIZE_4KB); + ASSERT (!EFI_ERROR (Status)); + } + + if (Length3 > 0) { + Status = BspAcceptMemoryResourceRange ( + StartAddress3, + Length3, + AcceptChunkSize, + SIZE_4KB); + ASSERT (!EFI_ERROR (Status)); + } + + MpSerializeEnd(); + + DEBUG((DEBUG_INFO, "Tallies %x %x %x %x %x %x %x %x\n", + MailBox->Tallies[0], + MailBox->Tallies[1], + MailBox->Tallies[2], + MailBox->Tallies[3], + MailBox->Tallies[4], + MailBox->Tallies[5], + MailBox->Tallies[6], + MailBox->Tallies[7])); + +} diff --git a/OvmfPkg/Library/TdxStartupLib/Tcg.c b/OvmfPkg/Library/TdxStartupLib/Tcg.c new file mode 100644 index 000000000000..7b0b145da0b4 --- /dev/null +++ b/OvmfPkg/Library/TdxStartupLib/Tcg.c @@ -0,0 +1,259 @@ +/** @file + Initialize TPM2 device and measure FVs before handing off control to DXE. + +Copyright (c) 2015 - 2018, Intel Corporation. All rights reserved.
+Copyright (c) 2017, Microsoft Corporation. All rights reserved.
+SPDX-License-Identifier: BSD-2-Clause-Patent + +**/ + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "TdxStartupInternal.h" + +#pragma pack (1) + +#define FV_HANDOFF_TABLE_DESC "Fv(XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX)" +typedef struct { + UINT8 BlobDescriptionSize; + UINT8 BlobDescription[sizeof(FV_HANDOFF_TABLE_DESC)]; + EFI_PHYSICAL_ADDRESS BlobBase; + UINT64 BlobLength; +} FV_HANDOFF_TABLE_POINTERS2; + +#pragma pack () + +extern UINT32 GetMappedRtmrIndex(UINT32 PCRIndex); + +/** + Add a new entry to the Event Log. + + @param[in] DigestList A list of digest. + @param[in,out] NewEventHdr Pointer to a TCG_PCR_EVENT_HDR data structure. + @param[in] NewEventData Pointer to the new event data. + + @retval EFI_SUCCESS The new event log entry was added. + @retval EFI_OUT_OF_RESOURCES No enough memory to log the new event. +**/ +EFI_STATUS +CreateTdxExtendEvent ( + IN TCG_PCRINDEX PCRIndex, + IN TCG_EVENTTYPE EventType, + IN UINT8 *EventData, + IN UINTN EventSize, + IN UINT8 *HashData, + IN UINTN HashDataLen + ) +{ + EFI_STATUS Status; + UINT32 RtmrIndex; + VOID *EventHobData; + TCG_PCR_EVENT2 *TcgPcrEvent2; + UINT8 *DigestBuffer; + TDX_DIGEST_VALUE *TdxDigest; + TPML_DIGEST_VALUES DigestList; + UINT8 *Ptr; + + + DEBUG ((EFI_D_INFO, "Creating TdTcg2PcrEvent PCR %d EventType 0x%x\n", PCRIndex, EventType)); + + RtmrIndex = GetMappedRtmrIndex (PCRIndex); + Status = HashAndExtend (RtmrIndex, + (VOID*)HashData, + HashDataLen, + &DigestList); + if (EFI_ERROR (Status)) { + DEBUG ((EFI_D_INFO, "Failed to HashAndExtend. %r\n", Status)); + return Status; + } + + // + // Use TDX_DIGEST_VALUE in the GUID HOB DataLength calculation + // to reserve enough buffer to hold TPML_DIGEST_VALUES compact binary + // which is limited to a SHA384 digest list + // + EventHobData = BuildGuidHob ( + &gTcgEvent2EntryHobGuid, + sizeof(TcgPcrEvent2->PCRIndex) + sizeof(TcgPcrEvent2->EventType) + + sizeof(TDX_DIGEST_VALUE) + + sizeof(TcgPcrEvent2->EventSize) + EventSize); + + if (EventHobData == NULL) { + return EFI_OUT_OF_RESOURCES; + } + + DEBUG ((EFI_D_INFO, " Tcg2PcrEvent - data %p\n", EventHobData)); + + Ptr = (UINT8*)EventHobData; + // + // Initialize PcrEvent data now + // + CopyMem(Ptr, &PCRIndex, sizeof(TCG_PCRINDEX)); + Ptr += sizeof(TCG_PCRINDEX); + CopyMem(Ptr, &EventType, sizeof(TCG_EVENTTYPE)); + Ptr += sizeof(TCG_EVENTTYPE); + + DigestBuffer = Ptr; + DEBUG ((EFI_D_INFO, " Tcg2PcrEvent - digest %p\n", DigestBuffer)); + + TdxDigest = (TDX_DIGEST_VALUE *)DigestBuffer; + TdxDigest->count = 1; + TdxDigest->hashAlg = TPM_ALG_SHA384; + CopyMem (TdxDigest->sha384, + DigestList.digests[0].digest.sha384, + SHA384_DIGEST_SIZE); + + Ptr += sizeof(TDX_DIGEST_VALUE); + DEBUG ((EFI_D_INFO, " Tcg2PcrEvent - eventdata %p\n", DigestBuffer)); + + CopyMem (Ptr, &EventSize, sizeof(UINT32)); + Ptr += sizeof(UINT32); + CopyMem (Ptr, EventData, EventSize); + Ptr += EventSize; + + Status = EFI_SUCCESS; + return Status; +} + + +/** + Get the FvName from the FV header. + + Causion: The FV is untrusted input. + + @param[in] FvBase Base address of FV image. + @param[in] FvLength Length of FV image. + + @return FvName pointer + @retval NULL FvName is NOT found +**/ +VOID * +GetFvName ( + IN EFI_PHYSICAL_ADDRESS FvBase, + IN UINT64 FvLength + ) +{ + EFI_FIRMWARE_VOLUME_HEADER *FvHeader; + EFI_FIRMWARE_VOLUME_EXT_HEADER *FvExtHeader; + + if (FvBase >= MAX_ADDRESS) { + return NULL; + } + if (FvLength >= MAX_ADDRESS - FvBase) { + return NULL; + } + if (FvLength < sizeof(EFI_FIRMWARE_VOLUME_HEADER)) { + return NULL; + } + + FvHeader = (EFI_FIRMWARE_VOLUME_HEADER *)(UINTN)FvBase; + if (FvHeader->ExtHeaderOffset < sizeof(EFI_FIRMWARE_VOLUME_HEADER)) { + return NULL; + } + if (FvHeader->ExtHeaderOffset + sizeof(EFI_FIRMWARE_VOLUME_EXT_HEADER) > FvLength) { + return NULL; + } + FvExtHeader = (EFI_FIRMWARE_VOLUME_EXT_HEADER *)(UINTN)(FvBase + FvHeader->ExtHeaderOffset); + + return &FvExtHeader->FvName; +} + +/** + Measure FV image. + Add it into the measured FV list after the FV is measured successfully. + + @param[in] FvBase Base address of FV image. + @param[in] FvLength Length of FV image. + @param[in] PcrIndex Index of PCR + + @retval EFI_SUCCESS Fv image is measured successfully + or it has been already measured. + @retval EFI_OUT_OF_RESOURCES No enough memory to log the new event. + @retval EFI_DEVICE_ERROR The command was unsuccessful. + +**/ +EFI_STATUS +TdxMeasureFvImage ( + IN EFI_PHYSICAL_ADDRESS FvBase, + IN UINT64 FvLength, + IN UINT8 PcrIndex + ) +{ + EFI_STATUS Status; + FV_HANDOFF_TABLE_POINTERS2 FvBlob2; + VOID *FvName; + + // + // Init the log event for FV measurement + // + FvBlob2.BlobDescriptionSize = sizeof(FvBlob2.BlobDescription); + CopyMem (FvBlob2.BlobDescription, FV_HANDOFF_TABLE_DESC, sizeof(FvBlob2.BlobDescription)); + FvName = GetFvName (FvBase, FvLength); + if (FvName != NULL) { + AsciiSPrint ((CHAR8 *)FvBlob2.BlobDescription, sizeof(FvBlob2.BlobDescription), "Fv(%g)", FvName); + } + FvBlob2.BlobBase = FvBase; + FvBlob2.BlobLength = FvLength; + + // + // Hash the FV, extend digest to the TPM and log TCG event + // + Status = CreateTdxExtendEvent ( + PcrIndex, // PCRIndex + EV_EFI_PLATFORM_FIRMWARE_BLOB2, // EventType + (VOID *)&FvBlob2, // EventData + sizeof (FvBlob2), // EventSize + (UINT8*) (UINTN) FvBase, // HashData + (UINTN) FvLength // HashDataLen + ); + + if (EFI_ERROR(Status)) { + DEBUG ((DEBUG_ERROR, "The FV which failed to be measured starts at: 0x%x\n", FvBase)); + return Status; + } + return Status; +} + +EFI_STATUS +MeasureQemuCfgSystemSts ( + IN TCG_PCRINDEX PCRIndex, + IN UINT8 *HashData, + IN UINTN HashDataLength + ) +{ + EFI_STATUS Status; + CHAR8 *Item = "etc/system-states"; + + Status = CreateTdxExtendEvent ( + PCRIndex, // PCRIndex + EV_PLATFORM_CONFIG_FLAGS, // EventType + (UINT8*)Item, // EventData + AsciiStrLen(Item), // EventSize + HashData, // HashData + HashDataLength // HashDataLen + ); + + return Status; +} + diff --git a/OvmfPkg/Library/TdxStartupLib/TdxStartup.c b/OvmfPkg/Library/TdxStartupLib/TdxStartup.c new file mode 100644 index 000000000000..e548ec425406 --- /dev/null +++ b/OvmfPkg/Library/TdxStartupLib/TdxStartup.c @@ -0,0 +1,260 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "TdxStartupInternal.h" + +volatile VOID *mMailBox = NULL; +UINT32 mNumOfCpus = 0; + +#define GET_GPAW_INIT_STATE(INFO) ((UINT8) ((INFO) & 0x3f)) + +volatile VOID * +EFIAPI +GetMailBox ( + VOID + ) +{ + return mMailBox; +} + +UINT32 +EFIAPI +GetNumCpus ( + VOID + ) +{ + return mNumOfCpus; +} + +/** + Validates the configuration volume, measures it, and created a FV Hob + + @param[in] VolumeAddress The base of the where the CFV must reside + + @retval None +**/ +VOID +EFIAPI +MeasureConfigurationVolume ( + IN UINT64 VolumeAddress + ) +{ + EFI_FIRMWARE_VOLUME_HEADER *Fv; + UINT16 Expected; + UINT16 Checksum; + UINT32 CfvSize; + + ASSERT (VolumeAddress); + ASSERT (((UINTN) VolumeAddress & EFI_PAGE_MASK) == 0); + + Fv = (EFI_FIRMWARE_VOLUME_HEADER *)(UINTN)VolumeAddress; + ASSERT (Fv->Signature == EFI_FVH_SIGNATURE ); + + // + // Validate Volume Checksum + // + Checksum = CalculateSum16 ((UINT16 *) Fv, Fv->HeaderLength); + + Expected = (UINT16) (((UINTN) Fv->Checksum + 0x10000 - Checksum) & 0xffff); + + DEBUG ((EFI_D_INFO, "FV@%p Checksum is 0x%x, expected 0x%x\n", + Fv, Fv->Checksum, Expected)); + + ASSERT (Fv->Checksum == Expected); + + // + // Add FvHob for the Volume + // + BuildFvHob ((UINTN)Fv, Fv->FvLength); + + // + // The configuration volume needs to be measured + // + CfvSize = PcdGet32 (PcdCfvRawDataSize); + TdxMeasureFvImage ((UINTN)Fv, CfvSize, 1); +} + +VOID +EFIAPI +TdxStartup( + IN VOID * Context, + IN VOID * VmmHobList, + IN UINTN Info, + IN fProcessLibraryConstructorList Function + ) +{ + EFI_SEC_PEI_HAND_OFF *SecCoreData; + EFI_FIRMWARE_VOLUME_HEADER *BootFv; + EFI_STATUS Status; + EFI_HOB_PLATFORM_INFO PlatformInfoHob; + VOID *Address; + VOID *ApLoopFunc = NULL; + UINT32 RelocationPages; + MP_RELOCATION_MAP RelocationMap; + MP_WAKEUP_MAILBOX *RelocatedMailBox; + UINT32 DxeCodeBase; + UINT32 DxeCodeSize; + TD_RETURN_DATA TdReturnData; + UINT8 *PlatformInfoPtr; + + Status = EFI_SUCCESS; + BootFv = NULL; + SecCoreData = (EFI_SEC_PEI_HAND_OFF *) Context; + + Status = TdCall (TDCALL_TDINFO, 0,0,0, &TdReturnData); + ASSERT (Status == EFI_SUCCESS); + mNumOfCpus = TdReturnData.TdInfo.NumVcpus; + mMailBox = (VOID *)(UINTN)PcdGet32 (PcdTdMailboxBase); + + DEBUG ((EFI_D_INFO, + "Tdx started with(Hob: 0x%x, Info: 0x%x, Cpus: %d, MailBox: 0x%x)\n", + (UINT32)(UINTN)VmmHobList, + (UINT32)(UINTN)Info, + mNumOfCpus, + (UINT32)(UINTN)mMailBox + )); + + ZeroMem (&PlatformInfoHob, sizeof (PlatformInfoHob)); + + // + // Validate HobList + // + if (ValidateHobList (VmmHobList) == FALSE) { + ASSERT (FALSE); + CpuDeadLoop (); + } + + // + // Process Hoblist for the TD + // + ProcessHobList (VmmHobList); + + // + // ProcessLibaryConstructorList + // + Function (NULL, NULL); + + // + // Tranfer the Hoblist to the final Hoblist for DXe + // + TransferHobList (VmmHobList); + + // + // Initialize Platform + // + TdvfPlatformInitialize (&PlatformInfoHob); + + // + // Get information needed to setup aps running in their + // run loop in allocated acpi reserved memory + // Add another page for mailbox + // + AsmGetRelocationMap (&RelocationMap); + RelocationPages = EFI_SIZE_TO_PAGES ((UINT32)RelocationMap.RelocateApLoopFuncSize) + 1; + + Address = AllocatePagesWithMemoryType (EfiACPIMemoryNVS, RelocationPages); + ApLoopFunc = (VOID *) ((UINTN) Address + EFI_PAGE_SIZE); + + CopyMem ( + ApLoopFunc, + RelocationMap.RelocateApLoopFuncAddress, + RelocationMap.RelocateApLoopFuncSize + ); + + DEBUG ((DEBUG_INFO, "Ap Relocation: mailbox %p, loop %p\n", + Address, ApLoopFunc)); + + // + // Initialize mailbox + // + RelocatedMailBox = (MP_WAKEUP_MAILBOX *)Address; + RelocatedMailBox->Command = MpProtectedModeWakeupCommandNoop; + RelocatedMailBox->ApicId = MP_CPU_PROTECTED_MODE_MAILBOX_APICID_INVALID; + RelocatedMailBox->WakeUpVector = 0; + + PlatformInfoHob.RelocatedMailBox = (UINT64)RelocatedMailBox; + + // + // Create and event log entry so VMM Hoblist can be measured + // + LogHobList (VmmHobList); + + // + // Wakup APs and have been move to the finalized run loop + // They will spin until guest OS wakes them + // + MpSerializeStart (); + + MpSendWakeupCommand ( + MpProtectedModeWakeupCommandWakeup, + (UINT64)ApLoopFunc, + (UINT64)RelocatedMailBox, + 0, + 0, + 0); + + // + // TDVF must not use any CpuHob from input HobList. + // It must create its own using GPWA from VMM and 0 for SizeOfIoSpace + // + BuildCpuHob (GET_GPAW_INIT_STATE(Info), 16); + + // + // SecFV + // + BootFv = (EFI_FIRMWARE_VOLUME_HEADER *)SecCoreData->BootFirmwareVolumeBase; + BuildFvHob ((UINTN)BootFv, BootFv->FvLength); + + // + // DxeFV + // + DxeCodeBase = PcdGet32 (PcdBfvBase); + DxeCodeSize = PcdGet32 (PcdBfvRawDataSize) - (UINT32)BootFv->FvLength; + BuildFvHob (DxeCodeBase, DxeCodeSize); + + DEBUG ((DEBUG_INFO, "SecFv : %p, 0x%x\n", BootFv, BootFv->FvLength)); + DEBUG ((DEBUG_INFO, "DxeFv : %x, 0x%x\n", DxeCodeBase, DxeCodeSize)); + + MeasureConfigurationVolume ((UINT64)(UINTN)PcdGet32 (PcdCfvBase)); + + PlatformInfoPtr = (UINT8*)BuildGuidDataHob (&gUefiOvmfPkgTdxPlatformGuid, &PlatformInfoHob, sizeof (EFI_HOB_PLATFORM_INFO)); + MeasureQemuCfgSystemSts (1, PlatformInfoPtr + sizeof(EFI_HOB_PLATFORM_INFO) - 6, 6); + + BuildStackHob ((UINTN)SecCoreData->StackBase, SecCoreData->StackSize <<=1 ); + + BuildResourceDescriptorHob ( + EFI_RESOURCE_SYSTEM_MEMORY, + EFI_RESOURCE_ATTRIBUTE_PRESENT | + EFI_RESOURCE_ATTRIBUTE_INITIALIZED | + EFI_RESOURCE_ATTRIBUTE_UNCACHEABLE | + EFI_RESOURCE_ATTRIBUTE_WRITE_COMBINEABLE | + EFI_RESOURCE_ATTRIBUTE_WRITE_THROUGH_CACHEABLE | + EFI_RESOURCE_ATTRIBUTE_WRITE_BACK_CACHEABLE | + EFI_RESOURCE_ATTRIBUTE_TESTED, + (UINT64)SecCoreData->TemporaryRamBase, + (UINT64)SecCoreData->TemporaryRamSize); + + BuildMemoryAllocationHob ( + FixedPcdGet32 (PcdTdMailboxBase), + EFI_PAGE_SIZE, + EfiACPIMemoryNVS + ); + + // + // Load the DXE Core and transfer control to it + // + Status = DxeLoadCore (1); + + ASSERT (FALSE); + CpuDeadLoop (); + +} + diff --git a/OvmfPkg/Library/TdxStartupLib/TdxStartupInternal.h b/OvmfPkg/Library/TdxStartupLib/TdxStartupInternal.h new file mode 100644 index 000000000000..9e2db4e0f3d3 --- /dev/null +++ b/OvmfPkg/Library/TdxStartupLib/TdxStartupInternal.h @@ -0,0 +1,191 @@ +#ifndef _TDX_STARTUP_INTERNAL_LIB__H_ +#define _TDX_STARTUP_INTERNAL_LIB__H_ + +#include +#include +#include +#include +#include +#include + +#define MP_CPU_PROTECTED_MODE_MAILBOX_APICID_INVALID 0xFFFFFFFF +#define MP_CPU_PROTECTED_MODE_MAILBOX_APICID_BROADCAST 0xFFFFFFFE + + typedef enum { + MpProtectedModeWakeupCommandNoop = 0, + MpProtectedModeWakeupCommandWakeup = 1, + MpProtectedModeWakeupCommandSleep = 2, + MpProtectedModeWakeupCommandAcceptPages = 3, + } MP_CPU_PROTECTED_MODE_WAKEUP_CMD; + +#pragma pack (1) + + // + // Describes the CPU MAILBOX control structure use to + // wakeup cpus spinning in long mode + // + typedef struct { + UINT16 Command; + UINT16 Resv; + UINT32 ApicId; + UINT64 WakeUpVector; + UINT8 ResvForOs[2032]; + // + // Arguments available for wakeup code + // + UINT64 WakeUpArgs1; + UINT64 WakeUpArgs2; + UINT64 WakeUpArgs3; + UINT64 WakeUpArgs4; + UINT8 Pad1[0xe0]; + UINT64 NumCpusArriving; + UINT8 Pad2[0xf8]; + UINT64 NumCpusExiting; + UINT32 Tallies[256]; + UINT8 Pad3[0x1f8]; + } MP_WAKEUP_MAILBOX; + + // + // AP relocation code information including code address and size, + // this structure will be shared be C code and assembly code. + // It is natural aligned by design. + // + typedef struct { + UINT8 *RelocateApLoopFuncAddress; + UINTN RelocateApLoopFuncSize; + } MP_RELOCATION_MAP; + +#define HANDOFF_TABLE_DESC "TdxTable" +typedef struct { + UINT8 TableDescriptionSize; + UINT8 TableDescription[sizeof (HANDOFF_TABLE_DESC)]; + UINT64 NumberOfTables; + EFI_CONFIGURATION_TABLE TableEntry[1]; +} TDX_HANDOFF_TABLE_POINTERS2; +#pragma pack() + +#define LOOPIT(X) do { \ + volatile int foo = (X); \ + while (foo) ; \ +} while(0) + + +volatile VOID * +EFIAPI +GetMailBox( + VOID + ); + +UINT32 +EFIAPI +GetNumCpus( + VOID + ); + +EFI_STATUS +EFIAPI +DxeLoadCore ( + IN INTN FvInstance + ); + +EFI_STATUS +EFIAPI +InitPcdPeim ( + IN INTN FvInstance + ); + +VOID +EFIAPI +MpSendWakeupCommand( + IN UINT16 Command, + IN UINT64 WakeupVector, + IN UINT64 WakeupArgs1, + IN UINT64 WakeupArgs2, + IN UINT64 WakeupArgs3, + IN UINT64 WakeupArgs4 +); + +VOID +EFIAPI +MpSerializeStart ( + VOID + ); + +VOID +EFIAPI +MpSerializeEnd ( + VOID + ); + +VOID +EFIAPI +MpAcceptMemoryResourceRange ( + IN EFI_PHYSICAL_ADDRESS PhysicalAddress, + IN EFI_PHYSICAL_ADDRESS PhysicalEnd + ); + +/** + Check the integrity of VMM Hob List. + + @param[in] VmmHobList - A pointer to Hob List + + @retval TRUE - The Hob List is valid. + @retval FALSE - The Hob List is invalid. + +**/ +BOOLEAN +EFIAPI +ValidateHobList ( + IN CONST VOID *VmmHobList + ); + + +VOID +EFIAPI +ProcessHobList ( + IN CONST VOID *HobStart + ); + +VOID +EFIAPI +TransferHobList ( + IN CONST VOID *HobStart + ); + +VOID +EFIAPI +LogHobList ( + IN CONST VOID *HobStart + ); + +EFI_STATUS +TdxMeasureFvImage ( + IN EFI_PHYSICAL_ADDRESS FvBase, + IN UINT64 FvLength, + IN UINT8 PcrIndex + ); + +EFI_STATUS +CreateTdxExtendEvent ( + IN TCG_PCRINDEX PCRIndex, + IN TCG_EVENTTYPE EventType, + IN UINT8 *EventData, + IN UINTN EventSize, + IN UINT8 *HashData, + IN UINTN HashDataLen + ); + +EFI_STATUS +MeasureQemuCfgSystemSts ( + IN TCG_PCRINDEX PCRIndex, + IN UINT8 *HashData, + IN UINTN HashDataLength + ); + +VOID +EFIAPI +AsmGetRelocationMap ( + OUT MP_RELOCATION_MAP *AddressMap + ); + +#endif diff --git a/OvmfPkg/Library/TdxStartupLib/TdxStartupLib.inf b/OvmfPkg/Library/TdxStartupLib/TdxStartupLib.inf new file mode 100644 index 000000000000..02eb185ee3b5 --- /dev/null +++ b/OvmfPkg/Library/TdxStartupLib/TdxStartupLib.inf @@ -0,0 +1,100 @@ +#/** @file +# Component description file for TDX Pre PI Library +# +# LIbrary helps you build a platform that skips PEI and loads DXE Core +# directly. Helps building HOBs, reading data from the FV, and doing +# decompression. +# +# Copyright (c) 2018, Intel Corporation. All rights reserved.
+# Copyright (c) 2008, Apple Inc. All rights reserved.
+# +# SPDX-License-Identifier: BSD-2-Clause-Patent +# +# +#**/ + +[Defines] + INF_VERSION = 0x00010005 + BASE_NAME = TdxStartupLib + FILE_GUID = 8FA74135-F841-40A4-86C8-69C923D2E85F + MODULE_TYPE = BASE + VERSION_STRING = 1.0 + LIBRARY_CLASS = TdxStartupLib|SEC + +# +# VALID_ARCHITECTURES = X64 +# + +[Sources] + TdxStartup.c + Hob.c + Mp.c + DxeLoad.c + Tcg.c + +[Sources.X64] + X64/ApRunLoop.nasm + X64/VirtualMemory.c + +[Packages] + MdePkg/MdePkg.dec + MdeModulePkg/MdeModulePkg.dec + UefiCpuPkg/UefiCpuPkg.dec + OvmfPkg/OvmfPkg.dec + CryptoPkg/CryptoPkg.dec + SecurityPkg/SecurityPkg.dec + +[LibraryClasses] + BaseLib + DebugLib + BaseMemoryLib + PcdLib + UefiCpuLib + DebugAgentLib + IoLib + LocalApicLib + CpuExceptionHandlerLib + SynchronizationLib + HobLib + TdxLib + MemoryAllocationLib + TdvfPlatformLib + PrePiLib + HashLib + +[Guids] + gEfiHobMemoryAllocModuleGuid + gEfiHobMemoryAllocStackGuid + gUefiOvmfPkgTdxPlatformGuid + gEfiMemoryTypeInformationGuid + gTcgEvent2EntryHobGuid + gPcdDataBaseHobGuid + +[Pcd] + gUefiOvmfPkgTokenSpaceGuid.PcdTdMailboxBase + gUefiOvmfPkgTokenSpaceGuid.PcdTdMailboxSize + gUefiOvmfPkgTokenSpaceGuid.PcdTdHobBase + gUefiOvmfPkgTokenSpaceGuid.PcdTdHobSize + gUefiOvmfPkgTokenSpaceGuid.PcdCfvBase + gUefiOvmfPkgTokenSpaceGuid.PcdCfvRawDataOffset + gUefiOvmfPkgTokenSpaceGuid.PcdCfvRawDataSize + gUefiOvmfPkgTokenSpaceGuid.PcdBfvBase + gUefiOvmfPkgTokenSpaceGuid.PcdBfvRawDataOffset + gUefiOvmfPkgTokenSpaceGuid.PcdBfvRawDataSize + gUefiOvmfPkgTokenSpaceGuid.PcdUseTdxEmulation + gUefiOvmfPkgTokenSpaceGuid.PcdTdxAcceptChunkSize + gUefiOvmfPkgTokenSpaceGuid.PcdTdxAcceptPageSize + gUefiOvmfPkgTokenSpaceGuid.PcdTdxSetNxForStack + gUefiOvmfPkgTokenSpaceGuid.PcdTdxPteMemoryEncryptionAddressOrMask + + // + // TODO check these PCDs' impact on Ovmf + // + gEfiMdeModulePkgTokenSpaceGuid.PcdSetNxForStack ## CONSUMES + gEfiMdeModulePkgTokenSpaceGuid.PcdDxeIplBuildPageTables ## CONSUMES + gEfiMdeModulePkgTokenSpaceGuid.PcdCpuStackGuard ## CONSUMES + gEfiMdeModulePkgTokenSpaceGuid.PcdUse1GPageTable ## SOMETIMES_CONSUMES + gEfiMdeModulePkgTokenSpaceGuid.PcdDxeNxMemoryProtectionPolicy ## SOMETIMES_CONSUMES + gEfiMdeModulePkgTokenSpaceGuid.PcdImageProtectionPolicy ## SOMETIMES_CONSUMES + gEfiMdeModulePkgTokenSpaceGuid.PcdPteMemoryEncryptionAddressOrMask ## CONSUMES + gEfiMdeModulePkgTokenSpaceGuid.PcdNullPointerDetectionPropertyMask ## CONSUMES diff --git a/OvmfPkg/Library/TdxStartupLib/TdxStartupLibNull.inf b/OvmfPkg/Library/TdxStartupLib/TdxStartupLibNull.inf new file mode 100644 index 000000000000..23600658f135 --- /dev/null +++ b/OvmfPkg/Library/TdxStartupLib/TdxStartupLibNull.inf @@ -0,0 +1,40 @@ +#/** @file +# Component description file for TDX Pre PI Library +# +# LIbrary helps you build a platform that skips PEI and loads DXE Core +# directly. Helps building HOBs, reading data from the FV, and doing +# decompression. +# +# Copyright (c) 2018, Intel Corporation. All rights reserved.
+# Copyright (c) 2008, Apple Inc. All rights reserved.
+# +# SPDX-License-Identifier: BSD-2-Clause-Patent +# +# +#**/ + +[Defines] + INF_VERSION = 0x00010005 + BASE_NAME = TdxStartupLibNull + FILE_GUID = 79DD2160-D5E0-48CD-AA87-479EACEE8393 + MODULE_TYPE = BASE + VERSION_STRING = 1.0 + LIBRARY_CLASS = TdxStartupLib|SEC + +# +# VALID_ARCHITECTURES = X64 +# + +[Sources] + TdxStartupNull.c + +[Packages] + MdePkg/MdePkg.dec + OvmfPkg/OvmfPkg.dec + +[LibraryClasses] + BaseLib + BaseMemoryLib + DebugLib + +[Pcd] diff --git a/OvmfPkg/Library/TdxStartupLib/TdxStartupNull.c b/OvmfPkg/Library/TdxStartupLib/TdxStartupNull.c new file mode 100644 index 000000000000..18b4e9ecdca0 --- /dev/null +++ b/OvmfPkg/Library/TdxStartupLib/TdxStartupNull.c @@ -0,0 +1,17 @@ +#include +#include +#include + +VOID +EFIAPI +TdxStartup( + IN VOID * Context, + IN VOID * VmmHobList, + IN UINTN Info, + IN fProcessLibraryConstructorList Function + ) +{ + ASSERT (FALSE); + CpuDeadLoop (); +} + diff --git a/OvmfPkg/Library/TdxStartupLib/X64/ApRunLoop.nasm b/OvmfPkg/Library/TdxStartupLib/X64/ApRunLoop.nasm new file mode 100644 index 000000000000..9cc5b7b340fa --- /dev/null +++ b/OvmfPkg/Library/TdxStartupLib/X64/ApRunLoop.nasm @@ -0,0 +1,88 @@ +;------------------------------------------------------------------------------ ; +; Copyright (c) 2015, Intel Corporation. All rights reserved.
+; SPDX-License-Identifier: BSD-2-Clause-Patent +; +; Module Name: +; +; ApRunLoop.nasm +; +; Abstract: +; +; This is the assembly code for run loop for APs in the guest TD +; +;------------------------------------------------------------------------------- + +%include "TdxCommondefs.inc" + +DEFAULT REL + +SECTION .text + +BITS 64 + +%macro tdcall 0 +%if (FixedPcdGet32 (PcdUseTdxEmulation) != 0) + vmcall +%else + db 0x66, 0x0f, 0x01, 0xcc +%endif +%endmacro + +; +; Relocated Ap Mailbox loop +; +; @param[in] RBX: Relocated mailbox address +; @param[in] RBP: vCpuId +; +; @return None This routine does not return +; +global ASM_PFX(AsmRelocateApMailBoxLoop) +ASM_PFX(AsmRelocateApMailBoxLoop): +AsmRelocateApMailBoxLoopStart: + + ; + ; TdCall[TDINFO] to get the vCpuId + ; + ;mov rax, 1 + ;tdcall + ; + ; R8 [31:0] NUM_VCPUS + ; [63:32] MAX_VCPUS + ; R9 [31:0] VCPU_INDEX + ; + + mov r8, rbp +MailBoxLoop: + ; Spin until command set + cmp dword [rbx + CommandOffset], MpProtectedModeWakeupCommandNoop + je MailBoxLoop + ; Determine if this is a broadcast or directly for my apic-id, if not, ignore + cmp dword [rbx + ApicidOffset], MailboxApicidBroadcast + je MailBoxProcessCommand + cmp dword [rbx + ApicidOffset], r8d + jne MailBoxLoop +MailBoxProcessCommand: + cmp dword [rbx + CommandOffset], MpProtectedModeWakeupCommandWakeup + je MailBoxWakeUp + cmp dword [rbx + CommandOffset], MpProtectedModeWakeupCommandSleep + je MailBoxSleep + ; Don't support this command, so ignore + jmp MailBoxLoop +MailBoxWakeUp: + mov rax, [rbx + WakeupVectorOffset] + jmp rax +MailBoxSleep: + jmp $ +BITS 64 +AsmRelocateApMailBoxLoopEnd: + +;------------------------------------------------------------------------------------- +; AsmGetRelocationMap (&RelocationMap); +;------------------------------------------------------------------------------------- +global ASM_PFX(AsmGetRelocationMap) +ASM_PFX(AsmGetRelocationMap): + lea rax, [ASM_PFX(AsmRelocateApMailBoxLoopStart)] + mov qword [rcx], rax + mov qword [rcx + 8h], AsmRelocateApMailBoxLoopEnd - AsmRelocateApMailBoxLoopStart + ret + diff --git a/OvmfPkg/Library/TdxStartupLib/X64/PageTables.h b/OvmfPkg/Library/TdxStartupLib/X64/PageTables.h new file mode 100644 index 000000000000..f1d1005c9a6b --- /dev/null +++ b/OvmfPkg/Library/TdxStartupLib/X64/PageTables.h @@ -0,0 +1,208 @@ +/** @file + x64 Long Mode Virtual Memory Management Definitions + + References: + 1) IA-32 Intel(R) Architecture Software Developer's Manual Volume 1:Basic Architecture, Intel + 2) IA-32 Intel(R) Architecture Software Developer's Manual Volume 2:Instruction Set Reference, Intel + 3) IA-32 Intel(R) Architecture Software Developer's Manual Volume 3:System Programmer's Guide, Intel + 4) AMD64 Architecture Programmer's Manual Volume 2: System Programming + +Copyright (c) 2006 - 2020, Intel Corporation. All rights reserved.
+Copyright (c) 2017, AMD Incorporated. All rights reserved.
+ +SPDX-License-Identifier: BSD-2-Clause-Patent + +**/ +#ifndef _PAGE_TABLES_H_ +#define _PAGE_TABLES_H_ + + +#define SYS_CODE64_SEL 0x38 + + +#pragma pack(1) + +typedef union { + struct { + UINT32 LimitLow : 16; + UINT32 BaseLow : 16; + UINT32 BaseMid : 8; + UINT32 Type : 4; + UINT32 System : 1; + UINT32 Dpl : 2; + UINT32 Present : 1; + UINT32 LimitHigh : 4; + UINT32 Software : 1; + UINT32 Reserved : 1; + UINT32 DefaultSize : 1; + UINT32 Granularity : 1; + UINT32 BaseHigh : 8; + } Bits; + UINT64 Uint64; +} IA32_GDT; + +typedef struct { + IA32_IDT_GATE_DESCRIPTOR Ia32IdtEntry; + UINT32 Offset32To63; + UINT32 Reserved; +} X64_IDT_GATE_DESCRIPTOR; + +// +// 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 CR0_WP BIT16 + +#define IA32_PG_P BIT0 +#define IA32_PG_RW BIT1 +#define IA32_PG_PS BIT7 + +#define PAGING_PAE_INDEX_MASK 0x1FF + +#define PAGING_4K_ADDRESS_MASK_64 0x000FFFFFFFFFF000ull +#define PAGING_2M_ADDRESS_MASK_64 0x000FFFFFFFE00000ull +#define PAGING_1G_ADDRESS_MASK_64 0x000FFFFFC0000000ull + +#define PAGING_L1_ADDRESS_SHIFT 12 +#define PAGING_L2_ADDRESS_SHIFT 21 +#define PAGING_L3_ADDRESS_SHIFT 30 +#define PAGING_L4_ADDRESS_SHIFT 39 + +#define PAGING_PML4E_NUMBER 4 + +#define PAGE_TABLE_POOL_ALIGNMENT BASE_2MB +#define PAGE_TABLE_POOL_UNIT_SIZE SIZE_2MB +#define PAGE_TABLE_POOL_UNIT_PAGES EFI_SIZE_TO_PAGES (PAGE_TABLE_POOL_UNIT_SIZE) +#define PAGE_TABLE_POOL_ALIGN_MASK \ + (~(EFI_PHYSICAL_ADDRESS)(PAGE_TABLE_POOL_ALIGNMENT - 1)) + +typedef struct { + VOID *NextPool; + UINTN Offset; + UINTN FreePages; +} PAGE_TABLE_POOL; + +UINTN +CreateIdentityMappingPageTables ( + IN EFI_PHYSICAL_ADDRESS StackBase, + IN UINTN StackSize + ); + +/** + Clear legacy memory located at the first 4K-page. + + This function traverses the whole HOB list to check if memory from 0 to 4095 + exists and has not been allocated, and then clear it if so. + + @param HobStart The start of HobList passed to DxeCore. + +**/ +VOID +ClearFirst4KPage ( + IN VOID *HobStart + ); + +/** + Return configure status of NULL pointer detection feature. + + @return TRUE NULL pointer detection feature is enabled + @return FALSE NULL pointer detection feature is disabled +**/ +BOOLEAN +IsNullDetectionEnabled ( + VOID + ); + + +#endif diff --git a/OvmfPkg/Library/TdxStartupLib/X64/VirtualMemory.c b/OvmfPkg/Library/TdxStartupLib/X64/VirtualMemory.c new file mode 100644 index 000000000000..ebdaa85b7c23 --- /dev/null +++ b/OvmfPkg/Library/TdxStartupLib/X64/VirtualMemory.c @@ -0,0 +1,896 @@ +/** @file + x64-specifc functionality for Page Table Setup. + +Copyright (c) 2006 - 2020, Intel Corporation. All rights reserved.
+SPDX-License-Identifier: BSD-2-Clause-Patent +**/ + + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "PageTables.h" + +// +// Global variable to keep track current available memory used as page table. +// +PAGE_TABLE_POOL *mPageTablePool = NULL; + +UINTN mLevelShift[5] = { + 0, + PAGING_L1_ADDRESS_SHIFT, + PAGING_L2_ADDRESS_SHIFT, + PAGING_L3_ADDRESS_SHIFT, + PAGING_L4_ADDRESS_SHIFT +}; + +UINT64 mLevelMask[5] = { + 0, + PAGING_4K_ADDRESS_MASK_64, + PAGING_2M_ADDRESS_MASK_64, + PAGING_1G_ADDRESS_MASK_64, + PAGING_1G_ADDRESS_MASK_64 +}; + +UINT64 mLevelSize[5] = { + 0, + SIZE_4KB, + SIZE_2MB, + SIZE_1GB, + SIZE_512GB +}; + +/** + Clear legacy memory located at the first 4K-page, if available. + + This function traverses the whole HOB list to check if memory from 0 to 4095 + exists and has not been allocated, and then clear it if so. + + @param HobStart The start of HobList passed to DxeCore. + +**/ +VOID +ClearFirst4KPage ( + IN VOID *HobStart + ) +{ + EFI_PEI_HOB_POINTERS RscHob; + EFI_PEI_HOB_POINTERS MemHob; + BOOLEAN DoClear; + + RscHob.Raw = HobStart; + MemHob.Raw = HobStart; + DoClear = FALSE; + + // + // Check if page 0 exists and free + // + while ((RscHob.Raw = GetNextHob (EFI_HOB_TYPE_RESOURCE_DESCRIPTOR, + RscHob.Raw)) != NULL) { + if (RscHob.ResourceDescriptor->ResourceType == EFI_RESOURCE_SYSTEM_MEMORY && + RscHob.ResourceDescriptor->PhysicalStart == 0) { + DoClear = TRUE; + // + // Make sure memory at 0-4095 has not been allocated. + // + while ((MemHob.Raw = GetNextHob (EFI_HOB_TYPE_MEMORY_ALLOCATION, + MemHob.Raw)) != NULL) { + if (MemHob.MemoryAllocation->AllocDescriptor.MemoryBaseAddress + < EFI_PAGE_SIZE) { + DoClear = FALSE; + break; + } + MemHob.Raw = GET_NEXT_HOB (MemHob); + } + break; + } + RscHob.Raw = GET_NEXT_HOB (RscHob); + } + + if (DoClear) { + DEBUG ((DEBUG_INFO, "Clearing first 4K-page!\r\n")); + SetMem (NULL, EFI_PAGE_SIZE, 0); + } + + return; +} + +/** + Return configure status of NULL pointer detection feature. + + @return TRUE NULL pointer detection feature is enabled + @return FALSE NULL pointer detection feature is disabled + +**/ +BOOLEAN +IsNullDetectionEnabled ( + VOID + ) +{ + return ((PcdGet8 (PcdNullPointerDetectionPropertyMask) & BIT0) != 0); +} + +/** + The function will check if Execute Disable Bit is available. + + @retval TRUE Execute Disable Bit is available. + @retval FALSE Execute Disable Bit is not available. + +**/ +BOOLEAN +IsExecuteDisableBitAvailable ( + VOID + ) +{ + UINT32 RegEax; + UINT32 RegEdx; + BOOLEAN Available; + + Available = FALSE; + AsmCpuid (0x80000000, &RegEax, NULL, NULL, NULL); + if (RegEax >= 0x80000001) { + AsmCpuid (0x80000001, NULL, NULL, NULL, &RegEdx); + if ((RegEdx & BIT20) != 0) { + // + // Bit 20: Execute Disable Bit available. + // + Available = TRUE; + } + } + + return Available; +} + +/** + Check if Execute Disable Bit (IA32_EFER.NXE) should be enabled or not. + + @retval TRUE IA32_EFER.NXE should be enabled. + @retval FALSE IA32_EFER.NXE should not be enabled. + +**/ +BOOLEAN +IsEnableNonExecNeeded ( + VOID + ) +{ + + if (!IsExecuteDisableBitAvailable ()) { + return FALSE; + } + + // + // XD flag (BIT63) in page table entry is only valid if IA32_EFER.NXE is set. + // Features controlled by Following PCDs need this feature to be enabled. + // + return (FixedPcdGetBool(PcdTdxSetNxForStack) || + FixedPcdGet64 (PcdDxeNxMemoryProtectionPolicy) != 0 || + PcdGet32 (PcdImageProtectionPolicy) != 0); +} + +/** + Enable Execute Disable Bit. + +**/ +VOID +EnableExecuteDisableBit ( + VOID + ) +{ + UINT64 MsrRegisters; + + MsrRegisters = AsmReadMsr64 (0xC0000080); + MsrRegisters |= BIT11; + AsmWriteMsr64 (0xC0000080, MsrRegisters); +} + +/** + The function will check if page table entry should be splitted to smaller + granularity. + + @param Address Physical memory address. + @param Size Size of the given physical memory. + @param StackBase Base address of stack. + @param StackSize Size of stack. + + @retval TRUE Page table should be split. + @retval FALSE Page table should not be split. +**/ +BOOLEAN +ToSplitPageTable ( + IN EFI_PHYSICAL_ADDRESS Address, + IN UINTN Size, + IN EFI_PHYSICAL_ADDRESS StackBase, + IN UINTN StackSize + ) +{ + if (IsNullDetectionEnabled () && Address == 0) { + return TRUE; + } + + if (FixedPcdGetBool (PcdCpuStackGuard)) { + if (StackBase >= Address && StackBase < (Address + Size)) { + return TRUE; + } + } + + if (FixedPcdGetBool(PcdTdxSetNxForStack)) { + if ((Address < StackBase + StackSize) && ((Address + Size) > StackBase)) { + return TRUE; + } + } + + return FALSE; +} +/** + Initialize a buffer pool for page table use only. + + To reduce the potential split operation on page table, the pages reserved for + page table should be allocated in the times of PAGE_TABLE_POOL_UNIT_PAGES and + at the boundary of PAGE_TABLE_POOL_ALIGNMENT. So the page pool is always + initialized with number of pages greater than or equal to the given PoolPages. + + Once the pages in the pool are used up, this method should be called again to + reserve at least another PAGE_TABLE_POOL_UNIT_PAGES. But usually this won't + happen in practice. + + @param PoolPages The least page number of the pool to be created. + + @retval TRUE The pool is initialized successfully. + @retval FALSE The memory is out of resource. +**/ +BOOLEAN +InitializePageTablePool ( + IN UINTN PoolPages + ) +{ + VOID *Buffer; + + DEBUG((DEBUG_INFO, "InitializePageTablePool PoolPages=%d\n", PoolPages)); + + // + // Always reserve at least PAGE_TABLE_POOL_UNIT_PAGES, including one page for + // header. + // + PoolPages += 1; // Add one page for header. + PoolPages = ((PoolPages - 1) / PAGE_TABLE_POOL_UNIT_PAGES + 1) * + PAGE_TABLE_POOL_UNIT_PAGES; + Buffer = AllocateAlignedPages (PoolPages, PAGE_TABLE_POOL_ALIGNMENT); + if (Buffer == NULL) { + DEBUG ((DEBUG_ERROR, "ERROR: Out of aligned pages\r\n")); + return FALSE; + } + + // + // Link all pools into a list for easier track later. + // + if (mPageTablePool == NULL) { + mPageTablePool = Buffer; + mPageTablePool->NextPool = mPageTablePool; + } else { + ((PAGE_TABLE_POOL *)Buffer)->NextPool = mPageTablePool->NextPool; + mPageTablePool->NextPool = Buffer; + mPageTablePool = Buffer; + } + + // + // Reserve one page for pool header. + // + mPageTablePool->FreePages = PoolPages - 1; + mPageTablePool->Offset = EFI_PAGES_TO_SIZE (1); + + return TRUE; +} + +/** + This API provides a way to allocate memory for page table. + + This API can be called more than once to allocate memory for page tables. + + Allocates the number of 4KB pages and returns a pointer to the allocated + buffer. The buffer returned is aligned on a 4KB boundary. + + If Pages is 0, then NULL is returned. + If there is not enough memory remaining to satisfy the request, then NULL is + returned. + + @param Pages The number of 4 KB pages to allocate. + + @return A pointer to the allocated buffer or NULL if allocation fails. + +**/ +VOID * +AllocatePageTableMemory ( + IN UINTN Pages + ) +{ + VOID *Buffer; + + if (Pages == 0) { + return NULL; + } + + DEBUG((DEBUG_INFO, "AllocatePageTableMemory. mPageTablePool=%p, Pages=%d\n", mPageTablePool, Pages)); + // + // Renew the pool if necessary. + // + if (mPageTablePool == NULL || + Pages > mPageTablePool->FreePages) { + if (!InitializePageTablePool (Pages)) { + return NULL; + } + } + + Buffer = (UINT8 *)mPageTablePool + mPageTablePool->Offset; + + mPageTablePool->Offset += EFI_PAGES_TO_SIZE (Pages); + mPageTablePool->FreePages -= Pages; + + DEBUG (( + DEBUG_INFO, + "%a:%a: Buffer=0x%Lx Pages=%ld\n", + gEfiCallerBaseName, + __FUNCTION__, + Buffer, + Pages + )); + + + return Buffer; +} + +/** + 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. + +**/ +VOID +Split2MPageTo4K ( + IN EFI_PHYSICAL_ADDRESS PhysicalAddress, + IN OUT UINT64 *PageEntry2M, + IN EFI_PHYSICAL_ADDRESS StackBase, + IN UINTN StackSize + ) +{ + EFI_PHYSICAL_ADDRESS PhysicalAddress4K; + UINTN IndexOfPageTableEntries; + PAGE_TABLE_4K_ENTRY *PageTableEntry; + UINT64 AddressEncMask; + + DEBUG((DEBUG_INFO, "Split2MPageTo4K\n")); + // + // Make sure AddressEncMask is contained to smallest supported address field + // + AddressEncMask = 0; + + PageTableEntry = AllocatePageTableMemory (1); + ASSERT (PageTableEntry != NULL); + + // + // Fill in 2M page entry. + // + *PageEntry2M = (UINT64) (UINTN) PageTableEntry | AddressEncMask | IA32_PG_P | IA32_PG_RW; + + 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; + + if ((IsNullDetectionEnabled () && PhysicalAddress4K == 0) || + (FixedPcdGetBool (PcdCpuStackGuard) && PhysicalAddress4K == StackBase)) { + PageTableEntry->Bits.Present = 0; + } else { + PageTableEntry->Bits.Present = 1; + } + + if (FixedPcdGetBool(PcdTdxSetNxForStack) + && (PhysicalAddress4K >= StackBase) + && (PhysicalAddress4K < StackBase + StackSize)) { + // + // Set Nx bit for stack. + // + PageTableEntry->Bits.Nx = 1; + } + } +} + +/** + 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. + +**/ +VOID +Split1GPageTo2M ( + IN EFI_PHYSICAL_ADDRESS PhysicalAddress, + IN OUT UINT64 *PageEntry1G, + IN EFI_PHYSICAL_ADDRESS StackBase, + IN UINTN StackSize + ) +{ + EFI_PHYSICAL_ADDRESS PhysicalAddress2M; + UINTN IndexOfPageDirectoryEntries; + PAGE_TABLE_ENTRY *PageDirectoryEntry; + UINT64 AddressEncMask; + + // + // Make sure AddressEncMask is contained to smallest supported address field + // + AddressEncMask = FixedPcdGet64 (PcdTdxPteMemoryEncryptionAddressOrMask) & PAGING_1G_ADDRESS_MASK_64; + + PageDirectoryEntry = AllocatePageTableMemory (1); + ASSERT (PageDirectoryEntry != NULL); + + // + // Fill in 1G page entry. + // + *PageEntry1G = (UINT64) (UINTN) PageDirectoryEntry | AddressEncMask | IA32_PG_P | IA32_PG_RW; + + PhysicalAddress2M = PhysicalAddress; + for (IndexOfPageDirectoryEntries = 0; IndexOfPageDirectoryEntries < 512; IndexOfPageDirectoryEntries++, PageDirectoryEntry++, PhysicalAddress2M += SIZE_2MB) { + if (ToSplitPageTable (PhysicalAddress2M, SIZE_2MB, StackBase, StackSize)) { + // + // Need to split this 2M page that covers NULL or 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 one page of page table pool memory to be read-only. + + @param[in] PageTableBase Base address of page table (CR3). + @param[in] Address Start address of a page to be set as read-only. + @param[in] Level4Paging Level 4 paging flag. + +**/ +VOID +SetPageTablePoolReadOnly ( + IN UINTN PageTableBase, + IN EFI_PHYSICAL_ADDRESS Address, + IN BOOLEAN Level4Paging + ) +{ + UINTN Index; + UINTN EntryIndex; + UINT64 AddressEncMask; + EFI_PHYSICAL_ADDRESS PhysicalAddress; + UINT64 *PageTable; + UINT64 *NewPageTable; + UINT64 PageAttr; + UINTN Level; + UINT64 PoolUnitSize; + + ASSERT (PageTableBase != 0); + + // + // Since the page table is always from page table pool, which is always + // located at the boundary of PcdPageTablePoolAlignment, we just need to + // set the whole pool unit to be read-only. + // + Address = Address & PAGE_TABLE_POOL_ALIGN_MASK; + + AddressEncMask = FixedPcdGet64 (PcdTdxPteMemoryEncryptionAddressOrMask) & + PAGING_1G_ADDRESS_MASK_64; + PageTable = (UINT64 *)(UINTN)PageTableBase; + PoolUnitSize = PAGE_TABLE_POOL_UNIT_SIZE; + + for (Level = (Level4Paging) ? 4 : 3; Level > 0; --Level) { + Index = ((UINTN)RShiftU64 (Address, mLevelShift[Level])); + Index &= PAGING_PAE_INDEX_MASK; + + PageAttr = PageTable[Index]; + if ((PageAttr & IA32_PG_PS) == 0) { + // + // Go to next level of table. + // + PageTable = (UINT64 *)(UINTN)(PageAttr & ~AddressEncMask & + PAGING_4K_ADDRESS_MASK_64); + continue; + } + + if (PoolUnitSize >= mLevelSize[Level]) { + // + // Clear R/W bit if current page granularity is not larger than pool unit + // size. + // + if ((PageAttr & IA32_PG_RW) != 0) { + while (PoolUnitSize > 0) { + // + // PAGE_TABLE_POOL_UNIT_SIZE and PAGE_TABLE_POOL_ALIGNMENT are fit in + // one page (2MB). Then we don't need to update attributes for pages + // crossing page directory. ASSERT below is for that purpose. + // + ASSERT (Index < EFI_PAGE_SIZE/sizeof (UINT64)); + + PageTable[Index] &= ~(UINT64)IA32_PG_RW; + PoolUnitSize -= mLevelSize[Level]; + + ++Index; + } + } + + break; + + } else { + // + // The smaller granularity of page must be needed. + // + ASSERT (Level > 1); + + NewPageTable = AllocatePageTableMemory (1); + ASSERT (NewPageTable != NULL); + + PhysicalAddress = PageAttr & mLevelMask[Level]; + for (EntryIndex = 0; + EntryIndex < EFI_PAGE_SIZE/sizeof (UINT64); + ++EntryIndex) { + NewPageTable[EntryIndex] = PhysicalAddress | AddressEncMask | + IA32_PG_P | IA32_PG_RW; + if (Level > 2) { + NewPageTable[EntryIndex] |= IA32_PG_PS; + } + PhysicalAddress += mLevelSize[Level - 1]; + } + + PageTable[Index] = (UINT64)(UINTN)NewPageTable | AddressEncMask | + IA32_PG_P | IA32_PG_RW; + PageTable = NewPageTable; + } + } +} + +/** + Prevent the memory pages used for page table from been overwritten. + + @param[in] PageTableBase Base address of page table (CR3). + @param[in] Level4Paging Level 4 paging flag. + +**/ +VOID +EnablePageTableProtection ( + IN UINTN PageTableBase, + IN BOOLEAN Level4Paging + ) +{ + PAGE_TABLE_POOL *HeadPool; + PAGE_TABLE_POOL *Pool; + UINT64 PoolSize; + EFI_PHYSICAL_ADDRESS Address; + + DEBUG((DEBUG_INFO, "EnablePageTableProtection\n")); + + if (mPageTablePool == NULL) { + return; + } + + // + // Disable write protection, because we need to mark page table to be write + // protected. + // + AsmWriteCr0 (AsmReadCr0() & ~CR0_WP); + + // + // SetPageTablePoolReadOnly might update mPageTablePool. It's safer to + // remember original one in advance. + // + HeadPool = mPageTablePool; + Pool = HeadPool; + do { + Address = (EFI_PHYSICAL_ADDRESS)(UINTN)Pool; + PoolSize = Pool->Offset + EFI_PAGES_TO_SIZE (Pool->FreePages); + + // + // The size of one pool must be multiple of PAGE_TABLE_POOL_UNIT_SIZE, which + // is one of page size of the processor (2MB by default). Let's apply the + // protection to them one by one. + // + while (PoolSize > 0) { + SetPageTablePoolReadOnly(PageTableBase, Address, Level4Paging); + Address += PAGE_TABLE_POOL_UNIT_SIZE; + PoolSize -= PAGE_TABLE_POOL_UNIT_SIZE; + } + + Pool = Pool->NextPool; + } while (Pool != HeadPool); + + // + // Enable write protection, after page table attribute updated. + // + AsmWriteCr0 (AsmReadCr0() | CR0_WP); +} + +/** + Allocates and fills in the Page Directory and Page Table Entries to + establish a 1:1 Virtual to Physical mapping. + + @param[in] StackBase Stack base address. + @param[in] StackSize Stack size. + + @return The address of 4 level page map. + +**/ +UINTN +CreateIdentityMappingPageTables ( + IN EFI_PHYSICAL_ADDRESS StackBase, + IN UINTN StackSize + ) +{ + UINT32 RegEax; + UINT32 RegEdx; + UINT8 PhysicalAddressBits; + EFI_PHYSICAL_ADDRESS PageAddress; + UINTN IndexOfPml5Entries; + UINTN IndexOfPml4Entries; + UINTN IndexOfPdpEntries; + UINTN IndexOfPageDirectoryEntries; + UINT32 NumberOfPml5EntriesNeeded; + UINT32 NumberOfPml4EntriesNeeded; + UINT32 NumberOfPdpEntriesNeeded; + PAGE_MAP_AND_DIRECTORY_POINTER *PageMapLevel5Entry; + PAGE_MAP_AND_DIRECTORY_POINTER *PageMapLevel4Entry; + PAGE_MAP_AND_DIRECTORY_POINTER *PageMap; + PAGE_MAP_AND_DIRECTORY_POINTER *PageDirectoryPointerEntry; + PAGE_TABLE_ENTRY *PageDirectoryEntry; + UINTN TotalPagesNum; + UINTN BigPageAddress; + VOID *Hob; + BOOLEAN Page5LevelSupport; + BOOLEAN Page1GSupport; + PAGE_TABLE_1G_ENTRY *PageDirectory1GEntry; + UINT64 AddressEncMask; + IA32_CR4 Cr4; + + // + // Set PageMapLevel5Entry to suppress incorrect compiler/analyzer warnings + // + PageMapLevel5Entry = NULL; + + // + // Make sure AddressEncMask is contained to smallest supported address field + // + AddressEncMask = FixedPcdGet64 (PcdTdxPteMemoryEncryptionAddressOrMask) & PAGING_1G_ADDRESS_MASK_64; + + Page1GSupport = FALSE; + if (FixedPcdGetBool(PcdUse1GPageTable)) { + AsmCpuid (0x80000000, &RegEax, NULL, NULL, NULL); + if (RegEax >= 0x80000001) { + AsmCpuid (0x80000001, NULL, NULL, NULL, &RegEdx); + if ((RegEdx & BIT26) != 0) { + Page1GSupport = TRUE; + } + } + } + + // + // Get physical address bits supported. + // + Hob = GetFirstHob (EFI_HOB_TYPE_CPU); + ASSERT(Hob != NULL); + PhysicalAddressBits = ((EFI_HOB_CPU *) Hob)->SizeOfMemorySpace; + + // + // CPU will already have LA57 enabled so just check CR4 + Cr4.UintN = AsmReadCr4 (); + Page5LevelSupport = (Cr4.Bits.LA57 ? TRUE : FALSE); + + DEBUG ((DEBUG_INFO, "AddressBits=%u 5LevelPaging=%u 1GPage=%u AddressEncMask=0x%llx\n", + PhysicalAddressBits, Page5LevelSupport, + Page1GSupport, AddressEncMask)); + + // + // Calculate the table entries needed. + // + NumberOfPml5EntriesNeeded = 1; + if (PhysicalAddressBits > 48) { + NumberOfPml5EntriesNeeded = (UINT32) LShiftU64 (1, PhysicalAddressBits - 48); + PhysicalAddressBits = 48; + } + + NumberOfPml4EntriesNeeded = 1; + if (PhysicalAddressBits > 39) { + NumberOfPml4EntriesNeeded = (UINT32) LShiftU64 (1, PhysicalAddressBits - 39); + PhysicalAddressBits = 39; + } + + NumberOfPdpEntriesNeeded = 1; + ASSERT (PhysicalAddressBits > 30); + NumberOfPdpEntriesNeeded = (UINT32) LShiftU64 (1, PhysicalAddressBits - 30); + + // + // Pre-allocate big pages to avoid later allocations. + // + if (!Page1GSupport) { + TotalPagesNum = ((NumberOfPdpEntriesNeeded + 1) * NumberOfPml4EntriesNeeded + 1) * NumberOfPml5EntriesNeeded + 1; + } else { + TotalPagesNum = (NumberOfPml4EntriesNeeded + 1) * NumberOfPml5EntriesNeeded + 1; + } + + // + // Substract the one page occupied by PML5 entries if 5-Level Paging is disabled. + // + if (!Page5LevelSupport) { + TotalPagesNum--; + } + + DEBUG ((DEBUG_INFO, "Pml5=%u Pml4=%u Pdp=%u TotalPage=%Lu\n", + NumberOfPml5EntriesNeeded, NumberOfPml4EntriesNeeded, + NumberOfPdpEntriesNeeded, (UINT64)TotalPagesNum)); + + BigPageAddress = (UINTN) AllocatePageTableMemory (TotalPagesNum); + ASSERT (BigPageAddress != 0); + + DEBUG((DEBUG_INFO, "BigPageAddress = 0x%llx\n", BigPageAddress)); + + // + // By architecture only one PageMapLevel4 exists - so lets allocate storage for it. + // + PageMap = (VOID *) BigPageAddress; + if (Page5LevelSupport) { + // + // By architecture only one PageMapLevel5 exists - so lets allocate storage for it. + // + PageMapLevel5Entry = PageMap; + BigPageAddress += SIZE_4KB; + } + PageAddress = 0; + + for ( IndexOfPml5Entries = 0 + ; IndexOfPml5Entries < NumberOfPml5EntriesNeeded + ; IndexOfPml5Entries++) { + + // + // Each PML5 entry points to a page of PML4 entires. + // So lets allocate space for them and fill them in in the IndexOfPml4Entries loop. + // When 5-Level Paging is disabled, below allocation happens only once. + // + PageMapLevel4Entry = (VOID *) BigPageAddress; + BigPageAddress += SIZE_4KB; + + if (Page5LevelSupport) { + // + // Make a PML5 Entry + // + PageMapLevel5Entry->Uint64 = (UINT64) (UINTN) PageMapLevel4Entry | AddressEncMask; + PageMapLevel5Entry->Bits.ReadWrite = 1; + PageMapLevel5Entry->Bits.Present = 1; + PageMapLevel5Entry++; + } + + for ( IndexOfPml4Entries = 0 + ; IndexOfPml4Entries < (NumberOfPml5EntriesNeeded == 1 ? NumberOfPml4EntriesNeeded : 512) + ; IndexOfPml4Entries++, PageMapLevel4Entry++) { + // + // Each PML4 entry points to a page of Page Directory Pointer entires. + // So lets allocate space for them and fill them in in the IndexOfPdpEntries loop. + // + PageDirectoryPointerEntry = (VOID *) BigPageAddress; + BigPageAddress += SIZE_4KB; + + // + // Make a PML4 Entry + // + PageMapLevel4Entry->Uint64 = (UINT64)(UINTN)PageDirectoryPointerEntry | AddressEncMask; + PageMapLevel4Entry->Bits.ReadWrite = 1; + PageMapLevel4Entry->Bits.Present = 1; + + if (Page1GSupport) { + PageDirectory1GEntry = (VOID *) PageDirectoryPointerEntry; + + for (IndexOfPageDirectoryEntries = 0; IndexOfPageDirectoryEntries < 512; IndexOfPageDirectoryEntries++, PageDirectory1GEntry++, PageAddress += SIZE_1GB) { + if (ToSplitPageTable (PageAddress, SIZE_1GB, StackBase, StackSize)) { + Split1GPageTo2M (PageAddress, (UINT64 *) PageDirectory1GEntry, + StackBase, StackSize); + } else { + // + // Fill in the Page Directory entries + // + PageDirectory1GEntry->Uint64 = (UINT64)PageAddress | AddressEncMask; + PageDirectory1GEntry->Bits.ReadWrite = 1; + PageDirectory1GEntry->Bits.Present = 1; + PageDirectory1GEntry->Bits.MustBe1 = 1; + } + } + } else { + for ( IndexOfPdpEntries = 0 + ; IndexOfPdpEntries < (NumberOfPml4EntriesNeeded == 1 ? NumberOfPdpEntriesNeeded : 512) + ; IndexOfPdpEntries++, PageDirectoryPointerEntry++) { + // + // Each Directory Pointer entries points to a page of Page Directory entires. + // So allocate space for them and fill them in in the IndexOfPageDirectoryEntries loop. + // + PageDirectoryEntry = (VOID *) BigPageAddress; + BigPageAddress += SIZE_4KB; + + // + // Fill in a Page Directory Pointer Entries + // + PageDirectoryPointerEntry->Uint64 = (UINT64)(UINTN)PageDirectoryEntry | AddressEncMask; + PageDirectoryPointerEntry->Bits.ReadWrite = 1; + PageDirectoryPointerEntry->Bits.Present = 1; + + for (IndexOfPageDirectoryEntries = 0; IndexOfPageDirectoryEntries < 512; IndexOfPageDirectoryEntries++, PageDirectoryEntry++, PageAddress += SIZE_2MB) { + if (ToSplitPageTable (PageAddress, SIZE_2MB, StackBase, StackSize)) { + // + // Need to split this 2M page that covers NULL or stack range. + // + Split2MPageTo4K (PageAddress, (UINT64 *) PageDirectoryEntry, StackBase, StackSize); + } else { + // + // Fill in the Page Directory entries + // + PageDirectoryEntry->Uint64 = (UINT64)PageAddress | AddressEncMask; + PageDirectoryEntry->Bits.ReadWrite = 1; + PageDirectoryEntry->Bits.Present = 1; + PageDirectoryEntry->Bits.MustBe1 = 1; + } + } + } + + // + // Fill with null entry for unused PDPTE + // + ZeroMem (PageDirectoryPointerEntry, (512 - IndexOfPdpEntries) * sizeof(PAGE_MAP_AND_DIRECTORY_POINTER)); + } + } + + // + // For the PML4 entries we are not using fill in a null entry. + // + ZeroMem (PageMapLevel4Entry, (512 - IndexOfPml4Entries) * sizeof (PAGE_MAP_AND_DIRECTORY_POINTER)); + } + + if (Page5LevelSupport) { + // + // For the PML5 entries we are not using fill in a null entry. + // + ZeroMem (PageMapLevel5Entry, (512 - IndexOfPml5Entries) * sizeof (PAGE_MAP_AND_DIRECTORY_POINTER)); + } + + // + // Protect the page table by marking the memory used for page table to be + // read-only. + // + EnablePageTableProtection ((UINTN)PageMap, TRUE); + + // + // Set IA32_EFER.NXE if necessary. + // + if (IsEnableNonExecNeeded ()) { + // + // ASSERT for now, TDX doesn't allow us to change EFER + // + ASSERT(FALSE); + EnableExecuteDisableBit (); + } + + return (UINTN)PageMap; +} diff --git a/OvmfPkg/Library/VmTdExitLib/VmTdExitLib.inf b/OvmfPkg/Library/VmTdExitLib/VmTdExitLib.inf new file mode 100644 index 000000000000..f475d492c6bc --- /dev/null +++ b/OvmfPkg/Library/VmTdExitLib/VmTdExitLib.inf @@ -0,0 +1,41 @@ +## @file +# VMTDEXIT Support Library. +# +# Copyright (c) 2020, Intel Inc. All rights reserved.
+# Copyright (C) 2020, Advanced Micro Devices, Inc. All rights reserved.
+# SPDX-License-Identifier: BSD-2-Clause-Patent +# +## + +[Defines] + INF_VERSION = 0x00010005 + BASE_NAME = VmTdExitLib + FILE_GUID = b29eabb0-f9a3-11ea-8b6e-0800200c9a66 + MODULE_TYPE = BASE + VERSION_STRING = 1.0 + LIBRARY_CLASS = VmTdExitLib + +# +# The following information is for reference only and not required by the build tools. +# +# VALID_ARCHITECTURES = X64 +# + +[Sources.common] + VmTdExitVeHandler.c + +[Packages] + MdePkg/MdePkg.dec + OvmfPkg/OvmfPkg.dec + UefiCpuPkg/UefiCpuPkg.dec + +[LibraryClasses] + BaseLib + BaseMemoryLib + DebugLib + TdxLib + +[Pcd] + gUefiOvmfPkgTokenSpaceGuid.PcdIgnoreVeHalt + + diff --git a/OvmfPkg/Library/VmTdExitLib/VmTdExitVeHandler.c b/OvmfPkg/Library/VmTdExitLib/VmTdExitVeHandler.c new file mode 100644 index 000000000000..43e4384cdb6a --- /dev/null +++ b/OvmfPkg/Library/VmTdExitLib/VmTdExitVeHandler.c @@ -0,0 +1,505 @@ +#include +#include +#include +#include +#include +#include +#include + +typedef union { + struct { + UINT32 Eax; + UINT32 Edx; + } Regs; + UINT64 Val; +} MSR_DATA; + +typedef union { + UINT8 Val; + struct { + UINT8 B:1; + UINT8 X:1; + UINT8 R:1; + UINT8 W:1; + } Bits; +} REX; + +typedef union { + UINT8 Val; + struct { + UINT8 Rm:3; + UINT8 Reg:3; + UINT8 Mod:2; + } Bits; +} MODRM; + +typedef struct { + UINT64 Regs[4]; +} CPUID_DATA; + +/** + Handle an CPUID event. + + Use the TDVMCALL instruction to handle cpuid #ve + + @param[in, out] Regs x64 processor context + @param[in] Veinfo VE Info + + @retval 0 Event handled successfully + @return New exception value to propagate +**/ +STATIC +UINT64 +EFIAPI +CpuIdExit( + IN EFI_SYSTEM_CONTEXT_X64 *Regs, + IN TDCALL_VEINFO_RETURN_DATA *Veinfo + ) +{ + CPUID_DATA CpuIdData; + UINT64 Status; + + Status = TdVmCallCpuid(Regs->Rax, Regs->Rcx, &CpuIdData); + + if (Status == 0) { + Regs->Rax = CpuIdData.Regs[0]; + Regs->Rbx = CpuIdData.Regs[1]; + Regs->Rcx = CpuIdData.Regs[2]; + Regs->Rdx = CpuIdData.Regs[3]; + } + + return Status; +} + +/** + Handle an IO event. + + Use the TDVMCALL instruction to handle either an IO read or an IO write. + + @param[in, out] Regs x64 processor context + @param[in] Veinfo VE Info + + @retval 0 Event handled successfully + @return New exception value to propagate +**/ +STATIC +UINT64 +EFIAPI +IoExit( + IN OUT EFI_SYSTEM_CONTEXT_X64 *Regs, + IN TDCALL_VEINFO_RETURN_DATA *Veinfo + ) +{ + BOOLEAN Write; + UINTN Size; + UINTN Port; + UINT64 Val; + UINT64 RepCnt; + UINT64 Status; + + Val = 0; + Write = Veinfo->ExitQualification.Io.Direction ? FALSE : TRUE; + Size = Veinfo->ExitQualification.Io.Size + 1; + Port = Veinfo->ExitQualification.Io.Port; + + if (Veinfo->ExitQualification.Io.String) { + // + // If REP is set, get rep-cnt from Rcx + // + RepCnt = Veinfo->ExitQualification.Io.Rep ? Regs->Rcx : 1; + + while (RepCnt) { + Val = 0; + if (Write == TRUE) { + CopyMem (&Val, (VOID *) Regs->Rsi, Size); + } + Regs->Rsi += Size; + Status = TdVmCall(EXIT_REASON_IO_INSTRUCTION, Size, Write, Port, Val, (Write ? NULL : &Val)); + if (Status != 0) { + break; + } + if (Write == FALSE) { + CopyMem ((VOID *) Regs->Rdi, &Val, Size); + } + Regs->Rdi += Size; + if (Veinfo->ExitQualification.Io.Rep) { + Regs->Rcx -= 1; + } + RepCnt -= 1; + } + } else { + if (Write == TRUE) { + CopyMem (&Val, (VOID *) &Regs->Rax, Size); + } + Status = TdVmCall(EXIT_REASON_IO_INSTRUCTION, Size, Write, Port, Val, (Write ? NULL : &Val)); + if ((Status == 0) && (Write == FALSE)) { + CopyMem ((VOID *) &Regs->Rax, &Val, Size); + } + } + return Status; +} + +/** + Handle an READ MSR event. + + Use the TDVMCALL instruction to handle msr read + + @param[in, out] Regs x64 processor context + @param[in] Veinfo VE Info + + @retval 0 Event handled successfully + @return New exception value to propagate +**/ +STATIC +UINT64 +ReadMsrExit( + IN OUT EFI_SYSTEM_CONTEXT_X64 *Regs, + IN TDCALL_VEINFO_RETURN_DATA *Veinfo + ) +{ + MSR_DATA Data; + UINT64 Status; + + Status = TdVmCall(EXIT_REASON_MSR_READ, Regs->Rcx, 0, 0, 0, &Data); + if (Status == 0) { + Regs->Rax = Data.Regs.Eax; + Regs->Rdx = Data.Regs.Edx; + } + + return Status; +} + +/** + Handle an WRITE MSR event. + + Use the TDVMCALL instruction to handle msr write + + @param[in, out] Regs x64 processor context + @param[in] Veinfo VE Info + + @retval 0 Event handled successfully + @return New exception value to propagate +**/ +STATIC +UINT64 +WriteMsrExit( + IN OUT EFI_SYSTEM_CONTEXT_X64 *Regs, + IN TDCALL_VEINFO_RETURN_DATA *Veinfo + ) +{ + UINT64 Status; + MSR_DATA Data; + + Data.Regs.Eax = (UINT32)Regs->Rax; + Data.Regs.Edx = (UINT32)Regs->Rdx; + + Status = TdVmCall(EXIT_REASON_MSR_WRITE, Regs->Rcx, Data.Val, 0, 0, NULL); + + return Status; +} + +STATIC +VOID +EFIAPI +TdxDecodeInstruction( + IN UINT8 *Rip +) +{ + UINTN i; + DEBUG ((EFI_D_INFO,"TDX: #TD[EPT] instruction (%p):", Rip)); + for (i = 0; i < 15; i++){ + DEBUG ((EFI_D_INFO, "%02x:", Rip[i])); + } + DEBUG ((EFI_D_INFO, "\n")); +} + +#define TDX_DECODER_BUG_ON(x) \ + if ((x)) { \ + TdxDecodeInstruction(Rip); \ + TdVmCall(TDVMCALL_HALT, 0, 0, 0, 0, 0); \ + } + +STATIC +UINT64 * +EFIAPI +GetRegFromContext( + IN EFI_SYSTEM_CONTEXT_X64 *Regs, + IN UINTN RegIndex +) +{ + switch(RegIndex) { + case 0: return &Regs->Rax; break; + case 1: return &Regs->Rcx; break; + case 2: return &Regs->Rdx; break; + case 3: return &Regs->Rbx; break; + case 4: return &Regs->Rsp; break; + case 5: return &Regs->Rbp; break; + case 6: return &Regs->Rsi; break; + case 7: return &Regs->Rdi; break; + case 8: return &Regs->R8; break; + case 9: return &Regs->R9; break; + case 10: return &Regs->R10; break; + case 11: return &Regs->R11; break; + case 12: return &Regs->R12; break; + case 13: return &Regs->R13; break; + case 14: return &Regs->R14; break; + case 15: return &Regs->R15; break; + } + return NULL; +} + +/** + Handle an MMIO event. + + Use the TDVMCALL instruction to handle either an mmio read or an mmio write. + + @param[in, out] Regs x64 processor context + @param[in] Veinfo VE Info + + @retval 0 Event handled successfully + @return New exception value to propagate +**/ +STATIC +INTN +EFIAPI +MmioExit( + IN OUT EFI_SYSTEM_CONTEXT_X64 *Regs, + IN TDCALL_VEINFO_RETURN_DATA *Veinfo + ) +{ + UINT64 Status; + UINT32 MmioSize; + UINT32 RegSize;; + UINT8 OpCode; + BOOLEAN SeenRex; + UINT64 *Reg; + UINT8 *Rip; + UINT64 Val; + UINT32 OpSize; + MODRM ModRm; + REX Rex; + + Rip = (UINT8 *)Regs->Rip; + Val = 0; + Rex.Val = 0; + SeenRex = FALSE; + + // + // Default to 32bit transfer + // + OpSize = 4; + + do { + OpCode = *Rip++; + if (OpCode == 0x66) { + OpSize = 2; + } else if (OpCode == 0x64 || OpCode == 0x65 || OpCode == 0x67) { + continue; + } else if (OpCode >= 0x40 && OpCode <= 0x4f) { + SeenRex = TRUE; + Rex.Val = OpCode; + } else { + break; + } + } while (TRUE); + + // + // We need to have at least 2 more bytes for this instruction + // + TDX_DECODER_BUG_ON(((UINT64)Rip - Regs->Rip) > 13); + + OpCode = *Rip++; + // + // Two-byte opecode, get next byte + // + if (OpCode == 0x0F) { + OpCode = *Rip++; + } + + switch (OpCode) { + case 0x88: + case 0x8A: + case 0xB6: + MmioSize = 1; + break; + case 0xB7: + MmioSize = 2; + break; + default: + MmioSize = Rex.Bits.W ? 8 : OpSize; + break; + } + + /* Punt on AH/BH/CH/DH unless it shows up. */ + ModRm.Val = *Rip++; + TDX_DECODER_BUG_ON(MmioSize == 1 && ModRm.Bits.Reg > 4 && !SeenRex && OpCode != 0xB6); + Reg = GetRegFromContext(Regs, ModRm.Bits.Reg | ((int)Rex.Bits.R << 3)); + TDX_DECODER_BUG_ON(!Reg); + + if (ModRm.Bits.Rm == 4) + ++Rip; /* SIB byte */ + + if (ModRm.Bits.Mod == 2 || (ModRm.Bits.Mod == 0 && ModRm.Bits.Rm == 5)) + Rip += 4; /* DISP32 */ + else if (ModRm.Bits.Mod == 1) + ++Rip; /* DISP8 */ + + switch (OpCode) { + case 0x88: + case 0x89: + CopyMem((void *)&Val, Reg, MmioSize); + Status = TdVmCall(TDVMCALL_MMIO, MmioSize, 1, Veinfo->GuestPA, Val, 0); + break; + case 0xC7: + CopyMem((void *)&Val, Rip, OpSize); + Status = TdVmCall(TDVMCALL_MMIO, MmioSize, 1, Veinfo->GuestPA, Val, 0); + Rip += OpSize; + default: + // + // 32-bit write registers are zero extended to the full register + // Hence 'MOVZX r[32/64], r/m16' is + // hardcoded to reg size 8, and the straight MOV case has a reg + // size of 8 in the 32-bit read case. + // + switch (OpCode) { + case 0xB6: + RegSize = Rex.Bits.W ? 8 : OpSize; + break; + case 0xB7: + RegSize = 8; + break; + default: + RegSize = MmioSize == 4 ? 8 : MmioSize; + break; + } + + Status = TdVmCall(TDVMCALL_MMIO, MmioSize, 0, Veinfo->GuestPA, 0, &Val); + if (Status == 0) { + ZeroMem(Reg, RegSize); + CopyMem(Reg, (void *)&Val, MmioSize); + } + } + + if (Status == 0) { + TDX_DECODER_BUG_ON(((UINT64)Rip - Regs->Rip) > 15); + + // + // We change instruction length to reflect true size so handler can + // bump rip + // + Veinfo->ExitInstructionLength = (UINT32)((UINT64)Rip - Regs->Rip); + } + + return Status; +} + +/** + Handle a #VE exception. + + Performs the necessary processing to handle a #VE exception. + + @param[in, out] ExceptionType Pointer to an EFI_EXCEPTION_TYPE to be set + as value to use on error. + @param[in, out] SystemContext Pointer to EFI_SYSTEM_CONTEXT + + @retval EFI_SUCCESS Exception handled + @retval EFI_UNSUPPORTED #VE not supported, (new) exception value to + propagate provided + @retval EFI_PROTOCOL_ERROR #VE handling failed, (new) exception value to + propagate provided + +**/ +EFI_STATUS +EFIAPI +VmTdExitHandleVe ( + IN OUT EFI_EXCEPTION_TYPE *ExceptionType, + IN OUT EFI_SYSTEM_CONTEXT SystemContext + ) +{ + UINT64 Status; + TD_RETURN_DATA ReturnData; + EFI_SYSTEM_CONTEXT_X64 *Regs; + + Regs = SystemContext.SystemContextX64; + Status = TdCall(TDCALL_TDGETVEINFO, 0, 0, 0, &ReturnData); + ASSERT(Status == 0); + if (Status != 0) { + DEBUG((DEBUG_ERROR, "#VE happened. TDGETVEINFO failed with Status = 0x%llx\n", Status)); + TdVmCall(TDVMCALL_HALT, 0, 0, 0, 0, 0); + } + + switch (ReturnData.VeInfo.ExitReason) { + case EXIT_REASON_CPUID: + Status = CpuIdExit(Regs, &ReturnData.VeInfo); + DEBUG ((DEBUG_VERBOSE , + "CPUID #VE happened, ExitReasion is %d, ExitQualification = 0x%x.\n", + ReturnData.VeInfo.ExitReason, ReturnData.VeInfo.ExitQualification.Val + )); + break; + + case EXIT_REASON_HLT: + if (FixedPcdGetBool(PcdIgnoreVeHalt) == FALSE) { + Status = TdVmCall(EXIT_REASON_HLT, 0, 0, 0, 0, 0); + } + break; + + case EXIT_REASON_IO_INSTRUCTION: + Status = IoExit(Regs, &ReturnData.VeInfo); + DEBUG ((DEBUG_VERBOSE , + "IO_Instruction #VE happened, ExitReasion is %d, ExitQualification = 0x%x.\n", + ReturnData.VeInfo.ExitReason, ReturnData.VeInfo.ExitQualification.Val + )); + break; + + case EXIT_REASON_MSR_READ: + Status = ReadMsrExit(Regs, &ReturnData.VeInfo); + DEBUG ((DEBUG_VERBOSE , + "RDMSR #VE happened, ExitReasion is %d, ExitQualification = 0x%x. Regs->Rcx=0x%llx, Status = 0x%llx\n", + ReturnData.VeInfo.ExitReason, ReturnData.VeInfo.ExitQualification.Val, Regs->Rcx, Status + )); + break; + + case EXIT_REASON_MSR_WRITE: + Status = WriteMsrExit(Regs, &ReturnData.VeInfo); + DEBUG ((DEBUG_VERBOSE , + "WRMSR #VE happened, ExitReasion is %d, ExitQualification = 0x%x. Regs->Rcx=0x%llx, Status = 0x%llx\n", + ReturnData.VeInfo.ExitReason, ReturnData.VeInfo.ExitQualification.Val, Regs->Rcx, Status + )); + break; + + case EXIT_REASON_EPT_VIOLATION: + Status = MmioExit(Regs, &ReturnData.VeInfo); + DEBUG ((DEBUG_VERBOSE , + "MMIO #VE happened, ExitReasion is %d, ExitQualification = 0x%x.\n", + ReturnData.VeInfo.ExitReason, ReturnData.VeInfo.ExitQualification.Val + )); + break; + + case EXIT_REASON_VMCALL: + case EXIT_REASON_MWAIT_INSTRUCTION: + case EXIT_REASON_MONITOR_INSTRUCTION: + case EXIT_REASON_WBINVD: + case EXIT_REASON_RDPMC: + /* Handle as nops. */ + break; + + default: + DEBUG ((DEBUG_ERROR, + "Unsupported #VE happened, ExitReason is %d, ExitQualification = 0x%x.\n", + ReturnData.VeInfo.ExitReason, ReturnData.VeInfo.ExitQualification.Val + )); + + ASSERT(FALSE); + CpuDeadLoop(); + } + if (Status) { + DEBUG ((DEBUG_ERROR, + "#VE Error (0x%llx) returned from host, ExitReason is %d, ExitQualification = 0x%x.\n", + Status, ReturnData.VeInfo.ExitReason, ReturnData.VeInfo.ExitQualification.Val + )); + + TdVmCall(TDVMCALL_HALT, 0, 0, 0, 0, 0); + } + SystemContext.SystemContextX64->Rip += ReturnData.VeInfo.ExitInstructionLength; + return EFI_SUCCESS; +} diff --git a/OvmfPkg/LocalApicTimerDxe/LocalApicTimer.c b/OvmfPkg/LocalApicTimerDxe/LocalApicTimer.c new file mode 100644 index 000000000000..0ad97fb8306d --- /dev/null +++ b/OvmfPkg/LocalApicTimerDxe/LocalApicTimer.c @@ -0,0 +1,489 @@ +/** @file + Timer Architectural Protocol module using Local APIC Timer + + Copyright (c) 2011 - 2021, Intel Corporation. All rights reserved.
+ SPDX-License-Identifier: BSD-2-Clause-Patent + +**/ + +#include + +#include +#include + +#include +#include +#include +#include +#include +#include + +#include + + +/** + This function registers the handler NotifyFunction so it is called every time + the timer interrupt fires. It also passes the amount of time since the last + handler call to the NotifyFunction. If NotifyFunction is NULL, then the + handler is unregistered. If the handler is registered, then EFI_SUCCESS is + returned. If the CPU does not support registering a timer interrupt handler, + then EFI_UNSUPPORTED is returned. If an attempt is made to register a handler + when a handler is already registered, then EFI_ALREADY_STARTED is returned. + If an attempt is made to unregister a handler when a handler is not registered, + then EFI_INVALID_PARAMETER is returned. If an error occurs attempting to + register the NotifyFunction with the timer interrupt, then EFI_DEVICE_ERROR + is returned. + + @param This The EFI_TIMER_ARCH_PROTOCOL instance. + @param NotifyFunction The function to call when a timer interrupt fires. + This function executes at TPL_HIGH_LEVEL. The DXE + Core will register a handler for the timer interrupt, + so it can know how much time has passed. This + information is used to signal timer based events. + NULL will unregister the handler. + + @retval EFI_SUCCESS The timer handler was registered. + @retval EFI_UNSUPPORTED The platform does not support timer interrupts. + @retval EFI_ALREADY_STARTED NotifyFunction is not NULL, and a handler is already + registered. + @retval EFI_INVALID_PARAMETER NotifyFunction is NULL, and a handler was not + previously registered. + @retval EFI_DEVICE_ERROR The timer handler could not be registered. + +**/ +EFI_STATUS +EFIAPI +TimerDriverRegisterHandler ( + IN EFI_TIMER_ARCH_PROTOCOL *This, + IN EFI_TIMER_NOTIFY NotifyFunction + ); + +/** + This function adjusts the period of timer interrupts to the value specified + by TimerPeriod. If the timer period is updated, then the selected timer + period is stored in EFI_TIMER.TimerPeriod, and EFI_SUCCESS is returned. If + the timer hardware is not programmable, then EFI_UNSUPPORTED is returned. + If an error occurs while attempting to update the timer period, then the + timer hardware will be put back in its state prior to this call, and + EFI_DEVICE_ERROR is returned. If TimerPeriod is 0, then the timer interrupt + is disabled. This is not the same as disabling the CPU's interrupts. + Instead, it must either turn off the timer hardware, or it must adjust the + interrupt controller so that a CPU interrupt is not generated when the timer + interrupt fires. + + @param This The EFI_TIMER_ARCH_PROTOCOL instance. + @param TimerPeriod The rate to program the timer interrupt in 100 nS units. + If the timer hardware is not programmable, then + EFI_UNSUPPORTED is returned. If the timer is programmable, + then the timer period will be rounded up to the nearest + timer period that is supported by the timer hardware. + If TimerPeriod is set to 0, then the timer interrupts + will be disabled. + + @retval EFI_SUCCESS The timer period was changed. + @retval EFI_UNSUPPORTED The platform cannot change the period of the timer interrupt. + @retval EFI_DEVICE_ERROR The timer period could not be changed due to a device error. + +**/ +EFI_STATUS +EFIAPI +TimerDriverSetTimerPeriod ( + IN EFI_TIMER_ARCH_PROTOCOL *This, + IN UINT64 TimerPeriod + ); + +/** + This function retrieves the period of timer interrupts in 100 ns units, + returns that value in TimerPeriod, and returns EFI_SUCCESS. If TimerPeriod + is NULL, then EFI_INVALID_PARAMETER is returned. If a TimerPeriod of 0 is + returned, then the timer is currently disabled. + + @param This The EFI_TIMER_ARCH_PROTOCOL instance. + @param TimerPeriod A pointer to the timer period to retrieve in 100 ns units. + If 0 is returned, then the timer is currently disabled. + + @retval EFI_SUCCESS The timer period was returned in TimerPeriod. + @retval EFI_INVALID_PARAMETER TimerPeriod is NULL. + +**/ +EFI_STATUS +EFIAPI +TimerDriverGetTimerPeriod ( + IN EFI_TIMER_ARCH_PROTOCOL *This, + OUT UINT64 *TimerPeriod + ); + +/** + This function generates a soft timer interrupt. If the platform does not support soft + timer interrupts, then EFI_UNSUPPORTED is returned. Otherwise, EFI_SUCCESS is returned. + If a handler has been registered through the EFI_TIMER_ARCH_PROTOCOL.RegisterHandler() + service, then a soft timer interrupt will be generated. If the timer interrupt is + enabled when this service is called, then the registered handler will be invoked. The + registered handler should not be able to distinguish a hardware-generated timer + interrupt from a software-generated timer interrupt. + + @param This The EFI_TIMER_ARCH_PROTOCOL instance. + + @retval EFI_SUCCESS The soft timer interrupt was generated. + @retval EFI_UNSUPPORTED The platform does not support the generation of soft + timer interrupts. + +**/ +EFI_STATUS +EFIAPI +TimerDriverGenerateSoftInterrupt ( + IN EFI_TIMER_ARCH_PROTOCOL *This + ); + +/// +/// The handle onto which the Timer Architectural Protocol will be installed. +/// +EFI_HANDLE mTimerHandle = NULL; + +/// +/// The Timer Architectural Protocol that this driver produces. +/// +EFI_TIMER_ARCH_PROTOCOL mTimer = { + TimerDriverRegisterHandler, + TimerDriverSetTimerPeriod, + TimerDriverGetTimerPeriod, + TimerDriverGenerateSoftInterrupt +}; + +/// +/// Pointer to the CPU Architectural Protocol instance. +/// +EFI_CPU_ARCH_PROTOCOL *mCpu = NULL; + +/// +/// The notification function to call on every timer interrupt. +/// +EFI_TIMER_NOTIFY mTimerNotifyFunction = NULL; + +/// +/// The current period of the Local APIC timer interrupt in 100 ns units. +/// +UINT64 mTimerPeriod = 0; + +/// +/// Counts the number of Local APIC Timer interrupts processed by this driver. +/// Only required for debug. +/// +volatile UINTN mNumTicks; + +/** + The interrupt handler for the Local APIC timer. This handler clears the Local + APIC interrupt and computes the amount of time that has passed since the last + Local APIC timer interrupt. If a notification function is registered, then + the amount of time since the last Local APIC timer interrupt is passed to that + notification function in 100 ns units. The Local APIC timer is updated to + generate another interrupt in the required time period. + + @param InterruptType The type of interrupt that occurred. + @param SystemContext A pointer to the system context when the interrupt occurred. +**/ +VOID +EFIAPI +TimerInterruptHandler ( + IN EFI_EXCEPTION_TYPE InterruptType, + IN EFI_SYSTEM_CONTEXT SystemContext + ) +{ + + EFI_TPL OriginalTPL; + + OriginalTPL = gBS->RaiseTPL (TPL_HIGH_LEVEL); + + // + // Count number of ticks + // + DEBUG_CODE (mNumTicks++;); + + // + // Check to see if there is a registered notification function + // + if (mTimerNotifyFunction != NULL) { + mTimerNotifyFunction (mTimerPeriod); + } + + gBS->RestoreTPL (OriginalTPL); + + DisableInterrupts (); + + SendApicEoi(); +} + +/** + This function registers the handler NotifyFunction so it is called every time + the timer interrupt fires. It also passes the amount of time since the last + handler call to the NotifyFunction. If NotifyFunction is NULL, then the + handler is unregistered. If the handler is registered, then EFI_SUCCESS is + returned. If the CPU does not support registering a timer interrupt handler, + then EFI_UNSUPPORTED is returned. If an attempt is made to register a handler + when a handler is already registered, then EFI_ALREADY_STARTED is returned. + If an attempt is made to unregister a handler when a handler is not registered, + then EFI_INVALID_PARAMETER is returned. If an error occurs attempting to + register the NotifyFunction with the timer interrupt, then EFI_DEVICE_ERROR + is returned. + + @param This The EFI_TIMER_ARCH_PROTOCOL instance. + @param NotifyFunction The function to call when a timer interrupt fires. + This function executes at TPL_HIGH_LEVEL. The DXE + Core will register a handler for the timer interrupt, + so it can know how much time has passed. This + information is used to signal timer based events. + NULL will unregister the handler. + + @retval EFI_SUCCESS The timer handler was registered. + @retval EFI_UNSUPPORTED The platform does not support timer interrupts. + @retval EFI_ALREADY_STARTED NotifyFunction is not NULL, and a handler is already + registered. + @retval EFI_INVALID_PARAMETER NotifyFunction is NULL, and a handler was not + previously registered. + @retval EFI_DEVICE_ERROR The timer handler could not be registered. + +**/ +EFI_STATUS +EFIAPI +TimerDriverRegisterHandler ( + IN EFI_TIMER_ARCH_PROTOCOL *This, + IN EFI_TIMER_NOTIFY NotifyFunction + ) +{ + // + // Check for invalid parameters + // + if (NotifyFunction == NULL && mTimerNotifyFunction == NULL) { + return EFI_INVALID_PARAMETER; + } + if (NotifyFunction != NULL && mTimerNotifyFunction != NULL) { + return EFI_ALREADY_STARTED; + } + + // + // Cache the registered notification function + // + mTimerNotifyFunction = NotifyFunction; + + return EFI_SUCCESS; +} + +/** + This function adjusts the period of timer interrupts to the value specified + by TimerPeriod. If the timer period is updated, then the selected timer + period is stored in EFI_TIMER.TimerPeriod, and EFI_SUCCESS is returned. If + the timer hardware is not programmable, then EFI_UNSUPPORTED is returned. + If an error occurs while attempting to update the timer period, then the + timer hardware will be put back in its state prior to this call, and + EFI_DEVICE_ERROR is returned. If TimerPeriod is 0, then the timer interrupt + is disabled. This is not the same as disabling the CPU's interrupts. + Instead, it must either turn off the timer hardware, or it must adjust the + interrupt controller so that a CPU interrupt is not generated when the timer + interrupt fires. + + @param This The EFI_TIMER_ARCH_PROTOCOL instance. + @param TimerPeriod The rate to program the timer interrupt in 100 nS units. + If the timer hardware is not programmable, then + EFI_UNSUPPORTED is returned. If the timer is programmable, + then the timer period will be rounded up to the nearest + timer period that is supported by the timer hardware. + If TimerPeriod is set to 0, then the timer interrupts + will be disabled. + + @retval EFI_SUCCESS The timer period was changed. + @retval EFI_UNSUPPORTED The platform cannot change the period of the timer interrupt. + @retval EFI_DEVICE_ERROR The timer period could not be changed due to a device error. + +**/ +EFI_STATUS +EFIAPI +TimerDriverSetTimerPeriod ( + IN EFI_TIMER_ARCH_PROTOCOL *This, + IN UINT64 TimerPeriod + ) +{ + EFI_TPL Tpl; + UINTN Divisor; + UINT64 TimerCount; + + // + // Disable interrupts + // + Tpl = gBS->RaiseTPL (TPL_HIGH_LEVEL); + + if (TimerPeriod == 0) { + // + // Disable timer interrupt for a TimerPeriod of 0 + // + DisableApicTimerInterrupt (); + } else { + DisableApicTimerInterrupt (); + + // + // Convert TimerPeriod in 100ns units to Local APIC Timer ticks. + // + GetApicTimerState (&Divisor, NULL, NULL); + TimerCount = DivU64x32 ( + MultU64x32 (TimerPeriod, PcdGet32(PcdFSBClock)), + (UINT32)Divisor * 10000000 + ); + + // + // Program the local APIC timer + // + InitializeApicTimer (0, (UINT32)TimerCount, TRUE, PcdGet8 (PcdHpetLocalApicVector)); + + EnableApicTimerInterrupt (); + } + + // + // Save the new timer period + // + mTimerPeriod = TimerPeriod; + + // + // Restore interrupts + // + gBS->RestoreTPL (Tpl); + + return EFI_SUCCESS; +} + +/** + This function retrieves the period of timer interrupts in 100 ns units, + returns that value in TimerPeriod, and returns EFI_SUCCESS. If TimerPeriod + is NULL, then EFI_INVALID_PARAMETER is returned. If a TimerPeriod of 0 is + returned, then the timer is currently disabled. + + @param This The EFI_TIMER_ARCH_PROTOCOL instance. + @param TimerPeriod A pointer to the timer period to retrieve in 100 ns units. + If 0 is returned, then the timer is currently disabled. + + @retval EFI_SUCCESS The timer period was returned in TimerPeriod. + @retval EFI_INVALID_PARAMETER TimerPeriod is NULL. + +**/ +EFI_STATUS +EFIAPI +TimerDriverGetTimerPeriod ( + IN EFI_TIMER_ARCH_PROTOCOL *This, + OUT UINT64 *TimerPeriod + ) +{ + if (TimerPeriod == NULL) { + return EFI_INVALID_PARAMETER; + } + + *TimerPeriod = mTimerPeriod; + + return EFI_SUCCESS; +} + +/** + This function generates a soft timer interrupt. If the platform does not support soft + timer interrupts, then EFI_UNSUPPORTED is returned. Otherwise, EFI_SUCCESS is returned. + If a handler has been registered through the EFI_TIMER_ARCH_PROTOCOL.RegisterHandler() + service, then a soft timer interrupt will be generated. If the timer interrupt is + enabled when this service is called, then the registered handler will be invoked. The + registered handler should not be able to distinguish a hardware-generated timer + interrupt from a software-generated timer interrupt. + + @param This The EFI_TIMER_ARCH_PROTOCOL instance. + + @retval EFI_SUCCESS The soft timer interrupt was generated. + @retval EFI_UNSUPPORTED The platform does not support the generation of soft + timer interrupts. + +**/ +EFI_STATUS +EFIAPI +TimerDriverGenerateSoftInterrupt ( + IN EFI_TIMER_ARCH_PROTOCOL *This + ) +{ + return EFI_UNSUPPORTED; +} + +/** + Initialize the Timer Architectural Protocol driver + + @param ImageHandle ImageHandle of the loaded driver + @param SystemTable Pointer to the System Table + + @retval EFI_SUCCESS Timer Architectural Protocol created + @retval EFI_OUT_OF_RESOURCES Not enough resources available to initialize driver. + @retval EFI_DEVICE_ERROR A device error occurred attempting to initialize the driver. + +**/ +EFI_STATUS +EFIAPI +TimerDriverInitialize ( + IN EFI_HANDLE ImageHandle, + IN EFI_SYSTEM_TABLE *SystemTable + ) +{ + EFI_STATUS Status; + + if (!ProbeTdGuest ()) { + return EFI_UNSUPPORTED; + } + + // + // Make sure the Timer Architectural Protocol is not already installed in the system + // + ASSERT_PROTOCOL_ALREADY_INSTALLED (NULL, &gEfiTimerArchProtocolGuid); + + // + // Find the CPU architectural protocol. + // + Status = gBS->LocateProtocol (&gEfiCpuArchProtocolGuid, NULL, (VOID **) &mCpu); + ASSERT_EFI_ERROR (Status); + + // + // Install interrupt handler for the Local APIC Timer + // + Status = mCpu->RegisterInterruptHandler (mCpu, PcdGet8 (PcdHpetLocalApicVector), TimerInterruptHandler); + ASSERT_EFI_ERROR (Status); + if (EFI_ERROR (Status)) { + DEBUG ((DEBUG_ERROR, "Unable to register Local APIC interrupt with CPU Arch Protocol. Unload Local APIC timer driver.\n")); + return EFI_DEVICE_ERROR; + } + + // + // Force the Local APIC timer to be disabled while setting everything up + // + DisableApicTimerInterrupt (); + InitializeApicTimer (0, 0, FALSE, PcdGet8 (PcdHpetLocalApicVector)); + + // + // Force the Local APIC Timer to be enabled at its default period + // + Status = TimerDriverSetTimerPeriod (&mTimer, PcdGet64 (PcdHpetDefaultTimerPeriod)); + ASSERT_EFI_ERROR (Status); + if (EFI_ERROR (Status)) { + DEBUG ((DEBUG_ERROR, "Unable to set Local APIC default timer rate. Unload Local APIC timer driver.\n")); + return EFI_DEVICE_ERROR; + } + + // + // Show state of enabled timer + // + DEBUG_CODE ( + // + // Wait for a few timer interrupts to fire before continuing + // + while (mNumTicks < 10); + ); + + // + // Install the Timer Architectural Protocol onto a new handle + // + Status = gBS->InstallMultipleProtocolInterfaces ( + &mTimerHandle, + &gEfiTimerArchProtocolGuid, &mTimer, + NULL + ); + ASSERT_EFI_ERROR (Status); + + return Status; +} diff --git a/OvmfPkg/LocalApicTimerDxe/LocalApicTimerDxe.inf b/OvmfPkg/LocalApicTimerDxe/LocalApicTimerDxe.inf new file mode 100644 index 000000000000..8bbd6d467410 --- /dev/null +++ b/OvmfPkg/LocalApicTimerDxe/LocalApicTimerDxe.inf @@ -0,0 +1,53 @@ +## @file +# Timer Architectural Protocol module using Local APIC Timer +# +# Copyright (c) 2011 - 2020, Intel Corporation. All rights reserved.
+# SPDX-License-Identifier: BSD-2-Clause-Patent +# +## + +[Defines] + INF_VERSION = 0x00010005 + BASE_NAME = LocalApicTimerDxe + MODULE_UNI_FILE = LocalApicTimerDxe.uni + FILE_GUID = 74EB4D00-E63E-11EA-8B6E-0800200C9A66 + MODULE_TYPE = DXE_DRIVER + VERSION_STRING = 1.0 + ENTRY_POINT = TimerDriverInitialize + +# +# The following information is for reference only and not required by the build tools. +# +# VALID_ARCHITECTURES = X64 +# +# + +[Sources] + LocalApicTimer.c + +[Packages] + MdePkg/MdePkg.dec + UefiCpuPkg/UefiCpuPkg.dec + PcAtChipsetPkg/PcAtChipsetPkg.dec + OvmfPkg/OvmfPkg.dec + +[LibraryClasses] + PcdLib + DebugLib + UefiDriverEntryPoint + UefiBootServicesTableLib + BaseLib + LocalApicLib + TdxProbeLib + +[Protocols] + gEfiTimerArchProtocolGuid ## PRODUCES + gEfiCpuArchProtocolGuid ## CONSUMES + +[Pcd] + gPcAtChipsetPkgTokenSpaceGuid.PcdHpetLocalApicVector ## CONSUMES + gPcAtChipsetPkgTokenSpaceGuid.PcdHpetDefaultTimerPeriod ## CONSUMES + gEfiMdePkgTokenSpaceGuid.PcdFSBClock + +[Depex] + gEfiCpuArchProtocolGuid diff --git a/OvmfPkg/LocalApicTimerDxe/LocalApicTimerDxe.uni b/OvmfPkg/LocalApicTimerDxe/LocalApicTimerDxe.uni new file mode 100644 index 000000000000..59f411a60f21 --- /dev/null +++ b/OvmfPkg/LocalApicTimerDxe/LocalApicTimerDxe.uni @@ -0,0 +1,14 @@ +// /** @file +// Timer Architectural Protocol module using Local APIC Timer +// +// Copyright (c) 2011 - 2020, Intel Corporation. All rights reserved.
+// +// SPDX-License-Identifier: BSD-2-Clause-Patent +// +// **/ + + +#string STR_MODULE_ABSTRACT #language en-US "Timer Architectural Protocol module using Local APIC Timer" + +#string STR_MODULE_DESCRIPTION #language en-US "Timer Architectural Protocol module using Local APIC Timer." + diff --git a/OvmfPkg/OvmfPkg.dec b/OvmfPkg/OvmfPkg.dec index 54804962ec02..62031ed182ea 100644 --- a/OvmfPkg/OvmfPkg.dec +++ b/OvmfPkg/OvmfPkg.dec @@ -118,6 +118,7 @@ gQemuKernelLoaderFsMediaGuid = {0x1428f772, 0xb64a, 0x441e, {0xb8, 0xc3, 0x9e, 0xbd, 0xd7, 0xf8, 0x93, 0xc7}} gGrubFileGuid = {0xb5ae312c, 0xbc8a, 0x43b1, {0x9c, 0x62, 0xeb, 0xb8, 0x26, 0xdd, 0x5d, 0x07}} gConfidentialComputingSecretGuid = {0xadf956ad, 0xe98c, 0x484c, {0xae, 0x11, 0xb5, 0x1c, 0x7d, 0x33, 0x64, 0x47}} + gUefiOvmfPkgTdxPlatformGuid = {0xdec9b486, 0x1f16, 0x47c7, {0x8f, 0x68, 0xdf, 0x1a, 0x41, 0x88, 0x8b, 0xa5}} [Ppis] # PPI whose presence in the PPI database signals that the TPM base address @@ -313,6 +314,35 @@ gUefiOvmfPkgTokenSpaceGuid.PcdSevLaunchSecretBase|0x0|UINT32|0x42 gUefiOvmfPkgTokenSpaceGuid.PcdSevLaunchSecretSize|0x0|UINT32|0x43 + ## PCD used by TDX + gUefiOvmfPkgTokenSpaceGuid.PcdTdMailboxBase|0|UINT32|0x46 + gUefiOvmfPkgTokenSpaceGuid.PcdTdMailboxSize|0|UINT32|0x47 + gUefiOvmfPkgTokenSpaceGuid.PcdTdHobBase|0|UINT32|0x48 + gUefiOvmfPkgTokenSpaceGuid.PcdTdHobSize|0|UINT32|0x49 + + gUefiOvmfPkgTokenSpaceGuid.PcdCfvBase|0|UINT32|0x4e + gUefiOvmfPkgTokenSpaceGuid.PcdCfvRawDataOffset|0|UINT32|0x4f + gUefiOvmfPkgTokenSpaceGuid.PcdCfvRawDataSize|0|UINT32|0x50 + + gUefiOvmfPkgTokenSpaceGuid.PcdBfvBase|0|UINT32|0x51 + gUefiOvmfPkgTokenSpaceGuid.PcdBfvRawDataOffset|0|UINT32|0x52 + gUefiOvmfPkgTokenSpaceGuid.PcdBfvRawDataSize|0|UINT32|0x53 + + gUefiOvmfPkgTokenSpaceGuid.PcdIgnoreVeHalt|FALSE|BOOLEAN|0x54 + gUefiOvmfPkgTokenSpaceGuid.PcdUseTdxEmulation|0x0|UINT32|0x55 + + gUefiOvmfPkgTokenSpaceGuid.PcdUseTdxMsr|TRUE|BOOLEAN|0x56 + + gUefiOvmfPkgTokenSpaceGuid.PcdOvmfImageSizeInKb|0|UINT32|0x58 + gUefiOvmfPkgTokenSpaceGuid.PcdTdxAcceptChunkSize|0x2000|UINT64|0x59 + gUefiOvmfPkgTokenSpaceGuid.PcdTdxSetNxForStack|FALSE|BOOLEAN|0x5b + gUefiOvmfPkgTokenSpaceGuid.PcdTdxPteMemoryEncryptionAddressOrMask|0|UINT64|0x5c + + ## The Tdx accept page size. 0x1000,0x200000 + # + gUefiOvmfPkgTokenSpaceGuid.PcdTdxAcceptPageSize|0x1000|UINT64|0x5d + + [PcdsDynamic, PcdsDynamicEx] gUefiOvmfPkgTokenSpaceGuid.PcdEmuVariableEvent|0|UINT64|2 gUefiOvmfPkgTokenSpaceGuid.PcdOvmfFlashVariablesEnable|FALSE|BOOLEAN|0x10 @@ -348,6 +378,14 @@ # This PCD is only accessed if PcdSmmSmramRequire is TRUE (see below). gUefiOvmfPkgTokenSpaceGuid.PcdQ35SmramAtDefaultSmbase|FALSE|BOOLEAN|0x34 + gUefiOvmfPkgTokenSpaceGuid.PcdTdRelocatedMailboxBase|0|UINT64|0x102 + + ## This PCD records LAML field in TDX EVENTLOG ACPI table. + gUefiOvmfPkgTokenSpaceGuid.PcdTdxEventlogAcpiTableLaml|0|UINT32|0x103 + + ## This PCD records LASA field in TDX EVENTLOG ACPI table. + gUefiOvmfPkgTokenSpaceGuid.PcdTdxEventlogAcpiTableLasa|0|UINT64|0x104 + [PcdsFeatureFlag] gUefiOvmfPkgTokenSpaceGuid.PcdQemuBootOrderPciTranslation|TRUE|BOOLEAN|0x1c gUefiOvmfPkgTokenSpaceGuid.PcdQemuBootOrderMmioTranslation|FALSE|BOOLEAN|0x1d diff --git a/OvmfPkg/OvmfPkgDefines.fdf.inc b/OvmfPkg/OvmfPkgDefines.fdf.inc index 35fd454b97ab..2b3990b2a3ac 100644 --- a/OvmfPkg/OvmfPkgDefines.fdf.inc +++ b/OvmfPkg/OvmfPkgDefines.fdf.inc @@ -9,7 +9,7 @@ ## DEFINE BLOCK_SIZE = 0x1000 - +DEFINE VARS_OFFSET = 0 # # A firmware binary built with FD_SIZE_IN_KB=1024, and a firmware binary built # with FD_SIZE_IN_KB=2048, use the same variable store layout. @@ -66,6 +66,7 @@ DEFINE SECFV_OFFSET = 0x003CC000 DEFINE SECFV_SIZE = 0x34000 !endif +SET gUefiOvmfPkgTokenSpaceGuid.PcdOvmfImageSizeInKb = $(FD_SIZE_IN_KB) SET gUefiOvmfPkgTokenSpaceGuid.PcdOvmfFdBaseAddress = $(FW_BASE_ADDRESS) SET gUefiOvmfPkgTokenSpaceGuid.PcdOvmfFirmwareFdSize = $(FW_SIZE) SET gUefiOvmfPkgTokenSpaceGuid.PcdOvmfFirmwareBlockSize = $(BLOCK_SIZE) @@ -82,6 +83,14 @@ SET gEfiMdeModulePkgTokenSpaceGuid.PcdFlashNvStorageFtwWorkingSize = $(BLOCK_SIZ SET gUefiOvmfPkgTokenSpaceGuid.PcdOvmfFlashNvStorageFtwSpareBase = gUefiOvmfPkgTokenSpaceGuid.PcdOvmfFlashNvStorageFtwWorkingBase + gEfiMdeModulePkgTokenSpaceGuid.PcdFlashNvStorageFtwWorkingSize SET gEfiMdeModulePkgTokenSpaceGuid.PcdFlashNvStorageFtwSpareSize = $(VARS_SPARE_SIZE) +SET gUefiOvmfPkgTokenSpaceGuid.PcdCfvBase = $(FW_BASE_ADDRESS) +SET gUefiOvmfPkgTokenSpaceGuid.PcdCfvRawDataOffset = $(VARS_OFFSET) +SET gUefiOvmfPkgTokenSpaceGuid.PcdCfvRawDataSize = $(VARS_LIVE_SIZE) + +SET gUefiOvmfPkgTokenSpaceGuid.PcdBfvBase = $(CODE_BASE_ADDRESS) +SET gUefiOvmfPkgTokenSpaceGuid.PcdBfvRawDataOffset = $(VARS_SIZE) +SET gUefiOvmfPkgTokenSpaceGuid.PcdBfvRawDataSize = $(CODE_SIZE) + !if $(SMM_REQUIRE) == TRUE SET gEfiMdeModulePkgTokenSpaceGuid.PcdFlashNvStorageVariableBase64 = gUefiOvmfPkgTokenSpaceGuid.PcdOvmfFlashNvStorageVariableBase SET gEfiMdeModulePkgTokenSpaceGuid.PcdFlashNvStorageFtwWorkingBase = gUefiOvmfPkgTokenSpaceGuid.PcdOvmfFlashNvStorageFtwWorkingBase diff --git a/OvmfPkg/OvmfPkgIa32.dsc b/OvmfPkg/OvmfPkgIa32.dsc index e5184ef06731..9377a5486e36 100644 --- a/OvmfPkg/OvmfPkgIa32.dsc +++ b/OvmfPkg/OvmfPkgIa32.dsc @@ -171,6 +171,7 @@ VirtioLib|OvmfPkg/Library/VirtioLib/VirtioLib.inf LoadLinuxLib|OvmfPkg/Library/LoadLinuxLib/LoadLinuxLib.inf MemEncryptSevLib|OvmfPkg/Library/BaseMemEncryptSevLib/DxeMemEncryptSevLib.inf + MemEncryptTdxLib|OvmfPkg/Library/BaseMemEncryptTdxLib/BaseMemEncryptTdxLibNull.inf !if $(SMM_REQUIRE) == FALSE LockBoxLib|OvmfPkg/Library/LockBoxLib/LockBoxBaseLib.inf !endif @@ -238,6 +239,9 @@ [LibraryClasses.common] BaseCryptLib|CryptoPkg/Library/BaseCryptLib/BaseCryptLib.inf VmgExitLib|UefiCpuPkg/Library/VmgExitLibNull/VmgExitLibNull.inf + TdxProbeLib|MdePkg/Library/TdxProbeLib/TdxProbeLibNull.inf + TdxStartupLib|OvmfPkg/Library/TdxStartupLib/TdxStartupLibNull.inf + VmTdExitLib|UefiCpuPkg/Library/VmTdExitLibNull/VmTdExitLibNull.inf [LibraryClasses.common.SEC] TimerLib|OvmfPkg/Library/AcpiTimerLib/BaseRomAcpiTimerLib.inf diff --git a/OvmfPkg/OvmfPkgIa32X64.dsc b/OvmfPkg/OvmfPkgIa32X64.dsc index 3bfec9196eba..0cab624266ee 100644 --- a/OvmfPkg/OvmfPkgIa32X64.dsc +++ b/OvmfPkg/OvmfPkgIa32X64.dsc @@ -175,6 +175,7 @@ VirtioLib|OvmfPkg/Library/VirtioLib/VirtioLib.inf LoadLinuxLib|OvmfPkg/Library/LoadLinuxLib/LoadLinuxLib.inf MemEncryptSevLib|OvmfPkg/Library/BaseMemEncryptSevLib/DxeMemEncryptSevLib.inf + MemEncryptTdxLib|OvmfPkg/Library/BaseMemEncryptTdxLib/BaseMemEncryptTdxLibNull.inf !if $(SMM_REQUIRE) == FALSE LockBoxLib|OvmfPkg/Library/LockBoxLib/LockBoxBaseLib.inf !endif @@ -242,6 +243,9 @@ [LibraryClasses.common] BaseCryptLib|CryptoPkg/Library/BaseCryptLib/BaseCryptLib.inf VmgExitLib|UefiCpuPkg/Library/VmgExitLibNull/VmgExitLibNull.inf + TdxStartupLib|OvmfPkg/Library/TdxStartupLib/TdxStartupLibNull.inf + TdxProbeLib|MdePkg/Library/TdxProbeLib/TdxProbeLibNull.inf + VmTdExitLib|UefiCpuPkg/Library/VmTdExitLibNull/VmTdExitLibNull.inf [LibraryClasses.common.SEC] TimerLib|OvmfPkg/Library/AcpiTimerLib/BaseRomAcpiTimerLib.inf diff --git a/OvmfPkg/OvmfPkgX64.dsc b/OvmfPkg/OvmfPkgX64.dsc index 1376158e37e3..7b2fcb8d66af 100644 --- a/OvmfPkg/OvmfPkgX64.dsc +++ b/OvmfPkg/OvmfPkgX64.dsc @@ -29,13 +29,18 @@ # Defines for default states. These can be changed on the command line. # -D FLAG=VALUE # - DEFINE SECURE_BOOT_ENABLE = FALSE + DEFINE SECURE_BOOT_ENABLE = TRUE DEFINE SMM_REQUIRE = FALSE DEFINE SOURCE_DEBUG_ENABLE = FALSE - DEFINE TPM_ENABLE = FALSE - DEFINE TPM_CONFIG_ENABLE = FALSE + DEFINE TPM_ENABLE = TRUE + DEFINE TPM_CONFIG_ENABLE = TRUE # + # TDX flags + # + DEFINE TDX_IGNORE_VE_HLT = FALSE + DEFINE TDX_EMULATION_ENABLE = FALSE + DEFINE TDX_SUPPORT = TRUE # Network definition # DEFINE NETWORK_TLS_ENABLE = FALSE @@ -92,6 +97,13 @@ INTEL:*_*_*_CC_FLAGS = /D DISABLE_NEW_DEPRECATED_INTERFACES GCC:*_*_*_CC_FLAGS = -D DISABLE_NEW_DEPRECATED_INTERFACES + # + # TDX Virtual Firmware + # + MSFT:*_*_*_CC_FLAGS = /D TDX_VIRTUAL_FIRMWARE + INTEL:*_*_*_CC_FLAGS = /D TDX_VIRTUAL_FIRMWARE + GCC:*_*_*_CC_FLAGS = -D TDX_VIRTUAL_FIRMWARE + !include NetworkPkg/NetworkBuildOptions.dsc.inc [BuildOptions.common.EDKII.DXE_RUNTIME_DRIVER] @@ -154,7 +166,7 @@ PciCapLib|OvmfPkg/Library/BasePciCapLib/BasePciCapLib.inf PciCapPciSegmentLib|OvmfPkg/Library/BasePciCapPciSegmentLib/BasePciCapPciSegmentLib.inf PciCapPciIoLib|OvmfPkg/Library/UefiPciCapPciIoLib/UefiPciCapPciIoLib.inf - IoLib|MdePkg/Library/BaseIoLibIntrinsic/BaseIoLibIntrinsicSev.inf + IoLib|MdePkg/Library/BaseIoLibIntrinsic/BaseIoLibIntrinsicTdx.inf OemHookStatusCodeLib|MdeModulePkg/Library/OemHookStatusCodeLibNull/OemHookStatusCodeLibNull.inf SerialPortLib|PcAtChipsetPkg/Library/SerialIoLib/SerialIoLib.inf MtrrLib|UefiCpuPkg/Library/MtrrLib/MtrrLib.inf @@ -175,6 +187,7 @@ VirtioLib|OvmfPkg/Library/VirtioLib/VirtioLib.inf LoadLinuxLib|OvmfPkg/Library/LoadLinuxLib/LoadLinuxLib.inf MemEncryptSevLib|OvmfPkg/Library/BaseMemEncryptSevLib/DxeMemEncryptSevLib.inf + MemEncryptTdxLib|OvmfPkg/Library/BaseMemEncryptTdxLib/BaseMemEncryptTdxLib.inf !if $(SMM_REQUIRE) == FALSE LockBoxLib|OvmfPkg/Library/LockBoxLib/LockBoxBaseLib.inf !endif @@ -210,6 +223,7 @@ VariablePolicyLib|MdeModulePkg/Library/VariablePolicyLib/VariablePolicyLib.inf VariablePolicyHelperLib|MdeModulePkg/Library/VariablePolicyHelperLib/VariablePolicyHelperLib.inf + LocalApicLib|UefiCpuPkg/Library/BaseXApicX2ApicLib/BaseXApicX2ApicLib.inf # # Network libraries @@ -233,15 +247,24 @@ Tpm2CommandLib|SecurityPkg/Library/Tpm2CommandLib/Tpm2CommandLib.inf Tcg2PhysicalPresenceLib|OvmfPkg/Library/Tcg2PhysicalPresenceLibQemu/DxeTcg2PhysicalPresenceLib.inf Tcg2PpVendorLib|SecurityPkg/Library/Tcg2PpVendorLibNull/Tcg2PpVendorLibNull.inf - TpmMeasurementLib|SecurityPkg/Library/DxeTpmMeasurementLib/DxeTpmMeasurementLib.inf + #TpmMeasurementLib|SecurityPkg/Library/DxeTpmMeasurementLib/DxeTpmMeasurementLib.inf !else Tcg2PhysicalPresenceLib|OvmfPkg/Library/Tcg2PhysicalPresenceLibNull/DxeTcg2PhysicalPresenceLib.inf + #TpmMeasurementLib|MdeModulePkg/Library/TpmMeasurementLibNull/TpmMeasurementLibNull.inf +!endif + +!if $(TPM_ENABLE) == TRUE or $(TDX_SUPPORT) == TRUE + TpmMeasurementLib|SecurityPkg/Library/DxeTpmMeasurementLib/DxeTpmMeasurementLib.inf +!else TpmMeasurementLib|MdeModulePkg/Library/TpmMeasurementLibNull/TpmMeasurementLibNull.inf !endif [LibraryClasses.common] BaseCryptLib|CryptoPkg/Library/BaseCryptLib/BaseCryptLib.inf VmgExitLib|OvmfPkg/Library/VmgExitLib/VmgExitLib.inf + VmTdExitLib|OvmfPkg/Library/VmTdExitLib/VmTdExitLib.inf + TdxLib|MdePkg/Library/TdxLib/TdxLib.inf + TdxProbeLib|OvmfPkg/Library/TdxProbeLib/TdxProbeLib.inf [LibraryClasses.common.SEC] TimerLib|OvmfPkg/Library/AcpiTimerLib/BaseRomAcpiTimerLib.inf @@ -256,10 +279,10 @@ !if $(SOURCE_DEBUG_ENABLE) == TRUE DebugAgentLib|SourceLevelDebugPkg/Library/DebugAgent/SecPeiDebugAgentLib.inf !endif - HobLib|MdePkg/Library/PeiHobLib/PeiHobLib.inf + HobLib|MdePkg/Library/SecHobLib/SecHobLib.inf PeiServicesLib|MdePkg/Library/PeiServicesLib/PeiServicesLib.inf PeiServicesTablePointerLib|MdePkg/Library/PeiServicesTablePointerLibIdt/PeiServicesTablePointerLibIdt.inf - MemoryAllocationLib|MdePkg/Library/PeiMemoryAllocationLib/PeiMemoryAllocationLib.inf + MemoryAllocationLib|MdePkg/Library/SecMemoryAllocationLib/SecMemoryAllocationLib.inf !if $(TOOL_CHAIN_TAG) == "XCODE5" CpuExceptionHandlerLib|UefiCpuPkg/Library/CpuExceptionHandlerLib/Xcode5SecPeiCpuExceptionHandlerLib.inf !else @@ -267,6 +290,11 @@ !endif VmgExitLib|OvmfPkg/Library/VmgExitLib/SecVmgExitLib.inf MemEncryptSevLib|OvmfPkg/Library/BaseMemEncryptSevLib/SecMemEncryptSevLib.inf + LocalApicLib|UefiCpuPkg/Library/BaseXApicX2ApicLib/BaseXApicX2ApicLibSec.inf + TdvfPlatformLib|OvmfPkg/Library/TdvfPlatformLibQemu/TdvfPlatformLibQemuSec.inf + PrePiLib|OvmfPkg/Library/PrePiLibTdx/PrePiLibTdx.inf + TdxStartupLib|OvmfPkg/Library/TdxStartupLib/TdxStartupLib.inf + BaseCryptLib|CryptoPkg/Library/BaseCryptLib/SecCryptLib.inf [LibraryClasses.common.PEI_CORE] HobLib|MdePkg/Library/PeiHobLib/PeiHobLib.inf @@ -532,7 +560,15 @@ # DEBUG_VERBOSE 0x00400000 // Detailed debug messages that may # // significantly impact boot performance # DEBUG_ERROR 0x80000000 // Error +!ifdef $(DEBUG_ON_SERIAL_PORT) + gEfiMdePkgTokenSpaceGuid.PcdDebugPrintErrorLevel|0xff1fff4f +!else gEfiMdePkgTokenSpaceGuid.PcdDebugPrintErrorLevel|0x8000004F +!endif + +!if $(TDX_EMULATION_ENABLE) == TRUE + gUefiOvmfPkgTokenSpaceGuid.PcdUseTdxEmulation|1 +!endif !if $(SOURCE_DEBUG_ENABLE) == TRUE gEfiMdePkgTokenSpaceGuid.PcdDebugPropertyMask|0x17 @@ -563,6 +599,28 @@ gEmbeddedTokenSpaceGuid.PcdMemoryTypeEfiReservedMemoryType|0x80 gEmbeddedTokenSpaceGuid.PcdMemoryTypeEfiRuntimeServicesCode|0x100 gEmbeddedTokenSpaceGuid.PcdMemoryTypeEfiRuntimeServicesData|0x100 + # + # TDX Pcds + # +!if $(TDX_IGNORE_VE_HLT) == TRUE + gUefiOvmfPkgTokenSpaceGuid.PcdIgnoreVeHalt|TRUE +!endif + # Accept chunk size - 32M + gUefiOvmfPkgTokenSpaceGuid.PcdTdxAcceptChunkSize|0x2000000 + # Accept page size - 4k + gUefiOvmfPkgTokenSpaceGuid.PcdTdxAcceptPageSize|0x1000 + + # Noexec settings for DXE. + # TDX doesn't allow us to change EFER so make sure these are disabled + gEfiMdeModulePkgTokenSpaceGuid.PcdImageProtectionPolicy|0x00000000 + gEfiMdeModulePkgTokenSpaceGuid.PcdDxeNxMemoryProtectionPolicy|0x00000000 + # Noexec settings for DXE. + # TDX doesn't allow us to change EFER so make sure these are disabled + #gEfiMdeModulePkgTokenSpaceGuid.PcdSetNxForStack|FALSE + gEfiMdeModulePkgTokenSpaceGuid.PcdUse1GPageTable|TRUE + + # Set memory encryption mask + gUefiOvmfPkgTokenSpaceGuid.PcdTdxPteMemoryEncryptionAddressOrMask|0x0 # # Network Pcds @@ -655,6 +713,7 @@ gEfiNetworkPkgTokenSpaceGuid.PcdIPv4PXESupport|0x01 gEfiNetworkPkgTokenSpaceGuid.PcdIPv6PXESupport|0x01 + [PcdsDynamicHii] !if $(TPM_ENABLE) == TRUE && $(TPM_CONFIG_ENABLE) == TRUE gEfiSecurityPkgTokenSpaceGuid.PcdTcgPhysicalPresenceInterfaceVer|L"TCG2_VERSION"|gTcg2ConfigFormSetGuid|0x0|"1.3"|NV,BS @@ -675,6 +734,8 @@ OvmfPkg/Sec/SecMain.inf { NULL|MdeModulePkg/Library/LzmaCustomDecompressLib/LzmaCustomDecompressLib.inf + HashLib|OvmfPkg/Library/HashLibBaseCryptoRouterTdx/HashLibBaseCryptoRouter.inf + NULL|SecurityPkg/Library/HashInstanceLibSha384/HashInstanceLibSha384.inf } # @@ -748,6 +809,9 @@ !endif !if $(TPM_ENABLE) == TRUE NULL|SecurityPkg/Library/DxeTpmMeasureBootLib/DxeTpmMeasureBootLib.inf + #NULL|SecurityPkg/Library/DxeTpm2MeasureBootLib/DxeTpm2MeasureBootLib.inf +!endif +!if $(TPM_ENABLE) == TRUE OR $(TDX_SUPPORT) == TRUE NULL|SecurityPkg/Library/DxeTpm2MeasureBootLib/DxeTpm2MeasureBootLib.inf !endif } @@ -756,6 +820,10 @@ OvmfPkg/8259InterruptControllerDxe/8259.inf UefiCpuPkg/CpuIo2Dxe/CpuIo2Dxe.inf UefiCpuPkg/CpuDxe/CpuDxe.inf + OvmfPkg/LocalApicTimerDxe/LocalApicTimerDxe.inf { + + LocalApicLib|UefiCpuPkg/Library/BaseXApicX2ApicLib/BaseXApicX2ApicLibDxe.inf + } OvmfPkg/8254TimerDxe/8254Timer.inf OvmfPkg/IncompatiblePciDeviceSupportDxe/IncompatiblePciDeviceSupport.inf OvmfPkg/PciHotPlugInitDxe/PciHotPlugInit.inf @@ -957,6 +1025,8 @@ OvmfPkg/AmdSevDxe/AmdSevDxe.inf OvmfPkg/IoMmuDxe/IoMmuDxe.inf + OvmfPkg/TdxDxe/TdxDxe.inf + !if $(SMM_REQUIRE) == TRUE OvmfPkg/SmmAccess/SmmAccess2Dxe.inf OvmfPkg/SmmControl2Dxe/SmmControl2Dxe.inf @@ -1016,6 +1086,16 @@ } !endif + # + # EFI TD PROTOCOL + # + OvmfPkg/Tcg/Tcg2Dxe/TdTcg2Dxe.inf { + + HashLib|OvmfPkg/Library/HashLibBaseCryptoRouterTdx/HashLibBaseCryptoRouter.inf + NULL|SecurityPkg/Library/HashInstanceLibSha384/HashInstanceLibSha384.inf + } + + # # TPM support # diff --git a/OvmfPkg/OvmfPkgX64.fdf b/OvmfPkg/OvmfPkgX64.fdf index d519f8532822..b693365545d4 100644 --- a/OvmfPkg/OvmfPkgX64.fdf +++ b/OvmfPkg/OvmfPkgX64.fdf @@ -88,6 +88,22 @@ gUefiCpuPkgTokenSpaceGuid.PcdSevEsWorkAreaBase|gUefiCpuPkgTokenSpaceGuid.PcdSevE 0x00C000|0x001000 gUefiOvmfPkgTokenSpaceGuid.PcdOvmfSecGhcbBackupBase|gUefiOvmfPkgTokenSpaceGuid.PcdOvmfSecGhcbBackupSize +0x00D000|0x001000 +gUefiOvmfPkgTokenSpaceGuid.PcdTdMailboxBase|gUefiOvmfPkgTokenSpaceGuid.PcdTdMailboxSize +DATA = { + 0x00,0x00,0x00,0x00, + 0xff,0xff,0xff,0xff, + 0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00 +} + +0x00E000|0x001000 +gUefiOvmfPkgTokenSpaceGuid.PcdTdHobBase|gUefiOvmfPkgTokenSpaceGuid.PcdTdHobSize + 0x010000|0x010000 gUefiOvmfPkgTokenSpaceGuid.PcdOvmfSecPeiTempRamBase|gUefiOvmfPkgTokenSpaceGuid.PcdOvmfSecPeiTempRamSize @@ -205,6 +221,7 @@ READ_LOCK_STATUS = TRUE APRIORI DXE { INF MdeModulePkg/Universal/DevicePathDxe/DevicePathDxe.inf INF MdeModulePkg/Universal/PCD/Dxe/Pcd.inf + INF OvmfPkg/TdxDxe/TdxDxe.inf INF OvmfPkg/AmdSevDxe/AmdSevDxe.inf !if $(SMM_REQUIRE) == FALSE INF OvmfPkg/QemuFlashFvbServicesRuntimeDxe/FvbServicesRuntimeDxe.inf @@ -226,6 +243,8 @@ INF MdeModulePkg/Universal/EbcDxe/EbcDxe.inf INF OvmfPkg/8259InterruptControllerDxe/8259.inf INF UefiCpuPkg/CpuIo2Dxe/CpuIo2Dxe.inf INF UefiCpuPkg/CpuDxe/CpuDxe.inf +INF OvmfPkg/LocalApicTimerDxe/LocalApicTimerDxe.inf + INF OvmfPkg/8254TimerDxe/8254Timer.inf INF OvmfPkg/IncompatiblePciDeviceSupportDxe/IncompatiblePciDeviceSupport.inf INF OvmfPkg/PciHotPlugInitDxe/PciHotPlugInit.inf @@ -314,6 +333,8 @@ INF ShellPkg/Application/Shell/Shell.inf INF MdeModulePkg/Logo/LogoDxe.inf +INF OvmfPkg/TdxDxe/TdxDxe.inf + # # Network modules # @@ -379,6 +400,11 @@ INF MdeModulePkg/Universal/FaultTolerantWriteDxe/FaultTolerantWriteDxe.inf INF MdeModulePkg/Universal/Variable/RuntimeDxe/VariableRuntimeDxe.inf !endif +# +# TDX support +# +INF OvmfPkg/Tcg/Tcg2Dxe/TdTcg2Dxe.inf + # # TPM support # diff --git a/OvmfPkg/ResetVector/Ia16/ResetVectorVtf0.asm b/OvmfPkg/ResetVector/Ia16/ResetVectorVtf0.asm index 9c0b5853a46f..024754a965fb 100644 --- a/OvmfPkg/ResetVector/Ia16/ResetVectorVtf0.asm +++ b/OvmfPkg/ResetVector/Ia16/ResetVectorVtf0.asm @@ -21,8 +21,12 @@ ALIGN 16 ; This is required so the page tables will be 4k aligned when VTF0 is ; located just below 0x100000000 (4GB) in the firmware device. ; -%ifdef ALIGN_TOP_TO_4K_FOR_PAGING +; For TDX_VIRTUAL_FIRMWARE the padding is in X64\TdxMetadata.asm. +; +%ifndef TDX_VIRTUAL_FIRMWARE + %ifdef ALIGN_TOP_TO_4K_FOR_PAGING TIMES (0x1000 - ($ - EndOfPageTables) - 0x20) DB 0 + %endif %endif ; @@ -115,11 +119,23 @@ applicationProcessorEntryPoint: ; location. (0xffffffe0) This allows the Local APIC Startup IPI to be ; used to wake up the application processors. ; +%ifdef TDX_VIRTUAL_FIRMWARE + DD (OVMF_IMAGE_SIZE_IN_KB * 1024 - (fourGigabytes - TdxMetadataGuid - 16)) +%else jmp EarlyApInitReal16 +%endif ALIGN 8 +; +; TDX Virtual Firmware injects metadata in VTF0. +; The address of the metadata is injected in this location (0xffffffe8) +; +%ifdef TDX_VIRTUAL_FIRMWARE + DD (OVMF_IMAGE_SIZE_IN_KB * 1024 - (fourGigabytes - TdxMetadataGuid - 16)) +%else DD 0 +%endif ; ; The VTF signature @@ -137,9 +153,23 @@ resetVector: ; Reset Vector ; ; This is where the processor will begin execution +; +; TDX_VIRTUAL_FIRMWARE is defined in X64/TdxMetadata.asm +; For TDX_VIRTAUL_FIRMWARE, it is of Protected mode in ResetVector +; Note: +; The distance between ResetVector and EarlyBspPmEntry should +; be less than 128 bytes. +; DO NOT ADD MORE DATA between ResetVector and EarlyBspPmEntry ; nop nop + +%ifdef TDX_VIRTUAL_FIRMWARE + smsw ax + test al, 1 + jnz EarlyBspPmEntry +%endif + jmp EarlyBspInitReal16 ALIGN 16 diff --git a/OvmfPkg/ResetVector/Ia32/Flat32ToFlat64.asm b/OvmfPkg/ResetVector/Ia32/Flat32ToFlat64.asm index c6d0d898bcd1..f034bf1b555d 100644 --- a/OvmfPkg/ResetVector/Ia32/Flat32ToFlat64.asm +++ b/OvmfPkg/ResetVector/Ia32/Flat32ToFlat64.asm @@ -14,6 +14,8 @@ BITS 32 ; Modified: EAX, ECX, EDX ; Transition32FlatTo64Flat: + cmp byte[TDX_WORK_AREA], 1 + jz TdxTransition32FlatTo64Flat OneTimeCall SetCr3ForPageTables64 @@ -26,6 +28,7 @@ Transition32FlatTo64Flat: bts eax, 8 ; set LME wrmsr +SevEsMitigationCheck: ; ; SEV-ES mitigation check support ; @@ -65,10 +68,53 @@ EnablePaging: bts eax, 31 ; set PG mov cr0, eax ; enable paging + jmp _jumpTo64Bit + +; +; Tdx Transition from 32Flat to 64Flat +; Tdx use the page table built in Vtf0 for quicker performance +; +TdxTransition32FlatTo64Flat: + mov eax, cr4 + bts eax, 5 ; enable PAE + + ; + ; byte[TDX_WORK_AREA_PAGELEVEL5] holds the indicator whether 52bit is supported. + ; if it is the case, need to set LA57 and use 5-level paging + ; + cmp byte[TDX_WORK_AREA_PAGELEVEL5], 0 + jz .set_cr4 + bts eax, 12 +.set_cr4: + mov cr4, eax + + mov ebx, ADDR_OF(TopLevelPageDirectory) + ; + ; if we just set la57, we are ok, if using 4-level paging, adjust top-level page directory + ; + bt eax, 12 + jc .set_cr3 + add ebx, 0x1000 +.set_cr3: + mov cr3, ebx + + mov eax, cr0 + bts eax, 31 ; set PG + mov cr0, eax ; enable paging + +_jumpTo64Bit: jmp LINEAR_CODE64_SEL:ADDR_OF(jumpTo64BitAndLandHere) + BITS 64 jumpTo64BitAndLandHere: + ; + ; For Td guest we are done and jump to the end + ; + mov eax, TDX_WORK_AREA + cmp byte[eax], 1 + jz GoodCompare + ; ; Check if the second step of the SEV-ES mitigation is to be performed. ; diff --git a/OvmfPkg/ResetVector/Ia32/PageTables64.asm b/OvmfPkg/ResetVector/Ia32/PageTables64.asm index 5fae8986d9da..e6f64900a82f 100644 --- a/OvmfPkg/ResetVector/Ia32/PageTables64.asm +++ b/OvmfPkg/ResetVector/Ia32/PageTables64.asm @@ -10,33 +10,6 @@ BITS 32 -%define PAGE_PRESENT 0x01 -%define PAGE_READ_WRITE 0x02 -%define PAGE_USER_SUPERVISOR 0x04 -%define PAGE_WRITE_THROUGH 0x08 -%define PAGE_CACHE_DISABLE 0x010 -%define PAGE_ACCESSED 0x020 -%define PAGE_DIRTY 0x040 -%define PAGE_PAT 0x080 -%define PAGE_GLOBAL 0x0100 -%define PAGE_2M_MBO 0x080 -%define PAGE_2M_PAT 0x01000 - -%define PAGE_4K_PDE_ATTR (PAGE_ACCESSED + \ - PAGE_DIRTY + \ - PAGE_READ_WRITE + \ - PAGE_PRESENT) - -%define PAGE_2M_PDE_ATTR (PAGE_2M_MBO + \ - PAGE_ACCESSED + \ - PAGE_DIRTY + \ - PAGE_READ_WRITE + \ - PAGE_PRESENT) - -%define PAGE_PDP_ATTR (PAGE_ACCESSED + \ - PAGE_READ_WRITE + \ - PAGE_PRESENT) - ; ; SEV-ES #VC exception handler support ; diff --git a/OvmfPkg/ResetVector/Ia32/ReloadFlat32.asm b/OvmfPkg/ResetVector/Ia32/ReloadFlat32.asm new file mode 100644 index 000000000000..b4a4960b5c64 --- /dev/null +++ b/OvmfPkg/ResetVector/Ia32/ReloadFlat32.asm @@ -0,0 +1,49 @@ +;------------------------------------------------------------------------------ +; @file +; Transition from 16 bit real mode into 32 bit flat protected mode +; +; Copyright (c) 2008 - 2010, Intel Corporation. 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. +; +;------------------------------------------------------------------------------ + +%define SEC_DEFAULT_CR0 0x00000023 +%define SEC_DEFAULT_CR4 0x640 + +BITS 32 + +; +; Modified: EAX, EBX +; +ReloadFlat32: + + cli + mov ebx, ADDR_OF(gdtr) + lgdt [ebx] + + mov eax, SEC_DEFAULT_CR0 + mov cr0, eax + + jmp LINEAR_CODE_SEL:dword ADDR_OF(jumpToFlat32BitAndLandHere) +BITS 32 +jumpToFlat32BitAndLandHere: + + mov eax, SEC_DEFAULT_CR4 + mov cr4, eax + + debugShowPostCode POSTCODE_32BIT_MODE + + mov ax, LINEAR_SEL + mov ds, ax + mov es, ax + mov fs, ax + mov gs, ax + mov ss, ax + + OneTimeCallRet ReloadFlat32 \ No newline at end of file diff --git a/OvmfPkg/ResetVector/ResetVector.inf b/OvmfPkg/ResetVector/ResetVector.inf index dc38f68919cd..0309295a0831 100644 --- a/OvmfPkg/ResetVector/ResetVector.inf +++ b/OvmfPkg/ResetVector/ResetVector.inf @@ -1,7 +1,7 @@ ## @file # Reset Vector # -# Copyright (c) 2006 - 2014, Intel Corporation. All rights reserved.
+# Copyright (c) 2006 - 2021, Intel Corporation. All rights reserved.
# # SPDX-License-Identifier: BSD-2-Clause-Patent # @@ -44,6 +44,19 @@ gUefiOvmfPkgTokenSpaceGuid.PcdOvmfSecPeiTempRamBase gUefiOvmfPkgTokenSpaceGuid.PcdOvmfSecPeiTempRamSize + gUefiOvmfPkgTokenSpaceGuid.PcdTdMailboxBase + gUefiOvmfPkgTokenSpaceGuid.PcdTdMailboxSize + gUefiOvmfPkgTokenSpaceGuid.PcdTdHobBase + gUefiOvmfPkgTokenSpaceGuid.PcdTdHobSize + + gUefiOvmfPkgTokenSpaceGuid.PcdCfvBase + gUefiOvmfPkgTokenSpaceGuid.PcdCfvRawDataOffset + gUefiOvmfPkgTokenSpaceGuid.PcdCfvRawDataSize + gUefiOvmfPkgTokenSpaceGuid.PcdBfvBase + gUefiOvmfPkgTokenSpaceGuid.PcdBfvRawDataOffset + gUefiOvmfPkgTokenSpaceGuid.PcdBfvRawDataSize + + gUefiOvmfPkgTokenSpaceGuid.PcdOvmfImageSizeInKb [FixedPcd] gUefiOvmfPkgTokenSpaceGuid.PcdSevLaunchSecretBase gUefiOvmfPkgTokenSpaceGuid.PcdSevLaunchSecretSize diff --git a/OvmfPkg/ResetVector/ResetVector.nasmb b/OvmfPkg/ResetVector/ResetVector.nasmb index 5fbacaed5f9d..abca50e75bc2 100644 --- a/OvmfPkg/ResetVector/ResetVector.nasmb +++ b/OvmfPkg/ResetVector/ResetVector.nasmb @@ -4,6 +4,7 @@ ; ; Copyright (c) 2008 - 2013, Intel Corporation. All rights reserved.
; Copyright (c) 2020, Advanced Micro Devices, Inc. All rights reserved.
+; Copyright (c) 2021, Intel Corporation. All rights reserved.
; SPDX-License-Identifier: BSD-2-Clause-Patent ; ;------------------------------------------------------------------------------ @@ -36,6 +37,56 @@ %include "PostCodes.inc" + +%ifdef ARCH_X64 + + ; + ; TdMailboxBase[0x10, 0x800] is reserved for OS + ; Td guest uses this area (TdMailboxBase[0x10,0x20]) to record the Td guest info + ; SEC can determine Td guest by this information + ; + %define TD_MAILBOX_WORKAREA_OFFSET 0x10 + %define TDX_WORK_AREA (FixedPcdGet32 (PcdTdMailboxBase) + TD_MAILBOX_WORKAREA_OFFSET) + %define TDX_WORK_AREA_PAGELEVEL5 (FixedPcdGet32 (PcdTdMailboxBase) + TD_MAILBOX_WORKAREA_OFFSET + 1) + %define TDX_WORK_AREA_INITVP (FixedPcdGet32 (PcdTdMailboxBase) + TD_MAILBOX_WORKAREA_OFFSET + 8) + %define TDX_WORK_AREA_INFO (FixedPcdGet32 (PcdTdMailboxBase) + TD_MAILBOX_WORKAREA_OFFSET + 8 + 4) + + ; + ; TDX meta data + ; + %define TDX_METADATA_SECTION_TYPE_BFV 0 + %define TDX_METADATA_SECTION_TYPE_CFV 1 + %define TDX_METADATA_SECTION_TYPE_TD_HOB 2 + %define TDX_METADATA_SECTION_TYPE_TEMP_MEM 3 + %define TDX_METADATA_VERSION 1 + %define TDX_METADATA_ATTRIBUTES_EXTENDMR 0x00000001 + + %define TDX_BFV_RAW_DATA_OFFSET FixedPcdGet32(PcdBfvRawDataOffset) + %define TDX_BFV_RAW_DATA_SIZE FixedPcdGet32(PcdBfvRawDataSize) + %define TDX_BFV_MEMORY_BASE FixedPcdGet32(PcdBfvBase) + %define TDX_BFV_MEMORY_SIZE FixedPcdGet32(PcdBfvRawDataSize) + + %define TDX_CFV_RAW_DATA_OFFSET FixedPcdGet32(PcdCfvRawDataOffset) + %define TDX_CFV_RAW_DATA_SIZE FixedPcdGet32(PcdCfvRawDataSize) + %define TDX_CFV_MEMORY_BASE FixedPcdGet32(PcdCfvBase), + %define TDX_CFV_MEMORY_SIZE FixedPcdGet32(PcdCfvRawDataSize), + + %define TDX_STACK_MEMORY_BASE (FixedPcdGet32(PcdOvmfSecPeiTempRamBase) + FixedPcdGet32(PcdOvmfSecPeiTempRamSize)/2) + %define TDX_STACK_MEMORY_SIZE (FixedPcdGet32(PcdOvmfSecPeiTempRamSize)/2) + + %define TDX_HEAP_MEMORY_BASE (FixedPcdGet32(PcdOvmfSecPeiTempRamBase)) + %define TDX_HEAP_MEMORY_SIZE (FixedPcdGet32(PcdOvmfSecPeiTempRamSize)/2) + + %define TDX_HOB_MEMORY_BASE FixedPcdGet32(PcdTdHobBase) + %define TDX_HOB_MEMORY_SIZE FixedPcdGet32(PcdTdHobSize) + + %define TDX_MAILBOX_MEMORY_BASE FixedPcdGet32(PcdTdMailboxBase) + %define TDX_MAILBOX_MEMORY_SIZE FixedPcdGet32(PcdTdMailboxSize) + + %include "X64/PageTables.asm" + %include "X64/TdxMetadata.asm" +%endif + %ifdef DEBUG_PORT80 %include "Port80Debug.asm" %elifdef DEBUG_SERIAL @@ -76,17 +127,30 @@ %define SEV_ES_WORK_AREA_RDRAND (FixedPcdGet32 (PcdSevEsWorkAreaBase) + 8) %define SEV_ES_WORK_AREA_ENC_MASK (FixedPcdGet32 (PcdSevEsWorkAreaBase) + 16) %define SEV_ES_VC_TOP_OF_STACK (FixedPcdGet32 (PcdOvmfSecPeiTempRamBase) + FixedPcdGet32 (PcdOvmfSecPeiTempRamSize)) -%include "Ia32/Flat32ToFlat64.asm" -%include "Ia32/PageTables64.asm" + + ; + ; TDX specific + ; + %if (FixedPcdGet32 (PcdTdMailboxSize) != 0x1000) + %error "This implementation inherently depends on PcdTdMailboxSize" + %endif + + %if (FixedPcdGet32 (PcdTdHobSize) != 0x1000) + %error "This implementation inherently depends on PcdTdHobSize" + %endif + + %include "Ia32/Flat32ToFlat64.asm" + %include "Ia32/PageTables64.asm" + %include "Ia32/ReloadFlat32.asm" %endif %include "Ia16/Real16ToFlat32.asm" -%include "Ia16/Init16.asm" - %include "Main.asm" %define SEV_ES_AP_RESET_IP FixedPcdGet32 (PcdSevEsWorkAreaBase) %define SEV_LAUNCH_SECRET_BASE FixedPcdGet32 (PcdSevLaunchSecretBase) %define SEV_LAUNCH_SECRET_SIZE FixedPcdGet32 (PcdSevLaunchSecretSize) + %define OVMF_IMAGE_SIZE_IN_KB FixedPcdGet32 (PcdOvmfImageSizeInKb) +%include "Ia16/Init16.asm" %include "Ia16/ResetVectorVtf0.asm" diff --git a/OvmfPkg/ResetVector/X64/PageTables.asm b/OvmfPkg/ResetVector/X64/PageTables.asm new file mode 100644 index 000000000000..379f9fd2e355 --- /dev/null +++ b/OvmfPkg/ResetVector/X64/PageTables.asm @@ -0,0 +1,48 @@ +;------------------------------------------------------------------------------ +; @file +; Emits Page Tables for 1:1 mapping of the addresses 0 - 0x100000000 (4GB) +; +; Copyright (c) 2008 - 2014, Intel Corporation. All rights reserved.
+; SPDX-License-Identifier: BSD-2-Clause-Patent +; +;------------------------------------------------------------------------------ + +BITS 64 + +%define ALIGN_TOP_TO_4K_FOR_PAGING + +%define PGTBLS_OFFSET(x) ((x) - TopLevelPageDirectory) +%define PGTBLS_ADDR(x) (ADDR_OF(TopLevelPageDirectory) + (x)) +%define PDP(offset) (ADDR_OF(TopLevelPageDirectory) + (offset) + PAGE_PDP_ATTR) +%define PTE_2MB(x) ((x << 21) + PAGE_2M_PDE_ATTR) + +TopLevelPageDirectory: + + ; + ; Top level Page Directory Pointers (1 * 512GB entry) + ; + DQ PDP(0x1000) + ; + ; Next level Page Directory Pointers (4 * 1GB entries => 4GB) + ; + TIMES 511 DQ 0 + + DQ PDP(0x2000) + TIMES 511 DQ 0 + DQ PDP(0x3000) + DQ PDP(0x4000) + DQ PDP(0x5000) + DQ PDP(0x6000) + + ; + ; Page Table Entries (2048 * 2MB entries => 4GB) + ; + TIMES 508 DQ 0 + +%assign i 0 +%rep 0x800 + DQ PTE_2MB(i) + %assign i i+1 +%endrep + +EndOfPageTables: diff --git a/OvmfPkg/ResetVector/X64/TdxMetadata.asm b/OvmfPkg/ResetVector/X64/TdxMetadata.asm new file mode 100644 index 000000000000..967773c3d48a --- /dev/null +++ b/OvmfPkg/ResetVector/X64/TdxMetadata.asm @@ -0,0 +1,98 @@ +;------------------------------------------------------------------------------ +; @file +; TDX metadata and padding the image size to 4k when page tables are in VTF0 +; +; Copyright (c) 2021, Intel Corporation. All rights reserved.
+; SPDX-License-Identifier: BSD-2-Clause-Patent +; +;------------------------------------------------------------------------------ + +BITS 64 + +%define TDX_VIRTUAL_FIRMWARE + +; +; Pad the image size to 4k when page tables are in VTF0 +; +; If the VTF0 image has page tables built in, then we need to make +; sure the end of VTF0 is 4k above where the page tables end. +; +; This is required so the page tables will be 4k aligned when VTF0 is +; located just below 0x100000000 (4GB) in the firmware device. +; +%ifdef TDX_VIRTUAL_FIRMWARE + %ifdef ALIGN_TOP_TO_4K_FOR_PAGING + TIMES ((0x1000 - ($ - EndOfPageTables) - (fourGigabytes - TdxPaddingEnd) % 4096)) DB 0 + %endif +%endif + +TdxPaddingEnd: + +ALIGN 16 +TIMES (15 - ((TdxGuidedStructureEnd - TdxGuidedStructureStart + 15) % 16)) DB 0 + +TdxGuidedStructureStart: + +; +; TDVF meta data +; +TdxMetadataGuid: + DB 0xf3, 0xf9, 0xea, 0xe9, 0x8e, 0x16, 0xd5, 0x44 + DB 0xa8, 0xeb, 0x7f, 0x4d, 0x87, 0x38, 0xf6, 0xae + +_Descriptor: + DB 'T','D','V','F' ; Signature + DD TdxGuidedStructureEnd - _Descriptor ; Length + DD TDX_METADATA_VERSION ; Version + DD (TdxGuidedStructureEnd - _Descriptor - 16)/32 ; Number of sections + +_Bfv: + DD TDX_BFV_RAW_DATA_OFFSET + DD TDX_BFV_RAW_DATA_SIZE + DQ TDX_BFV_MEMORY_BASE + DQ TDX_BFV_MEMORY_SIZE + DD TDX_METADATA_SECTION_TYPE_BFV + DD TDX_METADATA_ATTRIBUTES_EXTENDMR + +_Cfv: + DD TDX_CFV_RAW_DATA_OFFSET + DD TDX_CFV_RAW_DATA_SIZE + DQ TDX_CFV_MEMORY_BASE + DQ TDX_CFV_MEMORY_SIZE + DD TDX_METADATA_SECTION_TYPE_CFV + DD 0 + +_Stack: + DD 0 + DD 0 + DQ TDX_STACK_MEMORY_BASE + DQ TDX_STACK_MEMORY_SIZE + DD TDX_METADATA_SECTION_TYPE_TEMP_MEM + DD 0 + +_Heap: + DD 0 + DD 0 + DQ TDX_HEAP_MEMORY_BASE + DQ TDX_HEAP_MEMORY_SIZE + DD TDX_METADATA_SECTION_TYPE_TEMP_MEM + DD 0 + +_TdHob: + DD 0 + DD 0 + DQ TDX_HOB_MEMORY_BASE + DQ TDX_HOB_MEMORY_SIZE + DD TDX_METADATA_SECTION_TYPE_TD_HOB + DD 0 + +_MailBox: + DD 0 + DD 0 + DQ TDX_MAILBOX_MEMORY_BASE + DQ TDX_MAILBOX_MEMORY_SIZE + DD TDX_METADATA_SECTION_TYPE_TEMP_MEM + DD 0 + +TdxGuidedStructureEnd: +ALIGN 16 diff --git a/OvmfPkg/Sec/SecMain.c b/OvmfPkg/Sec/SecMain.c index 9db67e17b2aa..04e4f14514e1 100644 --- a/OvmfPkg/Sec/SecMain.c +++ b/OvmfPkg/Sec/SecMain.c @@ -29,9 +29,24 @@ #include #include #include +#include +#include #include +#if defined (MDE_CPU_X64) +BOOLEAN mTdxSupported = FALSE; + +typedef struct _TDX_WORK_AREA{ + UINT8 TdxIsEnabled; + UINT8 PageLevel5; // 5 page level is supported + UINT8 Rsvd[6]; + + UINT32 TdxInitVp; + UINT32 Info; +}TDX_WORK_AREA; +#endif + #define SEC_IDT_ENTRY_COUNT 34 typedef struct _SEC_IDT_TABLE { @@ -831,6 +846,137 @@ SevEsIsEnabled ( return ((SevEsWorkArea != NULL) && (SevEsWorkArea->SevEsEnabled != 0)); } +VOID +EFIAPI +InitializeSecCoreData( + IN EFI_SEC_PEI_HAND_OFF *SecCoreData, + IN EFI_FIRMWARE_VOLUME_HEADER *BootFv, + IN VOID *TopOfCurrentStack +) +{ + // + // |-------------| <-- TopOfCurrentStack + // | Stack | 32k + // |-------------| + // | Heap | 32k + // |-------------| <-- SecCoreData.TemporaryRamBase + // + + ASSERT ((UINTN) (PcdGet32 (PcdOvmfSecPeiTempRamBase) + + PcdGet32 (PcdOvmfSecPeiTempRamSize)) == + (UINTN) TopOfCurrentStack); + + // + // Initialize SEC hand-off state + // + SecCoreData->DataSize = sizeof(EFI_SEC_PEI_HAND_OFF); + + SecCoreData->TemporaryRamSize = (UINTN) PcdGet32 (PcdOvmfSecPeiTempRamSize); + SecCoreData->TemporaryRamBase = (VOID*)((UINT8 *)TopOfCurrentStack - SecCoreData->TemporaryRamSize); + + SecCoreData->PeiTemporaryRamBase = SecCoreData->TemporaryRamBase; + SecCoreData->PeiTemporaryRamSize = SecCoreData->TemporaryRamSize >> 1; + + SecCoreData->StackBase = (UINT8 *)SecCoreData->TemporaryRamBase + SecCoreData->PeiTemporaryRamSize; + SecCoreData->StackSize = SecCoreData->TemporaryRamSize >> 1; + + SecCoreData->BootFirmwareVolumeBase = BootFv; + SecCoreData->BootFirmwareVolumeSize = (UINTN) BootFv->FvLength; +} + + +#ifdef TDX_VIRTUAL_FIRMWARE +/** + Determine if TDX is supported + + During early booting, TDX support code will set a flag to indicate that + TDX is supported. Some more information are set in TDX_WORK_AREA if TDX + is supported. + + @retval TRUE TDX is supported + @retval FALSE TDX is not supported +**/ +BOOLEAN +CheckTdxSupported( + IN OUT VOID** TdInitVp, + IN OUT UINTN* TdInfo, + IN OUT UINT8* PageLevel5 + ) +{ + BOOLEAN Supported; + TDX_WORK_AREA *TdxWorkArea; + UINT32 TdMailboxBase; + + TdMailboxBase = FixedPcdGet32 (PcdTdMailboxBase); + if (TdMailboxBase == 0) { + return FALSE; + } + + TdxWorkArea = (TDX_WORK_AREA *) ((UINTN)(TdMailboxBase + 0x10)); + Supported = (TdxWorkArea != NULL) && (TdxWorkArea->TdxIsEnabled != 0); + + if (Supported) { + *TdInitVp = (VOID*)(UINTN)TdxWorkArea->TdxInitVp; + *TdInfo = (UINTN)TdxWorkArea->Info; + *PageLevel5 = TdxWorkArea->PageLevel5; + } + + return Supported; +} + +EFI_STATUS +EFIAPI +TdxInitialize ( + IN VOID *Context, + IN EFI_FIRMWARE_VOLUME_HEADER *BootFv, + IN VOID *TopOfCurrentStack + ) +{ + EFI_SEC_PEI_HAND_OFF *SecCoreData; + SEC_IDT_TABLE IdtTableInStack; + IA32_DESCRIPTOR IdtDescriptor; + UINT32 Index; + + SecCoreData = (EFI_SEC_PEI_HAND_OFF *)Context; + + IdtTableInStack.PeiService = NULL; + for (Index = 0; Index < SEC_IDT_ENTRY_COUNT; Index ++) { + CopyMem (&IdtTableInStack.IdtTable[Index], &mIdtEntryTemplate, sizeof (mIdtEntryTemplate)); + } + + IdtDescriptor.Base = (UINTN)&IdtTableInStack.IdtTable; + IdtDescriptor.Limit = (UINT16)(sizeof (IdtTableInStack.IdtTable) - 1); + + AsmWriteIdtr(&IdtDescriptor); + InitializeCpuExceptionHandlers(NULL); + + DEBUG ((DEBUG_INFO, + "SecCoreStartupWithStack(0x%x, 0x%x)\n", + (UINT32)(UINTN)BootFv, + (UINT32)(UINTN)TopOfCurrentStack + )); + + // + // Initialize SEC hand-off state + // + InitializeSecCoreData(SecCoreData, BootFv, TopOfCurrentStack); + + IoWrite8 (0x21, 0xff); + IoWrite8 (0xA1, 0xff); + + // + // Initialize Local APIC Timer hardware and disable Local APIC Timer + // interrupts before initializing the Debug Agent and the debug timer is + // enabled. + // + InitializeApicTimer (0, MAX_UINT32, TRUE, 5); + DisableApicTimerInterrupt (); + + return EFI_SUCCESS; +} + +#endif + VOID EFIAPI SecCoreStartupWithStack ( @@ -844,6 +990,29 @@ SecCoreStartupWithStack ( UINT32 Index; volatile UINT8 *Table; +#ifdef TDX_VIRTUAL_FIRMWARE + VOID *TdInitVp; + UINTN TdInfo; + UINT8 PageLevel5; + + // + // To check whether it is of Tdx Guest + // + mTdxSupported = CheckTdxSupported(&TdInitVp, &TdInfo, &PageLevel5); + + if(mTdxSupported){ + + TdxInitialize(&SecCoreData, BootFv, TopOfCurrentStack); + + DEBUG((DEBUG_INFO, "WorkArea: 0x%x, 0x%x, %d\n", TdInitVp, TdInfo, PageLevel5)); + + TdxStartup(&SecCoreData, TdInitVp, TdInfo, ProcessLibraryConstructorList); + + ASSERT(FALSE); + CpuDeadLoop(); + } +#endif + // // To ensure SMM can't be compromised on S3 resume, we must force re-init of // the BaseExtractGuidedSectionLib. Since this is before library contructors diff --git a/OvmfPkg/Sec/SecMain.inf b/OvmfPkg/Sec/SecMain.inf index 7f78dcee2772..b0bb36a66d98 100644 --- a/OvmfPkg/Sec/SecMain.inf +++ b/OvmfPkg/Sec/SecMain.inf @@ -51,6 +51,7 @@ ExtractGuidedSectionLib LocalApicLib CpuExceptionHandlerLib + TdxStartupLib [Ppis] gEfiTemporaryRamSupportPpiGuid # PPI ALWAYS_PRODUCED @@ -71,5 +72,10 @@ gUefiOvmfPkgTokenSpaceGuid.PcdOvmfDecompressionScratchEnd gEfiMdeModulePkgTokenSpaceGuid.PcdInitValueInTempStack + gUefiOvmfPkgTokenSpaceGuid.PcdUseTdxEmulation + gUefiOvmfPkgTokenSpaceGuid.PcdTdMailboxBase + gUefiOvmfPkgTokenSpaceGuid.PcdTdMailboxSize + + [FeaturePcd] gUefiOvmfPkgTokenSpaceGuid.PcdSmmSmramRequire diff --git a/OvmfPkg/Sec/X64/SecEntry.nasm b/OvmfPkg/Sec/X64/SecEntry.nasm index 1cc680a70716..8e8ae7f9daf5 100644 --- a/OvmfPkg/Sec/X64/SecEntry.nasm +++ b/OvmfPkg/Sec/X64/SecEntry.nasm @@ -10,12 +10,21 @@ ;------------------------------------------------------------------------------ #include +%include "TdxCommondefs.inc" DEFAULT REL SECTION .text extern ASM_PFX(SecCoreStartupWithStack) +%macro tdcall 0 +%if (FixedPcdGet32 (PcdUseTdxEmulation) != 0) + vmcall +%else + db 0x66, 0x0f, 0x01, 0xcc +%endif +%endmacro + ; ; SecCore Entry Point ; @@ -35,6 +44,31 @@ extern ASM_PFX(SecCoreStartupWithStack) global ASM_PFX(_ModuleEntryPoint) ASM_PFX(_ModuleEntryPoint): + ; + ; Td guest flag is stored in TDX_WORK_AREA which is in Mailbox[0x10,0x20] + ; + %define TDX_WORK_AREA (FixedPcdGet32 (PcdTdMailboxBase) + 0x10) + mov eax, TDX_WORK_AREA + cmp byte[eax], 1 + jne InitStack + + mov rax, TDCALL_TDINFO + tdcall + + ; + ; R8 [31:0] NUM_VCPUS + ; [63:32] MAX_VCPUS + ; R9 [31:0] VCPU_INDEX + ; Td Guest set the VCPU0 as the BSP, others are the APs + ; APs jump to spinloop and get released by DXE's MpInitLib + ; + mov rax, r9 + and rax, 0xffff + test rax, rax + jne ParkAp + +InitStack: + ; ; Fill the temporary RAM with the initial stack value. ; The loop below will seed the heap as well, but that's harmless. @@ -67,3 +101,156 @@ ASM_PFX(_ModuleEntryPoint): sub rsp, 0x20 call ASM_PFX(SecCoreStartupWithStack) + + ; + ; Note: BSP never gets here. APs will be unblocked by DXE + ; + ; R8 [31:0] NUM_VCPUS + ; [63:32] MAX_VCPUS + ; R9 [31:0] VCPU_INDEX + ; +ParkAp: + + mov rbp, r9 + +.do_wait_loop: + mov rsp, FixedPcdGet32 (PcdTdMailboxBase) + + ; + ; register itself in [rsp + CpuArrivalOffset] + ; + mov rax, 1 + lock xadd dword [rsp + CpuArrivalOffset], eax + inc eax + +.check_arrival_cnt: + cmp eax, r8d + je .check_command + mov eax, dword[rsp + CpuArrivalOffset] + jmp .check_arrival_cnt + +.check_command: + mov eax, dword[rsp + CommandOffset] + cmp eax, MpProtectedModeWakeupCommandNoop + je .check_command + + cmp eax, MpProtectedModeWakeupCommandWakeup + je .do_wakeup + + cmp eax, MpProtectedModeWakeupCommandAcceptPages + jne .check_command + + ; Get PhysicalAddress/AcceptSize/PageSize + mov rcx, [rsp + AcceptPageArgsPhysicalStart] + mov rbx, [rsp + AcceptPageArgsAcceptSize] + + ; Accept Page size + mov rdx, [rsp + AcceptPageArgsPageSize] + cmp rdx, SIZE_4KB + je .set_4kb + cmp rdx, SIZE_2MB + je .set_2mb + cmp rdx, SIZE_1GB + je .set_1gb +.set_4kb + mov rdx, 0 + jmp .physical_address +.set_2mb + mov rdx, 1 + jmp .physical_address +.set_1gb + mov rdx, 2 + jmp .physical_address + +.physical_address + ; + ; PhysicalAddress += (CpuId * AcceptSize) + mov eax, ebp + mul ebx + add rcx, rax + +.do_accept_next_range: + + ; + ; Make sure we don't accept page beyond ending page + ; This could happen is AcceptSize crosses the end of region + ; + ;while (PhysicalAddress < PhysicalEnd) { + cmp rcx, [rsp + AcceptPageArgsPhysicalEnd ] + jge .do_finish_command + + ; + ; Save starting address for this region + ; + mov r11, rcx + + ; Size = MIN(AcceptSize, PhysicalEnd - PhysicalAddress); + mov rax, [rsp + AcceptPageArgsPhysicalEnd] + + sub rax, rcx + cmp rax, rbx + jge .do_accept_loop + mov rbx, rax + +.do_accept_loop: + + ; + ; Accept address in rcx + ; + mov rax, TDCALL_TDACCEPTPAGE + ;xor rdx, rdx + tdcall + + ; + ; Keep track of how many accepts per cpu + ; + inc dword[rsp + TalliesOffset + rbp * 4] + + ; + ; Reduce accept size by a page, and increment address + ; + mov r12, [rsp + AcceptPageArgsPageSize] + sub rbx, r12 + add rcx, r12 + + ; + ; We may be given multiple pages to accept, make sure we + ; aren't done + ; + test rbx, rbx + jne .do_accept_loop + + ; + ; Restore address before, and then increment by stride (num-cpus * acceptsize) + ; + mov rcx, r11 + mov eax, r8d + mov rbx, [rsp + AcceptPageArgsAcceptSize] + mul ebx + add rcx, rax + jmp .do_accept_next_range + +.do_finish_command: + mov eax, 0FFFFFFFFh + lock xadd dword [rsp + CpusExitingOffset], eax + dec eax + +.check_exiting_cnt: + cmp eax, 0 + je .do_wait_loop + mov eax, dword[rsp + CpusExitingOffset] + jmp .check_exiting_cnt + +.do_wakeup: + ; + ; BSP sets these variables before unblocking APs + ; RAX: WakeupVectorOffset + ; RBX: Relocated mailbox address + ; RBP: vCpuId + ; + mov rax, 0 + mov eax, dword[rsp + WakeupVectorOffset] + mov rbx, [rsp + WakeupArgsRelocatedMailBox] + nop + jmp rax + jmp $ diff --git a/OvmfPkg/Tcg/Tcg2Dxe/MeasureBootPeCoff.c b/OvmfPkg/Tcg/Tcg2Dxe/MeasureBootPeCoff.c new file mode 100644 index 000000000000..f434fdf6a28d --- /dev/null +++ b/OvmfPkg/Tcg/Tcg2Dxe/MeasureBootPeCoff.c @@ -0,0 +1,405 @@ +/** @file + This module implements measuring PeCoff image for Tcg2 Protocol. + + Caution: This file requires additional review when modified. + This driver will have external input - PE/COFF image. + This external input must be validated carefully to avoid security issue like + buffer overflow, integer overflow. + +Copyright (c) 2015 - 2018, Intel Corporation. All rights reserved.
+SPDX-License-Identifier: BSD-2-Clause-Patent + +**/ + +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +UINTN mTcg2DxeImageSize = 0; + +/** + Reads contents of a PE/COFF image in memory buffer. + + Caution: This function may receive untrusted input. + PE/COFF image is external input, so this function will make sure the PE/COFF image content + read is within the image buffer. + + @param FileHandle Pointer to the file handle to read the PE/COFF image. + @param FileOffset Offset into the PE/COFF image to begin the read operation. + @param ReadSize On input, the size in bytes of the requested read operation. + On output, the number of bytes actually read. + @param Buffer Output buffer that contains the data read from the PE/COFF image. + + @retval EFI_SUCCESS The specified portion of the PE/COFF image was read and the size +**/ +EFI_STATUS +EFIAPI +Tcg2DxeImageRead ( + IN VOID *FileHandle, + IN UINTN FileOffset, + IN OUT UINTN *ReadSize, + OUT VOID *Buffer + ) +{ + UINTN EndPosition; + + if (FileHandle == NULL || ReadSize == NULL || Buffer == NULL) { + return EFI_INVALID_PARAMETER; + } + + if (MAX_ADDRESS - FileOffset < *ReadSize) { + return EFI_INVALID_PARAMETER; + } + + EndPosition = FileOffset + *ReadSize; + if (EndPosition > mTcg2DxeImageSize) { + *ReadSize = (UINT32)(mTcg2DxeImageSize - FileOffset); + } + + if (FileOffset >= mTcg2DxeImageSize) { + *ReadSize = 0; + } + + CopyMem (Buffer, (UINT8 *)((UINTN) FileHandle + FileOffset), *ReadSize); + + return EFI_SUCCESS; +} + +/** + Measure PE image into TPM log based on the authenticode image hashing in + PE/COFF Specification 8.0 Appendix A. + + Caution: This function may receive untrusted input. + PE/COFF image is external input, so this function will validate its data structure + within this image buffer before use. + + Notes: PE/COFF image is checked by BasePeCoffLib PeCoffLoaderGetImageInfo(). + + @param[in] PCRIndex TPM PCR index + @param[in] ImageAddress Start address of image buffer. + @param[in] ImageSize Image size + @param[out] DigestList Digest list of this image. + + @retval EFI_SUCCESS Successfully measure image. + @retval EFI_OUT_OF_RESOURCES No enough resource to measure image. + @retval other error value +**/ +EFI_STATUS +MeasurePeImageAndExtend ( + IN UINT32 PCRIndex, + IN EFI_PHYSICAL_ADDRESS ImageAddress, + IN UINTN ImageSize, + OUT TPML_DIGEST_VALUES *DigestList + ) +{ + EFI_STATUS Status; + EFI_IMAGE_DOS_HEADER *DosHdr; + UINT32 PeCoffHeaderOffset; + EFI_IMAGE_SECTION_HEADER *Section; + UINT8 *HashBase; + UINTN HashSize; + UINTN SumOfBytesHashed; + EFI_IMAGE_SECTION_HEADER *SectionHeader; + UINTN Index; + UINTN Pos; + EFI_IMAGE_OPTIONAL_HEADER_PTR_UNION Hdr; + UINT32 NumberOfRvaAndSizes; + UINT32 CertSize; + HASH_HANDLE HashHandle; + PE_COFF_LOADER_IMAGE_CONTEXT ImageContext; + + HashHandle = 0xFFFFFFFF; // Know bad value + + Status = EFI_UNSUPPORTED; + SectionHeader = NULL; + + // + // Check PE/COFF image + // + ZeroMem (&ImageContext, sizeof (ImageContext)); + ImageContext.Handle = (VOID *) (UINTN) ImageAddress; + mTcg2DxeImageSize = ImageSize; + ImageContext.ImageRead = (PE_COFF_LOADER_READ_FILE) Tcg2DxeImageRead; + + // + // Get information about the image being loaded + // + Status = PeCoffLoaderGetImageInfo (&ImageContext); + if (EFI_ERROR (Status)) { + // + // The information can't be got from the invalid PeImage + // + DEBUG ((DEBUG_INFO, "Tcg2Dxe: PeImage invalid. Cannot retrieve image information.\n")); + goto Finish; + } + + DosHdr = (EFI_IMAGE_DOS_HEADER *) (UINTN) ImageAddress; + PeCoffHeaderOffset = 0; + if (DosHdr->e_magic == EFI_IMAGE_DOS_SIGNATURE) { + PeCoffHeaderOffset = DosHdr->e_lfanew; + } + + Hdr.Pe32 = (EFI_IMAGE_NT_HEADERS32 *)((UINT8 *) (UINTN) ImageAddress + PeCoffHeaderOffset); + if (Hdr.Pe32->Signature != EFI_IMAGE_NT_SIGNATURE) { + Status = EFI_UNSUPPORTED; + goto Finish; + } + + // + // PE/COFF Image Measurement + // + // NOTE: The following codes/steps are based upon the authenticode image hashing in + // PE/COFF Specification 8.0 Appendix A. + // + // + + // 1. Load the image header into memory. + + // 2. Initialize a SHA hash context. + + Status = HashStart (&HashHandle); + if (EFI_ERROR (Status)) { + goto Finish; + } + + // + // Measuring PE/COFF Image Header; + // But CheckSum field and SECURITY data directory (certificate) are excluded + // + + // + // 3. Calculate the distance from the base of the image header to the image checksum address. + // 4. Hash the image header from its base to beginning of the image checksum. + // + HashBase = (UINT8 *) (UINTN) ImageAddress; + if (Hdr.Pe32->OptionalHeader.Magic == EFI_IMAGE_NT_OPTIONAL_HDR32_MAGIC) { + // + // Use PE32 offset + // + NumberOfRvaAndSizes = Hdr.Pe32->OptionalHeader.NumberOfRvaAndSizes; + HashSize = (UINTN) (&Hdr.Pe32->OptionalHeader.CheckSum) - (UINTN) HashBase; + } else { + // + // Use PE32+ offset + // + NumberOfRvaAndSizes = Hdr.Pe32Plus->OptionalHeader.NumberOfRvaAndSizes; + HashSize = (UINTN) (&Hdr.Pe32Plus->OptionalHeader.CheckSum) - (UINTN) HashBase; + } + + Status = HashUpdate (HashHandle, HashBase, HashSize); + if (EFI_ERROR (Status)) { + goto Finish; + } + + // + // 5. Skip over the image checksum (it occupies a single ULONG). + // + if (NumberOfRvaAndSizes <= EFI_IMAGE_DIRECTORY_ENTRY_SECURITY) { + // + // 6. Since there is no Cert Directory in optional header, hash everything + // from the end of the checksum to the end of image header. + // + if (Hdr.Pe32->OptionalHeader.Magic == EFI_IMAGE_NT_OPTIONAL_HDR32_MAGIC) { + // + // Use PE32 offset. + // + HashBase = (UINT8 *) &Hdr.Pe32->OptionalHeader.CheckSum + sizeof (UINT32); + HashSize = Hdr.Pe32->OptionalHeader.SizeOfHeaders - (UINTN) (HashBase - ImageAddress); + } else { + // + // Use PE32+ offset. + // + HashBase = (UINT8 *) &Hdr.Pe32Plus->OptionalHeader.CheckSum + sizeof (UINT32); + HashSize = Hdr.Pe32Plus->OptionalHeader.SizeOfHeaders - (UINTN) (HashBase - ImageAddress); + } + + if (HashSize != 0) { + Status = HashUpdate (HashHandle, HashBase, HashSize); + if (EFI_ERROR (Status)) { + goto Finish; + } + } + } else { + // + // 7. Hash everything from the end of the checksum to the start of the Cert Directory. + // + if (Hdr.Pe32->OptionalHeader.Magic == EFI_IMAGE_NT_OPTIONAL_HDR32_MAGIC) { + // + // Use PE32 offset + // + HashBase = (UINT8 *) &Hdr.Pe32->OptionalHeader.CheckSum + sizeof (UINT32); + HashSize = (UINTN) (&Hdr.Pe32->OptionalHeader.DataDirectory[EFI_IMAGE_DIRECTORY_ENTRY_SECURITY]) - (UINTN) HashBase; + } else { + // + // Use PE32+ offset + // + HashBase = (UINT8 *) &Hdr.Pe32Plus->OptionalHeader.CheckSum + sizeof (UINT32); + HashSize = (UINTN) (&Hdr.Pe32Plus->OptionalHeader.DataDirectory[EFI_IMAGE_DIRECTORY_ENTRY_SECURITY]) - (UINTN) HashBase; + } + + if (HashSize != 0) { + Status = HashUpdate (HashHandle, HashBase, HashSize); + if (EFI_ERROR (Status)) { + goto Finish; + } + } + + // + // 8. Skip over the Cert Directory. (It is sizeof(IMAGE_DATA_DIRECTORY) bytes.) + // 9. Hash everything from the end of the Cert Directory to the end of image header. + // + if (Hdr.Pe32->OptionalHeader.Magic == EFI_IMAGE_NT_OPTIONAL_HDR32_MAGIC) { + // + // Use PE32 offset + // + HashBase = (UINT8 *) &Hdr.Pe32->OptionalHeader.DataDirectory[EFI_IMAGE_DIRECTORY_ENTRY_SECURITY + 1]; + HashSize = Hdr.Pe32->OptionalHeader.SizeOfHeaders - (UINTN) (HashBase - ImageAddress); + } else { + // + // Use PE32+ offset + // + HashBase = (UINT8 *) &Hdr.Pe32Plus->OptionalHeader.DataDirectory[EFI_IMAGE_DIRECTORY_ENTRY_SECURITY + 1]; + HashSize = Hdr.Pe32Plus->OptionalHeader.SizeOfHeaders - (UINTN) (HashBase - ImageAddress); + } + + if (HashSize != 0) { + Status = HashUpdate (HashHandle, HashBase, HashSize); + if (EFI_ERROR (Status)) { + goto Finish; + } + } + } + + // + // 10. Set the SUM_OF_BYTES_HASHED to the size of the header + // + if (Hdr.Pe32->OptionalHeader.Magic == EFI_IMAGE_NT_OPTIONAL_HDR32_MAGIC) { + // + // Use PE32 offset + // + SumOfBytesHashed = Hdr.Pe32->OptionalHeader.SizeOfHeaders; + } else { + // + // Use PE32+ offset + // + SumOfBytesHashed = Hdr.Pe32Plus->OptionalHeader.SizeOfHeaders; + } + + // + // 11. Build a temporary table of pointers to all the IMAGE_SECTION_HEADER + // structures in the image. The 'NumberOfSections' field of the image + // header indicates how big the table should be. Do not include any + // IMAGE_SECTION_HEADERs in the table whose 'SizeOfRawData' field is zero. + // + SectionHeader = (EFI_IMAGE_SECTION_HEADER *) AllocateZeroPool (sizeof (EFI_IMAGE_SECTION_HEADER) * Hdr.Pe32->FileHeader.NumberOfSections); + if (SectionHeader == NULL) { + Status = EFI_OUT_OF_RESOURCES; + goto Finish; + } + + // + // 12. Using the 'PointerToRawData' in the referenced section headers as + // a key, arrange the elements in the table in ascending order. In other + // words, sort the section headers according to the disk-file offset of + // the section. + // + Section = (EFI_IMAGE_SECTION_HEADER *) ( + (UINT8 *) (UINTN) ImageAddress + + PeCoffHeaderOffset + + sizeof(UINT32) + + sizeof(EFI_IMAGE_FILE_HEADER) + + Hdr.Pe32->FileHeader.SizeOfOptionalHeader + ); + for (Index = 0; Index < Hdr.Pe32->FileHeader.NumberOfSections; Index++) { + Pos = Index; + while ((Pos > 0) && (Section->PointerToRawData < SectionHeader[Pos - 1].PointerToRawData)) { + CopyMem (&SectionHeader[Pos], &SectionHeader[Pos - 1], sizeof(EFI_IMAGE_SECTION_HEADER)); + Pos--; + } + CopyMem (&SectionHeader[Pos], Section, sizeof(EFI_IMAGE_SECTION_HEADER)); + Section += 1; + } + + // + // 13. Walk through the sorted table, bring the corresponding section + // into memory, and hash the entire section (using the 'SizeOfRawData' + // field in the section header to determine the amount of data to hash). + // 14. Add the section's 'SizeOfRawData' to SUM_OF_BYTES_HASHED . + // 15. Repeat steps 13 and 14 for all the sections in the sorted table. + // + for (Index = 0; Index < Hdr.Pe32->FileHeader.NumberOfSections; Index++) { + Section = (EFI_IMAGE_SECTION_HEADER *) &SectionHeader[Index]; + if (Section->SizeOfRawData == 0) { + continue; + } + HashBase = (UINT8 *) (UINTN) ImageAddress + Section->PointerToRawData; + HashSize = (UINTN) Section->SizeOfRawData; + + Status = HashUpdate (HashHandle, HashBase, HashSize); + if (EFI_ERROR (Status)) { + goto Finish; + } + + SumOfBytesHashed += HashSize; + } + + // + // 16. If the file size is greater than SUM_OF_BYTES_HASHED, there is extra + // data in the file that needs to be added to the hash. This data begins + // at file offset SUM_OF_BYTES_HASHED and its length is: + // FileSize - (CertDirectory->Size) + // + if (ImageSize > SumOfBytesHashed) { + HashBase = (UINT8 *) (UINTN) ImageAddress + SumOfBytesHashed; + + if (NumberOfRvaAndSizes <= EFI_IMAGE_DIRECTORY_ENTRY_SECURITY) { + CertSize = 0; + } else { + if (Hdr.Pe32->OptionalHeader.Magic == EFI_IMAGE_NT_OPTIONAL_HDR32_MAGIC) { + // + // Use PE32 offset. + // + CertSize = Hdr.Pe32->OptionalHeader.DataDirectory[EFI_IMAGE_DIRECTORY_ENTRY_SECURITY].Size; + } else { + // + // Use PE32+ offset. + // + CertSize = Hdr.Pe32Plus->OptionalHeader.DataDirectory[EFI_IMAGE_DIRECTORY_ENTRY_SECURITY].Size; + } + } + + if (ImageSize > CertSize + SumOfBytesHashed) { + HashSize = (UINTN) (ImageSize - CertSize - SumOfBytesHashed); + + Status = HashUpdate (HashHandle, HashBase, HashSize); + if (EFI_ERROR (Status)) { + goto Finish; + } + } else if (ImageSize < CertSize + SumOfBytesHashed) { + Status = EFI_UNSUPPORTED; + goto Finish; + } + } + + // + // 17. Finalize the SHA hash. + // + Status = HashCompleteAndExtend (HashHandle, PCRIndex, NULL, 0, DigestList); + if (EFI_ERROR (Status)) { + goto Finish; + } + +Finish: + if (SectionHeader != NULL) { + FreePool (SectionHeader); + } + + return Status; +} diff --git a/OvmfPkg/Tcg/Tcg2Dxe/TdTcg2Dxe.c b/OvmfPkg/Tcg/Tcg2Dxe/TdTcg2Dxe.c new file mode 100644 index 000000000000..d3a3e8770af4 --- /dev/null +++ b/OvmfPkg/Tcg/Tcg2Dxe/TdTcg2Dxe.c @@ -0,0 +1,2615 @@ +/** @file + This module implements EFI TD Protocol. + + Copyright (c) 2020 - 2021, Intel Corporation. All rights reserved.
+ SPDX-License-Identifier: BSD-2-Clause-Patent + +**/ + +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + + +#define PERF_ID_TD_TCG2_DXE 0x3130 +// TODO TDX command/response size +#define TCG2_DEFAULT_MAX_COMMAND_SIZE 0x1000 +#define TCG2_DEFAULT_MAX_RESPONSE_SIZE 0x1000 +#define TCG_EVENT_LOG_AREA_COUNT_MAX 1 + +typedef struct { + CHAR16 *VariableName; + EFI_GUID *VendorGuid; +} VARIABLE_TYPE; + +typedef struct { + EFI_GUID *EventGuid; + EFI_TCG2_EVENT_LOG_FORMAT LogFormat; +} TCG2_EVENT_INFO_STRUCT; + +typedef struct { + EFI_TCG2_EVENT_LOG_FORMAT EventLogFormat; + EFI_PHYSICAL_ADDRESS Lasa; + UINT64 Laml; + UINTN EventLogSize; + UINT8 *LastEvent; + BOOLEAN EventLogStarted; + BOOLEAN EventLogTruncated; + UINTN Next800155EventOffset; +} TCG_EVENT_LOG_AREA_STRUCT; + +typedef struct _TCG_DXE_DATA { + EFI_TCG2_BOOT_SERVICE_CAPABILITY BsCap; + TCG_EVENT_LOG_AREA_STRUCT EventLogAreaStruct[TCG_EVENT_LOG_AREA_COUNT_MAX]; + BOOLEAN GetEventLogCalled[TCG_EVENT_LOG_AREA_COUNT_MAX]; + TCG_EVENT_LOG_AREA_STRUCT FinalEventLogAreaStruct[TCG_EVENT_LOG_AREA_COUNT_MAX]; + EFI_TCG2_FINAL_EVENTS_TABLE *FinalEventsTable[TCG_EVENT_LOG_AREA_COUNT_MAX]; +} TCG_DXE_DATA; + +typedef struct{ + TPMI_ALG_HASH HashAlgo; + UINT16 HashSize; + UINT32 HashMask; +}TDX_HASH_INFO; + +TCG2_EVENT_INFO_STRUCT mTcg2EventInfo[] = { + {&gTcgEvent2EntryHobGuid, EFI_TCG2_EVENT_LOG_FORMAT_TCG_2}, +}; + +TCG_DXE_DATA mTcgDxeData = { + { + sizeof (EFI_TCG2_BOOT_SERVICE_CAPABILITY), // Size + { 1, 1 }, // StructureVersion + { 1, 1 }, // ProtocolVersion + EFI_TCG2_BOOT_HASH_ALG_SHA384, // HashAlgorithmBitmap + EFI_TCG2_EVENT_LOG_FORMAT_TCG_2, // SupportedEventLogs + TRUE, // TPMPresentFlag + TCG2_DEFAULT_MAX_COMMAND_SIZE, // MaxCommandSize + TCG2_DEFAULT_MAX_RESPONSE_SIZE, // MaxResponseSize + 0, // ManufacturerID + 0, // NumberOfPCRBanks + 0, // ActivePcrBanks + }, +}; + +UINTN mBootAttempts = 0; +CHAR16 mBootVarName[] = L"BootOrder"; + +VARIABLE_TYPE mVariableType[] = { + {EFI_SECURE_BOOT_MODE_NAME, &gEfiGlobalVariableGuid}, + {EFI_PLATFORM_KEY_NAME, &gEfiGlobalVariableGuid}, + {EFI_KEY_EXCHANGE_KEY_NAME, &gEfiGlobalVariableGuid}, + {EFI_IMAGE_SECURITY_DATABASE, &gEfiImageSecurityDatabaseGuid}, + {EFI_IMAGE_SECURITY_DATABASE1, &gEfiImageSecurityDatabaseGuid}, +}; + +EFI_TDX_EVENTLOG_ACPI_TABLE mTdxEventlogAcpiTemplate = { + { + EFI_TDX_EVENTLOG_ACPI_TABLE_SIGNATURE, + sizeof(mTdxEventlogAcpiTemplate), + EFI_TDX_EVENTLOG_ACPIT_TABLE_REVISION, + // + // Compiler initializes the remaining bytes to 0 + // These fields should be filled in production + // + }, + 0, // rsvd + 0, // laml + 0, // lasa +}; + +TDX_HASH_INFO mHashInfo[] = { + {TPM_ALG_SHA384, SHA384_DIGEST_SIZE, HASH_ALG_SHA384} +}; + +/** + Get hash size based on Algo + + @param[in] HashAlgo Hash Algorithm Id. + + @return Size of the hash. +**/ +UINT16 +GetHashSizeFromAlgo( + IN TPMI_ALG_HASH HashAlgo + ) +{ + UINTN Index; + + for(Index = 0; Index < sizeof(mHashInfo)/sizeof(mHashInfo[0]); Index++){ + if(mHashInfo[Index].HashAlgo == HashAlgo){ + return mHashInfo[Index].HashSize; + } + } + + return 0; +} + +/** + Get hash mask based on Algo + + @param[in] HashAlgo Hash Algorithm Id. + + @return Hash mask. +**/ +UINT32 +GetHashMaskFromAlgo( + IN TPMI_ALG_HASH HashAlgo + ) +{ + UINTN Index; + + for(Index = 0; Index < sizeof(mHashInfo)/sizeof(mHashInfo[0]); Index++){ + if(mHashInfo[Index].HashAlgo == HashAlgo){ + return mHashInfo[Index].HashMask; + } + } + + return 0; +} + +/** + Copy TPML_DIGEST_VALUES into a buffer + + @param[in,out] Buffer Buffer to hold copied TPML_DIGEST_VALUES compact binary. + @param[in] DigestList TPML_DIGEST_VALUES to be copied. + @param[in] HashAlgorithmMask HASH bits corresponding to the desired digests to copy. + + @return The end of buffer to hold TPML_DIGEST_VALUES. +**/ +VOID * +CopyDigestListToBuffer ( + IN OUT VOID *Buffer, + IN TPML_DIGEST_VALUES *DigestList, + IN UINT32 HashAlgorithmMask + ) +{ + UINTN Index; + UINT16 DigestSize; + UINT32 DigestListCount; + UINT32 *DigestListCountPtr; + + DigestListCountPtr = (UINT32 *) Buffer; + DigestListCount = 0; + Buffer = (UINT8 *)Buffer + sizeof(DigestList->count); + for (Index = 0; Index < DigestList->count; Index++) { + if((DigestList->digests[Index].hashAlg & HashAlgorithmMask) == 0){ + DEBUG ((EFI_D_ERROR, "WARNING: TPM2 Event log has HashAlg unsupported by PCR bank (0x%x)\n", DigestList->digests[Index].hashAlg)); + continue; + } + CopyMem (Buffer, &DigestList->digests[Index].hashAlg, sizeof(DigestList->digests[Index].hashAlg)); + Buffer = (UINT8 *)Buffer + sizeof(DigestList->digests[Index].hashAlg); + DigestSize = GetHashSizeFromAlgo (DigestList->digests[Index].hashAlg); + CopyMem (Buffer, &DigestList->digests[Index].digest, DigestSize); + Buffer = (UINT8 *)Buffer + DigestSize; + DigestListCount++; + } + WriteUnaligned32 (DigestListCountPtr, DigestListCount); + + return Buffer; +} + +EFI_HANDLE mImageHandle; + +/** + Measure PE image into TPM log based on the authenticode image hashing in + PE/COFF Specification 8.0 Appendix A. + + Caution: This function may receive untrusted input. + PE/COFF image is external input, so this function will validate its data structure + within this image buffer before use. + + Notes: PE/COFF image is checked by BasePeCoffLib PeCoffLoaderGetImageInfo(). + + @param[in] PCRIndex TPM PCR index + @param[in] ImageAddress Start address of image buffer. + @param[in] ImageSize Image size + @param[out] DigestList Digest list of this image. + + @retval EFI_SUCCESS Successfully measure image. + @retval EFI_OUT_OF_RESOURCES No enough resource to measure image. + @retval other error value +**/ +EFI_STATUS +MeasurePeImageAndExtend ( + IN UINT32 PCRIndex, + IN EFI_PHYSICAL_ADDRESS ImageAddress, + IN UINTN ImageSize, + OUT TPML_DIGEST_VALUES *DigestList + ); + +#define COLUME_SIZE (16 * 2) +/** + + This function dump raw data. + + @param Data raw data + @param Size raw data size + +**/ +VOID +InternalDumpData ( + IN UINT8 *Data, + IN UINTN Size + ) +{ + UINTN Index; + for (Index = 0; Index < Size; Index++) { + DEBUG ((EFI_D_INFO, Index == COLUME_SIZE/2 ? " | %02x" : " %02x", (UINTN)Data[Index])); + } +} + +/** + + This function initialize TCG_PCR_EVENT2_HDR for EV_NO_ACTION Event Type other than EFI Specification ID event + The behavior is defined by TCG PC Client PFP Spec. Section 9.3.4 EV_NO_ACTION Event Types + + @param[in, out] NoActionEvent Event Header of EV_NO_ACTION Event + @param[in] EventSize Event Size of the EV_NO_ACTION Event + +**/ +VOID +InitNoActionEvent ( + IN OUT TCG_PCR_EVENT2_HDR *NoActionEvent, + IN UINT32 EventSize + ) +{ + UINT32 DigestListCount; + TPMI_ALG_HASH HashAlgId; + UINT8 *DigestBuffer; + + DigestBuffer = (UINT8 *)NoActionEvent->Digests.digests; + DigestListCount = 0; + + NoActionEvent->PCRIndex = 0; + NoActionEvent->EventType = EV_NO_ACTION; + + // + // Set Hash count & hashAlg accordingly, while Digest.digests[n].digest to all 0 + // + ZeroMem (&NoActionEvent->Digests, sizeof(NoActionEvent->Digests)); + + if ((mTcgDxeData.BsCap.ActivePcrBanks & EFI_TCG2_BOOT_HASH_ALG_SHA384) != 0) { + HashAlgId = TPM_ALG_SHA384; + CopyMem (DigestBuffer, &HashAlgId, sizeof(TPMI_ALG_HASH)); + DigestBuffer += sizeof(TPMI_ALG_HASH) + GetHashSizeFromAlgo (HashAlgId); + DigestListCount++; + } + + // + // Set Digests Count + // + WriteUnaligned32 ((UINT32 *)&NoActionEvent->Digests.count, DigestListCount); + + // + // Set Event Size + // + WriteUnaligned32((UINT32 *)DigestBuffer, EventSize); +} + +/** + + This function dump raw data with colume format. + + @param Data raw data + @param Size raw data size + +**/ +VOID +InternalDumpHex ( + IN UINT8 *Data, + IN UINTN Size + ) +{ + UINTN Index; + UINTN Count; + UINTN Left; + + Count = Size / COLUME_SIZE; + Left = Size % COLUME_SIZE; + for (Index = 0; Index < Count; Index++) { + DEBUG ((EFI_D_INFO, "%04x: ", Index * COLUME_SIZE)); + InternalDumpData (Data + Index * COLUME_SIZE, COLUME_SIZE); + DEBUG ((EFI_D_INFO, "\n")); + } + + if (Left != 0) { + DEBUG ((EFI_D_INFO, "%04x: ", Index * COLUME_SIZE)); + InternalDumpData (Data + Index * COLUME_SIZE, Left); + DEBUG ((EFI_D_INFO, "\n")); + } +} + +/** + Get All processors EFI_CPU_LOCATION in system. LocationBuf is allocated inside the function + Caller is responsible to free LocationBuf. + + @param[out] LocationBuf Returns Processor Location Buffer. + @param[out] Num Returns processor number. + + @retval EFI_SUCCESS Operation completed successfully. + @retval EFI_UNSUPPORTED MpService protocol not found. + +**/ +EFI_STATUS +GetProcessorsCpuLocation ( + OUT EFI_CPU_PHYSICAL_LOCATION **LocationBuf, + OUT UINTN *Num + ) +{ + EFI_STATUS Status; + EFI_MP_SERVICES_PROTOCOL *MpProtocol; + UINTN ProcessorNum; + UINTN EnabledProcessorNum; + EFI_PROCESSOR_INFORMATION ProcessorInfo; + EFI_CPU_PHYSICAL_LOCATION *ProcessorLocBuf; + UINTN Index; + + Status = gBS->LocateProtocol (&gEfiMpServiceProtocolGuid, NULL, (VOID **) &MpProtocol); + if (EFI_ERROR (Status)) { + // + // MP protocol is not installed + // + return EFI_UNSUPPORTED; + } + + Status = MpProtocol->GetNumberOfProcessors( + MpProtocol, + &ProcessorNum, + &EnabledProcessorNum + ); + if (EFI_ERROR(Status)){ + return Status; + } + + Status = gBS->AllocatePool( + EfiBootServicesData, + sizeof(EFI_CPU_PHYSICAL_LOCATION) * ProcessorNum, + (VOID **) &ProcessorLocBuf + ); + if (EFI_ERROR(Status)){ + return Status; + } + + // + // Get each processor Location info + // + for (Index = 0; Index < ProcessorNum; Index++) { + Status = MpProtocol->GetProcessorInfo( + MpProtocol, + Index, + &ProcessorInfo + ); + if (EFI_ERROR(Status)){ + FreePool(ProcessorLocBuf); + return Status; + } + + // + // Get all Processor Location info & measure + // + CopyMem( + &ProcessorLocBuf[Index], + &ProcessorInfo.Location, + sizeof(EFI_CPU_PHYSICAL_LOCATION) + ); + } + + *LocationBuf = ProcessorLocBuf; + *Num = ProcessorNum; + + return Status; +} + +/** + The EFI_TCG2_PROTOCOL GetCapability function call provides protocol + capability information and state information. + + @param[in] This Indicates the calling context + @param[in, out] ProtocolCapability The caller allocates memory for a EFI_TCG2_BOOT_SERVICE_CAPABILITY + structure and sets the size field to the size of the structure allocated. + The callee fills in the fields with the EFI protocol capability information + and the current EFI TCG2 state information up to the number of fields which + fit within the size of the structure passed in. + + @retval EFI_SUCCESS Operation completed successfully. + @retval EFI_DEVICE_ERROR The command was unsuccessful. + The ProtocolCapability variable will not be populated. + @retval EFI_INVALID_PARAMETER One or more of the parameters are incorrect. + The ProtocolCapability variable will not be populated. + @retval EFI_BUFFER_TOO_SMALL The ProtocolCapability variable is too small to hold the full response. + It will be partially populated (required Size field will be set). +**/ +EFI_STATUS +EFIAPI +Tcg2GetCapability ( + IN EFI_TCG2_PROTOCOL *This, + IN OUT EFI_TCG2_BOOT_SERVICE_CAPABILITY *ProtocolCapability + ) +{ + DEBUG ((DEBUG_INFO, "TdTcg2GetCapability\n")); + return EFI_SUCCESS; +} + +/** + This function dump PCR event. + + @param[in] EventHdr TCG PCR event structure. +**/ +VOID +DumpEvent ( + IN TCG_PCR_EVENT_HDR *EventHdr + ) +{ + UINTN Index; + + DEBUG ((EFI_D_INFO, " Event:\n")); + DEBUG ((EFI_D_INFO, " PCRIndex - %d\n", EventHdr->PCRIndex)); + DEBUG ((EFI_D_INFO, " EventType - 0x%08x\n", EventHdr->EventType)); + DEBUG ((EFI_D_INFO, " Digest - ")); + for (Index = 0; Index < sizeof(TCG_DIGEST); Index++) { + DEBUG ((EFI_D_INFO, "%02x ", EventHdr->Digest.digest[Index])); + } + DEBUG ((EFI_D_INFO, "\n")); + DEBUG ((EFI_D_INFO, " EventSize - 0x%08x\n", EventHdr->EventSize)); + InternalDumpHex ((UINT8 *)(EventHdr + 1), EventHdr->EventSize); +} + +/** + This function dump TCG_EfiSpecIDEventStruct. + + @param[in] TcgEfiSpecIdEventStruct A pointer to TCG_EfiSpecIDEventStruct. +**/ +VOID +DumpTcgEfiSpecIdEventStruct ( + IN TCG_EfiSpecIDEventStruct *TcgEfiSpecIdEventStruct + ) +{ + TCG_EfiSpecIdEventAlgorithmSize *DigestSize; + UINTN Index; + UINT8 *VendorInfoSize; + UINT8 *VendorInfo; + UINT32 NumberOfAlgorithms; + + DEBUG ((EFI_D_INFO, " TCG_EfiSpecIDEventStruct:\n")); + DEBUG ((EFI_D_INFO, " signature - '")); + for (Index = 0; Index < sizeof(TcgEfiSpecIdEventStruct->signature); Index++) { + DEBUG ((EFI_D_INFO, "%c", TcgEfiSpecIdEventStruct->signature[Index])); + } + DEBUG ((EFI_D_INFO, "'\n")); + DEBUG ((EFI_D_INFO, " platformClass - 0x%08x\n", TcgEfiSpecIdEventStruct->platformClass)); + DEBUG ((EFI_D_INFO, " specVersion - %d.%d%d\n", TcgEfiSpecIdEventStruct->specVersionMajor, TcgEfiSpecIdEventStruct->specVersionMinor, TcgEfiSpecIdEventStruct->specErrata)); + DEBUG ((EFI_D_INFO, " uintnSize - 0x%02x\n", TcgEfiSpecIdEventStruct->uintnSize)); + + CopyMem (&NumberOfAlgorithms, TcgEfiSpecIdEventStruct + 1, sizeof(NumberOfAlgorithms)); + DEBUG ((EFI_D_INFO, " NumberOfAlgorithms - 0x%08x\n", NumberOfAlgorithms)); + + DigestSize = (TCG_EfiSpecIdEventAlgorithmSize *)((UINT8 *)TcgEfiSpecIdEventStruct + sizeof(*TcgEfiSpecIdEventStruct) + sizeof(NumberOfAlgorithms)); + for (Index = 0; Index < NumberOfAlgorithms; Index++) { + DEBUG ((EFI_D_INFO, " digest(%d)\n", Index)); + DEBUG ((EFI_D_INFO, " algorithmId - 0x%04x\n", DigestSize[Index].algorithmId)); + DEBUG ((EFI_D_INFO, " digestSize - 0x%04x\n", DigestSize[Index].digestSize)); + } + VendorInfoSize = (UINT8 *)&DigestSize[NumberOfAlgorithms]; + DEBUG ((EFI_D_INFO, " VendorInfoSize - 0x%02x\n", *VendorInfoSize)); + VendorInfo = VendorInfoSize + 1; + DEBUG ((EFI_D_INFO, " VendorInfo - ")); + for (Index = 0; Index < *VendorInfoSize; Index++) { + DEBUG ((EFI_D_INFO, "%02x ", VendorInfo[Index])); + } + DEBUG ((EFI_D_INFO, "\n")); +} + +/** + This function get size of TCG_EfiSpecIDEventStruct. + + @param[in] TcgEfiSpecIdEventStruct A pointer to TCG_EfiSpecIDEventStruct. +**/ +UINTN +GetTcgEfiSpecIdEventStructSize ( + IN TCG_EfiSpecIDEventStruct *TcgEfiSpecIdEventStruct + ) +{ + TCG_EfiSpecIdEventAlgorithmSize *DigestSize; + UINT8 *VendorInfoSize; + UINT32 NumberOfAlgorithms; + + CopyMem (&NumberOfAlgorithms, TcgEfiSpecIdEventStruct + 1, sizeof(NumberOfAlgorithms)); + + DigestSize = (TCG_EfiSpecIdEventAlgorithmSize *)((UINT8 *)TcgEfiSpecIdEventStruct + sizeof(*TcgEfiSpecIdEventStruct) + sizeof(NumberOfAlgorithms)); + VendorInfoSize = (UINT8 *)&DigestSize[NumberOfAlgorithms]; + return sizeof(TCG_EfiSpecIDEventStruct) + sizeof(UINT32) + (NumberOfAlgorithms * sizeof(TCG_EfiSpecIdEventAlgorithmSize)) + sizeof(UINT8) + (*VendorInfoSize); +} + +/** + This function dump PCR event 2. + + @param[in] TcgPcrEvent2 TCG PCR event 2 structure. +**/ +VOID +DumpEvent2 ( + IN TCG_PCR_EVENT2 *TcgPcrEvent2 + ) +{ + UINT32 DigestIndex; + UINT32 DigestCount; + TPMI_ALG_HASH HashAlgo; + UINT32 DigestSize; + UINT8 *DigestBuffer; + UINT32 EventSize; + UINT8 *EventBuffer; + + DEBUG ((EFI_D_INFO, " Event:\n")); + DEBUG ((EFI_D_INFO, " PCRIndex - %d\n", TcgPcrEvent2->PCRIndex)); + DEBUG ((EFI_D_INFO, " EventType - 0x%08x\n", TcgPcrEvent2->EventType)); + + DEBUG ((EFI_D_INFO, " DigestCount: 0x%08x\n", TcgPcrEvent2->Digest.count)); + + DigestCount = TcgPcrEvent2->Digest.count; + HashAlgo = TcgPcrEvent2->Digest.digests[0].hashAlg; + DigestBuffer = (UINT8 *)&TcgPcrEvent2->Digest.digests[0].digest; + for (DigestIndex = 0; DigestIndex < DigestCount; DigestIndex++) { + DEBUG ((EFI_D_INFO, " HashAlgo : 0x%04x\n", HashAlgo)); + DEBUG ((EFI_D_INFO, " Digest(%d): \n", DigestIndex)); + DigestSize = GetHashSizeFromAlgo (HashAlgo); + InternalDumpHex(DigestBuffer, DigestSize); + // + // Prepare next + // + CopyMem (&HashAlgo, DigestBuffer + DigestSize, sizeof(TPMI_ALG_HASH)); + DigestBuffer = DigestBuffer + DigestSize + sizeof(TPMI_ALG_HASH); + } + DEBUG ((EFI_D_INFO, "\n")); + DigestBuffer = DigestBuffer - sizeof(TPMI_ALG_HASH); + + CopyMem (&EventSize, DigestBuffer, sizeof(TcgPcrEvent2->EventSize)); + DEBUG ((EFI_D_INFO, " EventSize - 0x%08x\n", EventSize)); + EventBuffer = DigestBuffer + sizeof(TcgPcrEvent2->EventSize); + InternalDumpHex (EventBuffer, EventSize); +} + +/** + This function returns size of TCG PCR event 2. + + @param[in] TcgPcrEvent2 TCG PCR event 2 structure. + + @return size of TCG PCR event 2. +**/ +UINTN +GetPcrEvent2Size ( + IN TCG_PCR_EVENT2 *TcgPcrEvent2 + ) +{ + UINT32 DigestIndex; + UINT32 DigestCount; + TPMI_ALG_HASH HashAlgo; + UINT32 DigestSize; + UINT8 *DigestBuffer; + UINT32 EventSize; + UINT8 *EventBuffer; + + DigestCount = TcgPcrEvent2->Digest.count; + HashAlgo = TcgPcrEvent2->Digest.digests[0].hashAlg; + DigestBuffer = (UINT8 *)&TcgPcrEvent2->Digest.digests[0].digest; + for (DigestIndex = 0; DigestIndex < DigestCount; DigestIndex++) { + DigestSize = GetHashSizeFromAlgo (HashAlgo); + // + // Prepare next + // + CopyMem (&HashAlgo, DigestBuffer + DigestSize, sizeof(TPMI_ALG_HASH)); + DigestBuffer = DigestBuffer + DigestSize + sizeof(TPMI_ALG_HASH); + } + DigestBuffer = DigestBuffer - sizeof(TPMI_ALG_HASH); + + CopyMem (&EventSize, DigestBuffer, sizeof(TcgPcrEvent2->EventSize)); + EventBuffer = DigestBuffer + sizeof(TcgPcrEvent2->EventSize); + + return (UINTN)EventBuffer + EventSize - (UINTN)TcgPcrEvent2; +} + +/** + This function dump event log. + TDVF only supports EFI_TCG2_EVENT_LOG_FORMAT_TCG_2 + + @param[in] EventLogFormat The type of the event log for which the information is requested. + @param[in] EventLogLocation A pointer to the memory address of the event log. + @param[in] EventLogLastEntry If the Event Log contains more than one entry, this is a pointer to the + address of the start of the last entry in the event log in memory. + @param[in] FinalEventsTable A pointer to the memory address of the final event table. +**/ +VOID +DumpEventLog ( + IN EFI_TCG2_EVENT_LOG_FORMAT EventLogFormat, + IN EFI_PHYSICAL_ADDRESS EventLogLocation, + IN EFI_PHYSICAL_ADDRESS EventLogLastEntry, + IN EFI_TCG2_FINAL_EVENTS_TABLE *FinalEventsTable + ) +{ + TCG_PCR_EVENT_HDR *EventHdr; + TCG_PCR_EVENT2 *TcgPcrEvent2; + TCG_EfiSpecIDEventStruct *TcgEfiSpecIdEventStruct; + UINTN NumberOfEvents; + + DEBUG ((EFI_D_INFO, "EventLogFormat: (0x%x)\n", EventLogFormat)); + + switch (EventLogFormat) { + case EFI_TCG2_EVENT_LOG_FORMAT_TCG_2: + // + // Dump first event + // + EventHdr = (TCG_PCR_EVENT_HDR *)(UINTN)EventLogLocation; + DumpEvent (EventHdr); + + TcgEfiSpecIdEventStruct = (TCG_EfiSpecIDEventStruct *)(EventHdr + 1); + DumpTcgEfiSpecIdEventStruct (TcgEfiSpecIdEventStruct); + + TcgPcrEvent2 = (TCG_PCR_EVENT2 *)((UINTN)TcgEfiSpecIdEventStruct + GetTcgEfiSpecIdEventStructSize (TcgEfiSpecIdEventStruct)); + while ((UINTN)TcgPcrEvent2 <= EventLogLastEntry) { + DumpEvent2 (TcgPcrEvent2); + TcgPcrEvent2 = (TCG_PCR_EVENT2 *)((UINTN)TcgPcrEvent2 + GetPcrEvent2Size (TcgPcrEvent2)); + } + + if (FinalEventsTable == NULL) { + DEBUG ((EFI_D_INFO, "FinalEventsTable: NOT FOUND\n")); + } else { + DEBUG ((EFI_D_INFO, "FinalEventsTable: (0x%x)\n", FinalEventsTable)); + DEBUG ((EFI_D_INFO, " Version: (0x%x)\n", FinalEventsTable->Version)); + DEBUG ((EFI_D_INFO, " NumberOfEvents: (0x%x)\n", FinalEventsTable->NumberOfEvents)); + + TcgPcrEvent2 = (TCG_PCR_EVENT2 *)(UINTN)(FinalEventsTable + 1); + for (NumberOfEvents = 0; NumberOfEvents < FinalEventsTable->NumberOfEvents; NumberOfEvents++) { + DumpEvent2 (TcgPcrEvent2); + TcgPcrEvent2 = (TCG_PCR_EVENT2 *)((UINTN)TcgPcrEvent2 + GetPcrEvent2Size (TcgPcrEvent2)); + } + } + break; + } + + return ; +} + +/** + The EFI_TCG2_PROTOCOL Get Event Log function call allows a caller to + retrieve the address of a given event log and its last entry. + + @param[in] This Indicates the calling context + @param[in] EventLogFormat The type of the event log for which the information is requested. + @param[out] EventLogLocation A pointer to the memory address of the event log. + @param[out] EventLogLastEntry If the Event Log contains more than one entry, this is a pointer to the + address of the start of the last entry in the event log in memory. + @param[out] EventLogTruncated If the Event Log is missing at least one entry because an event would + have exceeded the area allocated for events, this value is set to TRUE. + Otherwise, the value will be FALSE and the Event Log will be complete. + + @retval EFI_SUCCESS Operation completed successfully. + @retval EFI_INVALID_PARAMETER One or more of the parameters are incorrect + (e.g. asking for an event log whose format is not supported). +**/ +EFI_STATUS +EFIAPI +Tcg2GetEventLog ( + IN EFI_TCG2_PROTOCOL *This, + IN EFI_TCG2_EVENT_LOG_FORMAT EventLogFormat, + OUT EFI_PHYSICAL_ADDRESS *EventLogLocation, + OUT EFI_PHYSICAL_ADDRESS *EventLogLastEntry, + OUT BOOLEAN *EventLogTruncated + ) +{ + UINTN Index; + + DEBUG ((EFI_D_INFO, "Tcg2GetEventLog ... (0x%x)\n", EventLogFormat)); + + if (This == NULL) { + return EFI_INVALID_PARAMETER; + } + + for (Index = 0; Index < sizeof(mTcg2EventInfo)/sizeof(mTcg2EventInfo[0]); Index++) { + if (EventLogFormat == mTcg2EventInfo[Index].LogFormat) { + break; + } + } + + if (Index == sizeof(mTcg2EventInfo)/sizeof(mTcg2EventInfo[0])) { + return EFI_INVALID_PARAMETER; + } + + if ((mTcg2EventInfo[Index].LogFormat & mTcgDxeData.BsCap.SupportedEventLogs) == 0) { + return EFI_INVALID_PARAMETER; + } + + if (!mTcgDxeData.BsCap.TPMPresentFlag) { + if (EventLogLocation != NULL) { + *EventLogLocation = 0; + } + if (EventLogLastEntry != NULL) { + *EventLogLastEntry = 0; + } + if (EventLogTruncated != NULL) { + *EventLogTruncated = FALSE; + } + return EFI_SUCCESS; + } + + if (EventLogLocation != NULL) { + *EventLogLocation = mTcgDxeData.EventLogAreaStruct[Index].Lasa; + DEBUG ((EFI_D_INFO, "Tcg2GetEventLog (EventLogLocation - %x)\n", *EventLogLocation)); + } + + if (EventLogLastEntry != NULL) { + if (!mTcgDxeData.EventLogAreaStruct[Index].EventLogStarted) { + *EventLogLastEntry = (EFI_PHYSICAL_ADDRESS)(UINTN)0; + } else { + *EventLogLastEntry = (EFI_PHYSICAL_ADDRESS)(UINTN)mTcgDxeData.EventLogAreaStruct[Index].LastEvent; + } + DEBUG ((EFI_D_INFO, "Tcg2GetEventLog (EventLogLastEntry - %x)\n", *EventLogLastEntry)); + } + + if (EventLogTruncated != NULL) { + *EventLogTruncated = mTcgDxeData.EventLogAreaStruct[Index].EventLogTruncated; + DEBUG ((EFI_D_INFO, "Tcg2GetEventLog (EventLogTruncated - %x)\n", *EventLogTruncated)); + } + + DEBUG ((EFI_D_INFO, "Tcg2GetEventLog - %r\n", EFI_SUCCESS)); + + // Dump Event Log for debug purpose + if ((EventLogLocation != NULL) && (EventLogLastEntry != NULL)) { + DumpEventLog (EventLogFormat, *EventLogLocation, *EventLogLastEntry, mTcgDxeData.FinalEventsTable[Index]); + } + + // + // All events generated after the invocation of EFI_TCG2_GET_EVENT_LOG SHALL be stored + // in an instance of an EFI_CONFIGURATION_TABLE named by the VendorGuid of EFI_TCG2_FINAL_EVENTS_TABLE_GUID. + // + mTcgDxeData.GetEventLogCalled[Index] = TRUE; + + return EFI_SUCCESS; +} + +/** + Return if this is a Tcg800155PlatformIdEvent. + + @param[in] NewEventHdr Pointer to a TCG_PCR_EVENT_HDR/TCG_PCR_EVENT_EX data structure. + @param[in] NewEventHdrSize New event header size. + @param[in] NewEventData Pointer to the new event data. + @param[in] NewEventSize New event data size. + + @retval TRUE This is a Tcg800155PlatformIdEvent. + @retval FALSE This is NOT a Tcg800155PlatformIdEvent. + +**/ +BOOLEAN +Is800155Event ( + IN VOID *NewEventHdr, + IN UINT32 NewEventHdrSize, + IN UINT8 *NewEventData, + IN UINT32 NewEventSize + ) +{ + if ((((TCG_PCR_EVENT2_HDR *)NewEventHdr)->EventType == EV_NO_ACTION) && + (NewEventSize >= sizeof(TCG_Sp800_155_PlatformId_Event2)) && + (CompareMem (NewEventData, TCG_Sp800_155_PlatformId_Event2_SIGNATURE, + sizeof(TCG_Sp800_155_PlatformId_Event2_SIGNATURE) - 1) == 0)) { + return TRUE; + } + return FALSE; +} + +/** + Add a new entry to the Event Log. + + @param[in, out] EventLogAreaStruct The event log area data structure + @param[in] NewEventHdr Pointer to a TCG_PCR_EVENT_HDR/TCG_PCR_EVENT_EX data structure. + @param[in] NewEventHdrSize New event header size. + @param[in] NewEventData Pointer to the new event data. + @param[in] NewEventSize New event data size. + + @retval EFI_SUCCESS The new event log entry was added. + @retval EFI_OUT_OF_RESOURCES No enough memory to log the new event. + +**/ +EFI_STATUS +TcgCommLogEvent ( + IN OUT TCG_EVENT_LOG_AREA_STRUCT *EventLogAreaStruct, + IN VOID *NewEventHdr, + IN UINT32 NewEventHdrSize, + IN UINT8 *NewEventData, + IN UINT32 NewEventSize + ) +{ + UINTN NewLogSize; + BOOLEAN Record800155Event; + + if (NewEventSize > MAX_ADDRESS - NewEventHdrSize) { + return EFI_OUT_OF_RESOURCES; + } + + NewLogSize = NewEventHdrSize + NewEventSize; + + if (NewLogSize > MAX_ADDRESS - EventLogAreaStruct->EventLogSize) { + return EFI_OUT_OF_RESOURCES; + } + + if (NewLogSize + EventLogAreaStruct->EventLogSize > EventLogAreaStruct->Laml) { + DEBUG ((DEBUG_INFO, " Laml - 0x%x\n", EventLogAreaStruct->Laml)); + DEBUG ((DEBUG_INFO, " NewLogSize - 0x%x\n", NewLogSize)); + DEBUG ((DEBUG_INFO, " LogSize - 0x%x\n", EventLogAreaStruct->EventLogSize)); + DEBUG ((DEBUG_INFO, "TcgCommLogEvent - %r\n", EFI_OUT_OF_RESOURCES)); + return EFI_OUT_OF_RESOURCES; + } + + // + // Check 800-155 event + // Record to 800-155 event offset only. + // If the offset is 0, no need to record. + // + Record800155Event = Is800155Event (NewEventHdr, NewEventHdrSize, NewEventData, NewEventSize); + if (Record800155Event) { + if (EventLogAreaStruct->Next800155EventOffset != 0) { + CopyMem ( + (UINT8 *)(UINTN)EventLogAreaStruct->Lasa + EventLogAreaStruct->Next800155EventOffset + NewLogSize, + (UINT8 *)(UINTN)EventLogAreaStruct->Lasa + EventLogAreaStruct->Next800155EventOffset, + EventLogAreaStruct->EventLogSize - EventLogAreaStruct->Next800155EventOffset + ); + + CopyMem ( + (UINT8 *)(UINTN)EventLogAreaStruct->Lasa + EventLogAreaStruct->Next800155EventOffset, + NewEventHdr, + NewEventHdrSize + ); + CopyMem ( + (UINT8 *)(UINTN)EventLogAreaStruct->Lasa + EventLogAreaStruct->Next800155EventOffset + NewEventHdrSize, + NewEventData, + NewEventSize + ); + + EventLogAreaStruct->Next800155EventOffset += NewLogSize; + EventLogAreaStruct->LastEvent += NewLogSize; + EventLogAreaStruct->EventLogSize += NewLogSize; + } + return EFI_SUCCESS; + } + + EventLogAreaStruct->LastEvent = (UINT8 *)(UINTN)EventLogAreaStruct->Lasa + EventLogAreaStruct->EventLogSize; + EventLogAreaStruct->EventLogSize += NewLogSize; + CopyMem (EventLogAreaStruct->LastEvent, NewEventHdr, NewEventHdrSize); + CopyMem ( + EventLogAreaStruct->LastEvent + NewEventHdrSize, + NewEventData, + NewEventSize + ); + return EFI_SUCCESS; +} + +/** + MRTD => PCR[0] + RTMR[0] => PCR[1,7] + RTMR[1] => PCR[2,3,4,5,6] + RTMR[2] => PCR[8~15] + RTMR[3] => NA + +**/ +UINT32 GetMappedIndexInEventLog(UINT32 PCRIndex) +{ + UINT32 RtmrIndex; + + ASSERT (PCRIndex <= 16 && PCRIndex >= 0); + RtmrIndex = 0; + if (PCRIndex == 1 || PCRIndex == 7) { + RtmrIndex = 0; + } else if (PCRIndex >= 2 && PCRIndex <= 6) { + RtmrIndex = 1; + } else if (PCRIndex >= 8 && PCRIndex <= 15) { + RtmrIndex = 2; + } + + return RtmrIndex + 1; +} + +/** + Add a new entry to the Event Log. + + @param[in] EventLogFormat The type of the event log for which the information is requested. + @param[in] NewEventHdr Pointer to a TCG_PCR_EVENT_HDR/TCG_PCR_EVENT_EX data structure. + @param[in] NewEventHdrSize New event header size. + @param[in] NewEventData Pointer to the new event data. + @param[in] NewEventSize New event data size. + + @retval EFI_SUCCESS The new event log entry was added. + @retval EFI_OUT_OF_RESOURCES No enough memory to log the new event. + +**/ +EFI_STATUS +TcgDxeLogEvent ( + IN EFI_TCG2_EVENT_LOG_FORMAT EventLogFormat, + IN VOID *NewEventHdr, + IN UINT32 NewEventHdrSize, + IN UINT8 *NewEventData, + IN UINT32 NewEventSize + ) +{ + EFI_STATUS Status; + UINTN Index; + TCG_PCR_EVENT2 *TcgPcrEvent2; + TCG_EVENT_LOG_AREA_STRUCT *EventLogAreaStruct; + + // + // map the PCRIndex to RTMR index + TcgPcrEvent2 = (TCG_PCR_EVENT2 *)NewEventHdr; + TcgPcrEvent2->PCRIndex = GetMappedIndexInEventLog(TcgPcrEvent2->PCRIndex); + + for (Index = 0; Index < sizeof(mTcg2EventInfo)/sizeof(mTcg2EventInfo[0]); Index++) { + if (EventLogFormat == mTcg2EventInfo[Index].LogFormat) { + break; + } + } + + if (Index == sizeof(mTcg2EventInfo)/sizeof(mTcg2EventInfo[0])) { + return EFI_INVALID_PARAMETER; + } + + // + // Record to normal event log + // + EventLogAreaStruct = &mTcgDxeData.EventLogAreaStruct[Index]; + + if (EventLogAreaStruct->EventLogTruncated) { + return EFI_VOLUME_FULL; + } + + Status = TcgCommLogEvent ( + EventLogAreaStruct, + NewEventHdr, + NewEventHdrSize, + NewEventData, + NewEventSize + ); + + if (Status == EFI_OUT_OF_RESOURCES) { + EventLogAreaStruct->EventLogTruncated = TRUE; + return EFI_VOLUME_FULL; + } else if (Status == EFI_SUCCESS) { + EventLogAreaStruct->EventLogStarted = TRUE; + } + + // + // If GetEventLog is called, record to FinalEventsTable, too. + // + if (mTcgDxeData.GetEventLogCalled[Index]) { + if (mTcgDxeData.FinalEventsTable[Index] == NULL) { + // + // no need for FinalEventsTable + // + return EFI_SUCCESS; + } + EventLogAreaStruct = &mTcgDxeData.FinalEventLogAreaStruct[Index]; + + if (EventLogAreaStruct->EventLogTruncated) { + return EFI_VOLUME_FULL; + } + + Status = TcgCommLogEvent ( + EventLogAreaStruct, + NewEventHdr, + NewEventHdrSize, + NewEventData, + NewEventSize + ); + if (Status == EFI_OUT_OF_RESOURCES) { + EventLogAreaStruct->EventLogTruncated = TRUE; + return EFI_VOLUME_FULL; + } else if (Status == EFI_SUCCESS) { + EventLogAreaStruct->EventLogStarted = TRUE; + // + // Increase the NumberOfEvents in FinalEventsTable + // + (mTcgDxeData.FinalEventsTable[Index])->NumberOfEvents ++; + DEBUG ((EFI_D_INFO, "FinalEventsTable->NumberOfEvents - 0x%x\n", (mTcgDxeData.FinalEventsTable[Index])->NumberOfEvents)); + DEBUG ((EFI_D_INFO, " Size - 0x%x\n", (UINTN)EventLogAreaStruct->EventLogSize)); + } + } + + return Status; +} + +/** + Get TPML_DIGEST_VALUES compact binary buffer size. + + @param[in] DigestListBin TPML_DIGEST_VALUES compact binary buffer. + + @return TPML_DIGEST_VALUES compact binary buffer size. +**/ +UINT32 +GetDigestListBinSize ( + IN VOID *DigestListBin + ) +{ + UINTN Index; + UINT16 DigestSize; + UINT32 TotalSize; + UINT32 Count; + TPMI_ALG_HASH HashAlg; + + Count = ReadUnaligned32 (DigestListBin); + TotalSize = sizeof(Count); + DigestListBin = (UINT8 *)DigestListBin + sizeof(Count); + for (Index = 0; Index < Count; Index++) { + HashAlg = ReadUnaligned16 (DigestListBin); + TotalSize += sizeof(HashAlg); + DigestListBin = (UINT8 *)DigestListBin + sizeof(HashAlg); + + DigestSize = GetHashSizeFromAlgo (HashAlg); + TotalSize += DigestSize; + DigestListBin = (UINT8 *)DigestListBin + DigestSize; + } + + return TotalSize; +} + +/** + Copy TPML_DIGEST_VALUES compact binary into a buffer + + @param[in,out] Buffer Buffer to hold copied TPML_DIGEST_VALUES compact binary. + @param[in] DigestListBin TPML_DIGEST_VALUES compact binary buffer. + @param[in] HashAlgorithmMask HASH bits corresponding to the desired digests to copy. + @param[out] HashAlgorithmMaskCopied Pointer to HASH bits corresponding to the digests copied. + + @return The end of buffer to hold TPML_DIGEST_VALUES compact binary. +**/ +VOID * +CopyDigestListBinToBuffer ( + IN OUT VOID *Buffer, + IN VOID *DigestListBin, + IN UINT32 HashAlgorithmMask, + OUT UINT32 *HashAlgorithmMaskCopied + ) +{ + UINTN Index; + UINT16 DigestSize; + UINT32 Count; + TPMI_ALG_HASH HashAlg; + UINT32 DigestListCount; + UINT32 *DigestListCountPtr; + + DigestListCountPtr = (UINT32 *) Buffer; + DigestListCount = 0; + *HashAlgorithmMaskCopied = 0; + + Count = ReadUnaligned32 (DigestListBin); + Buffer = (UINT8 *)Buffer + sizeof(Count); + DigestListBin = (UINT8 *)DigestListBin + sizeof(Count); + for (Index = 0; Index < Count; Index++) { + HashAlg = ReadUnaligned16 (DigestListBin); + DigestListBin = (UINT8 *)DigestListBin + sizeof(HashAlg); + DigestSize = GetHashSizeFromAlgo (HashAlg); + + if((HashAlg & HashAlgorithmMask) != 0){ + CopyMem (Buffer, &HashAlg, sizeof(HashAlg)); + Buffer = (UINT8 *)Buffer + sizeof(HashAlg); + CopyMem (Buffer, DigestListBin, DigestSize); + Buffer = (UINT8 *)Buffer + DigestSize; + DigestListCount++; + (*HashAlgorithmMaskCopied) |= GetHashMaskFromAlgo (HashAlg); + } else { + DEBUG ((DEBUG_ERROR, "WARNING: CopyDigestListBinToBuffer Event log has HashAlg unsupported by PCR bank (0x%x)\n", HashAlg)); + } + DigestListBin = (UINT8 *)DigestListBin + DigestSize; + } + WriteUnaligned32 (DigestListCountPtr, DigestListCount); + + return Buffer; +} + +/** + Add a new entry to the Event Log. + + @param[in] DigestList A list of digest. + @param[in,out] NewEventHdr Pointer to a TCG_PCR_EVENT_HDR data structure. + @param[in] NewEventData Pointer to the new event data. + + @retval EFI_SUCCESS The new event log entry was added. + @retval EFI_OUT_OF_RESOURCES No enough memory to log the new event. +**/ +EFI_STATUS +TcgDxeLogHashEvent ( + IN TPML_DIGEST_VALUES *DigestList, + IN OUT TCG_PCR_EVENT_HDR *NewEventHdr, + IN UINT8 *NewEventData + ) +{ + EFI_STATUS Status; + EFI_TPL OldTpl; + UINTN Index; + EFI_STATUS RetStatus; + TCG_PCR_EVENT2 TcgPcrEvent2; + UINT8 *DigestBuffer; + UINT32 *EventSizePtr; + + DEBUG ((EFI_D_INFO, "SupportedEventLogs - 0x%08x\n", mTcgDxeData.BsCap.SupportedEventLogs)); + + RetStatus = EFI_SUCCESS; + for (Index = 0; Index < sizeof(mTcg2EventInfo)/sizeof(mTcg2EventInfo[0]); Index++) { + if ((mTcgDxeData.BsCap.SupportedEventLogs & mTcg2EventInfo[Index].LogFormat) != 0) { + DEBUG ((EFI_D_INFO, " LogFormat - 0x%08x\n", mTcg2EventInfo[Index].LogFormat)); + switch (mTcg2EventInfo[Index].LogFormat) { + case EFI_TCG2_EVENT_LOG_FORMAT_TCG_2: + ZeroMem (&TcgPcrEvent2, sizeof(TcgPcrEvent2)); + TcgPcrEvent2.PCRIndex = NewEventHdr->PCRIndex; + TcgPcrEvent2.EventType = NewEventHdr->EventType; + DigestBuffer = (UINT8 *)&TcgPcrEvent2.Digest; + EventSizePtr = CopyDigestListToBuffer (DigestBuffer, DigestList, mTcgDxeData.BsCap.ActivePcrBanks); + CopyMem (EventSizePtr, &NewEventHdr->EventSize, sizeof(NewEventHdr->EventSize)); + + // + // Enter critical region + // + OldTpl = gBS->RaiseTPL (TPL_HIGH_LEVEL); + Status = TcgDxeLogEvent ( + mTcg2EventInfo[Index].LogFormat, + &TcgPcrEvent2, + sizeof(TcgPcrEvent2.PCRIndex) + sizeof(TcgPcrEvent2.EventType) + GetDigestListBinSize (DigestBuffer) + sizeof(TcgPcrEvent2.EventSize), + NewEventData, + NewEventHdr->EventSize + ); + if (Status != EFI_SUCCESS) { + RetStatus = Status; + } + + gBS->RestoreTPL (OldTpl); + // + // Exit critical region + // + break; + } + } + } + + return RetStatus; +} + +/** + Do a hash operation on a data buffer, extend a specific TPM PCR with the hash result, + and add an entry to the Event Log. + + @param[in] Flags Bitmap providing additional information. + @param[in] HashData Physical address of the start of the data buffer + to be hashed, extended, and logged. + @param[in] HashDataLen The length, in bytes, of the buffer referenced by HashData + @param[in, out] NewEventHdr Pointer to a TCG_PCR_EVENT_HDR data structure. + @param[in] NewEventData Pointer to the new event data. + + @retval EFI_SUCCESS Operation completed successfully. + @retval EFI_OUT_OF_RESOURCES No enough memory to log the new event. + @retval EFI_DEVICE_ERROR The command was unsuccessful. + +**/ +EFI_STATUS +TcgDxeHashLogExtendEvent ( + IN UINT64 Flags, + IN UINT8 *HashData, + IN UINT64 HashDataLen, + IN OUT TCG_PCR_EVENT_HDR *NewEventHdr, + IN UINT8 *NewEventData + ) +{ + EFI_STATUS Status; + TPML_DIGEST_VALUES DigestList; + TCG_PCR_EVENT2_HDR NoActionEvent; + + // + // For TDVF, TPMPresentFlag should always be TRUE + // + if (!mTcgDxeData.BsCap.TPMPresentFlag) { + return EFI_DEVICE_ERROR; + } + + if (NewEventHdr->EventType == EV_NO_ACTION) { + // + // Do not do TPM extend for EV_NO_ACTION + // + Status = EFI_SUCCESS; + InitNoActionEvent (&NoActionEvent, NewEventHdr->EventSize); + if ((Flags & EFI_TCG2_EXTEND_ONLY) == 0) { + Status = TcgDxeLogHashEvent (&(NoActionEvent.Digests), NewEventHdr, NewEventData); + } + + return Status; + } + + Status = HashAndExtend ( + NewEventHdr->PCRIndex, + HashData, + (UINTN)HashDataLen, + &DigestList + ); + if (!EFI_ERROR (Status)) { + if ((Flags & EFI_TCG2_EXTEND_ONLY) == 0) { + Status = TcgDxeLogHashEvent (&DigestList, NewEventHdr, NewEventData); + } + } + + if (Status == EFI_DEVICE_ERROR) { + DEBUG ((EFI_D_ERROR, "TcgDxeHashLogExtendEvent - %r. Disable TPM.\n", Status)); + mTcgDxeData.BsCap.TPMPresentFlag = FALSE; + REPORT_STATUS_CODE ( + EFI_ERROR_CODE | EFI_ERROR_MINOR, + (PcdGet32 (PcdStatusCodeSubClassTpmDevice) | EFI_P_EC_INTERFACE_ERROR) + ); + } + + return Status; +} + +/** + The EFI_TCG2_PROTOCOL HashLogExtendEvent function call provides callers with + an opportunity to extend and optionally log events without requiring + knowledge of actual TPM commands. + The extend operation will occur even if this function cannot create an event + log entry (e.g. due to the event log being full). + + @param[in] This Indicates the calling context + @param[in] Flags Bitmap providing additional information. + @param[in] DataToHash Physical address of the start of the data buffer to be hashed. + @param[in] DataToHashLen The length in bytes of the buffer referenced by DataToHash. + @param[in] Event Pointer to data buffer containing information about the event. + + @retval EFI_SUCCESS Operation completed successfully. + @retval EFI_DEVICE_ERROR The command was unsuccessful. + @retval EFI_VOLUME_FULL The extend operation occurred, but the event could not be written to one or more event logs. + @retval EFI_INVALID_PARAMETER One or more of the parameters are incorrect. + @retval EFI_UNSUPPORTED The PE/COFF image type is not supported. +**/ +EFI_STATUS +EFIAPI +Tcg2HashLogExtendEvent ( + IN EFI_TCG2_PROTOCOL *This, + IN UINT64 Flags, + IN EFI_PHYSICAL_ADDRESS DataToHash, + IN UINT64 DataToHashLen, + IN EFI_TCG2_EVENT *Event + ) +{ + EFI_STATUS Status; + TCG_PCR_EVENT_HDR NewEventHdr; + TPML_DIGEST_VALUES DigestList; + + DEBUG ((DEBUG_VERBOSE, "Tcg2HashLogExtendEvent ...\n")); + + if ((This == NULL) || (Event == NULL)) { + return EFI_INVALID_PARAMETER; + } + // + // Do not check hash data size for EV_NO_ACTION event. + // + if ((Event->Header.EventType != EV_NO_ACTION) && (DataToHash == 0)) { + return EFI_INVALID_PARAMETER; + } + + if (!mTcgDxeData.BsCap.TPMPresentFlag) { + return EFI_DEVICE_ERROR; + } + + if (Event->Size < Event->Header.HeaderSize + sizeof(UINT32)) { + return EFI_INVALID_PARAMETER; + } + + if (Event->Header.PCRIndex > MAX_PCR_INDEX) { + return EFI_INVALID_PARAMETER; + } + + NewEventHdr.PCRIndex = Event->Header.PCRIndex; + NewEventHdr.EventType = Event->Header.EventType; + NewEventHdr.EventSize = Event->Size - sizeof(UINT32) - Event->Header.HeaderSize; + if ((Flags & PE_COFF_IMAGE) != 0) { + Status = MeasurePeImageAndExtend ( + NewEventHdr.PCRIndex, + DataToHash, + (UINTN)DataToHashLen, + &DigestList + ); + if (!EFI_ERROR (Status)) { + if ((Flags & EFI_TCG2_EXTEND_ONLY) == 0) { + Status = TcgDxeLogHashEvent (&DigestList, &NewEventHdr, Event->Event); + } + } + if (Status == EFI_DEVICE_ERROR) { + DEBUG ((EFI_D_ERROR, "MeasurePeImageAndExtend - %r. Disable TPM.\n", Status)); + mTcgDxeData.BsCap.TPMPresentFlag = FALSE; + REPORT_STATUS_CODE ( + EFI_ERROR_CODE | EFI_ERROR_MINOR, + (PcdGet32 (PcdStatusCodeSubClassTpmDevice) | EFI_P_EC_INTERFACE_ERROR) + ); + } + } else { + Status = TcgDxeHashLogExtendEvent ( + Flags, + (UINT8 *) (UINTN) DataToHash, + DataToHashLen, + &NewEventHdr, + Event->Event + ); + } + DEBUG ((DEBUG_VERBOSE, "Tcg2HashLogExtendEvent - %r\n", Status)); + return Status; +} + +/** + This service enables the sending of commands to the TPM. + TODO For TDVF the commands should be submitted to RTMR + + @param[in] This Indicates the calling context + @param[in] InputParameterBlockSize Size of the TPM input parameter block. + @param[in] InputParameterBlock Pointer to the TPM input parameter block. + @param[in] OutputParameterBlockSize Size of the TPM output parameter block. + @param[in] OutputParameterBlock Pointer to the TPM output parameter block. + + @retval EFI_SUCCESS The command byte stream was successfully sent to the device and a response was successfully received. + @retval EFI_DEVICE_ERROR The command was not successfully sent to the device or a response was not successfully received from the device. + @retval EFI_INVALID_PARAMETER One or more of the parameters are incorrect. + @retval EFI_BUFFER_TOO_SMALL The output parameter block is too small. +**/ +EFI_STATUS +EFIAPI +Tcg2SubmitCommand ( + IN EFI_TCG2_PROTOCOL *This, + IN UINT32 InputParameterBlockSize, + IN UINT8 *InputParameterBlock, + IN UINT32 OutputParameterBlockSize, + IN UINT8 *OutputParameterBlock + ) +{ + return EFI_UNSUPPORTED; +} + +/** + This service returns the currently active PCR banks. + + @param[in] This Indicates the calling context + @param[out] ActivePcrBanks Pointer to the variable receiving the bitmap of currently active PCR banks. + + @retval EFI_SUCCESS The bitmap of active PCR banks was stored in the ActivePcrBanks parameter. + @retval EFI_INVALID_PARAMETER One or more of the parameters are incorrect. +**/ +EFI_STATUS +EFIAPI +Tcg2GetActivePCRBanks ( + IN EFI_TCG2_PROTOCOL *This, + OUT UINT32 *ActivePcrBanks + ) +{ + if (ActivePcrBanks == NULL) { + return EFI_INVALID_PARAMETER; + } + *ActivePcrBanks = mTcgDxeData.BsCap.ActivePcrBanks; + return EFI_SUCCESS; +} + +/** + This service sets the currently active PCR banks. + TODO Can PCR banks be set externally? + + @param[in] This Indicates the calling context + @param[in] ActivePcrBanks Bitmap of the requested active PCR banks. At least one bit SHALL be set. + + @retval EFI_SUCCESS The bitmap in ActivePcrBank parameter is already active. + @retval EFI_INVALID_PARAMETER One or more of the parameters are incorrect. +**/ +EFI_STATUS +EFIAPI +Tcg2SetActivePCRBanks ( + IN EFI_TCG2_PROTOCOL *This, + IN UINT32 ActivePcrBanks + ) +{ + DEBUG ((EFI_D_INFO, "Tcg2SetActivePCRBanks ... (0x%x)\n", ActivePcrBanks)); + + return EFI_SUCCESS; + +} + +/** + This service retrieves the result of a previous invocation of SetActivePcrBanks. + + @param[in] This Indicates the calling context + @param[out] OperationPresent Non-zero value to indicate a SetActivePcrBank operation was invoked during the last boot. + @param[out] Response The response from the SetActivePcrBank request. + + @retval EFI_SUCCESS The result value could be returned. + @retval EFI_INVALID_PARAMETER One or more of the parameters are incorrect. +**/ +EFI_STATUS +EFIAPI +Tcg2GetResultOfSetActivePcrBanks ( + IN EFI_TCG2_PROTOCOL *This, + OUT UINT32 *OperationPresent, + OUT UINT32 *Response + ) +{ +// UINT32 ReturnCode; + DEBUG((EFI_D_INFO, "Tcg2GetResultOfSetActivePcrBanks\n")); + if ((OperationPresent == NULL) || (Response == NULL)) { + return EFI_INVALID_PARAMETER; + } + + *OperationPresent = 0; + *Response = 0; + return EFI_SUCCESS; +} + +EFI_TCG2_PROTOCOL mTcg2Protocol = { + Tcg2GetCapability, + Tcg2GetEventLog, + Tcg2HashLogExtendEvent, + Tcg2SubmitCommand, + Tcg2GetActivePCRBanks, + Tcg2SetActivePCRBanks, + Tcg2GetResultOfSetActivePcrBanks, +}; + +/** + Initialize the Event Log and log events passed from the PEI phase. + + @retval EFI_SUCCESS Operation completed successfully. + @retval EFI_OUT_OF_RESOURCES Out of memory. + +**/ +EFI_STATUS +SetupEventLog ( + VOID + ) +{ + EFI_STATUS Status; + EFI_PEI_HOB_POINTERS GuidHob; + EFI_PHYSICAL_ADDRESS Lasa; + UINTN Index; + TCG_EfiSpecIDEventStruct *TcgEfiSpecIdEventStruct; + UINT8 TempBuf[sizeof(TCG_EfiSpecIDEventStruct) + + sizeof(UINT32) + + (HASH_COUNT * sizeof(TCG_EfiSpecIdEventAlgorithmSize)) + + sizeof(UINT8)]; + TCG_PCR_EVENT_HDR SpecIdEvent; + TCG_PCR_EVENT2_HDR NoActionEvent; + TCG_EfiSpecIdEventAlgorithmSize *DigestSize; + TCG_EfiSpecIdEventAlgorithmSize *TempDigestSize; + UINT8 *VendorInfoSize; + UINT32 NumberOfAlgorithms; + + DEBUG ((EFI_D_INFO, "Td: SetupEventLog\n")); + + // + // 1. Create Log Area + // + for (Index = 0; Index < sizeof(mTcg2EventInfo)/sizeof(mTcg2EventInfo[0]); Index++) { + if ((mTcgDxeData.BsCap.SupportedEventLogs & mTcg2EventInfo[Index].LogFormat) != 0) { + mTcgDxeData.EventLogAreaStruct[Index].EventLogFormat = mTcg2EventInfo[Index].LogFormat; + + // allocate pages for TDVF Event log + Status = gBS->AllocatePages ( + AllocateAnyPages, + EfiACPIMemoryNVS, + EFI_SIZE_TO_PAGES (PcdGet32 (PcdTcgLogAreaMinLen)), + &Lasa + ); + if (EFI_ERROR (Status)) { + return Status; + } + mTcgDxeData.EventLogAreaStruct[Index].Lasa = Lasa; + mTcgDxeData.EventLogAreaStruct[Index].Laml = PcdGet32 (PcdTcgLogAreaMinLen); + mTcgDxeData.EventLogAreaStruct[Index].Next800155EventOffset = 0; + + if (mTcg2EventInfo[Index].LogFormat == EFI_TCG2_EVENT_LOG_FORMAT_TCG_2 ) { + // + // Report TCG2 event log address and length, so that they can be reported in TPM2 ACPI table. + // Ignore the return status, because those fields are optional. + // + PcdSet32S(PcdTdxEventlogAcpiTableLaml, (UINT32)mTcgDxeData.EventLogAreaStruct[Index].Laml); + PcdSet64S(PcdTdxEventlogAcpiTableLasa, mTcgDxeData.EventLogAreaStruct[Index].Lasa); + } + + // + // To initialize them as 0xFF is recommended + // because the OS can know the last entry for that. + // + SetMem ((VOID *)(UINTN)Lasa, PcdGet32 (PcdTcgLogAreaMinLen), 0xFF); + // + // Create first entry for Log Header Entry Data + // + if (mTcg2EventInfo[Index].LogFormat != EFI_TCG2_EVENT_LOG_FORMAT_TCG_1_2) { + // + // TcgEfiSpecIdEventStruct + // + TcgEfiSpecIdEventStruct = (TCG_EfiSpecIDEventStruct *)TempBuf; + CopyMem (TcgEfiSpecIdEventStruct->signature, TCG_EfiSpecIDEventStruct_SIGNATURE_03, sizeof(TcgEfiSpecIdEventStruct->signature)); + TcgEfiSpecIdEventStruct->platformClass = PcdGet8 (PcdTpmPlatformClass); + TcgEfiSpecIdEventStruct->specVersionMajor = TCG_EfiSpecIDEventStruct_SPEC_VERSION_MAJOR_TPM2; + TcgEfiSpecIdEventStruct->specVersionMinor = TCG_EfiSpecIDEventStruct_SPEC_VERSION_MINOR_TPM2; + TcgEfiSpecIdEventStruct->specErrata = TCG_EfiSpecIDEventStruct_SPEC_ERRATA_TPM2; + TcgEfiSpecIdEventStruct->uintnSize = sizeof(UINTN)/sizeof(UINT32); + NumberOfAlgorithms = 0; + DigestSize = (TCG_EfiSpecIdEventAlgorithmSize *)((UINT8 *)TcgEfiSpecIdEventStruct + + sizeof(*TcgEfiSpecIdEventStruct) + + sizeof(NumberOfAlgorithms)); + + if ((mTcgDxeData.BsCap.ActivePcrBanks & EFI_TCG2_BOOT_HASH_ALG_SHA384) != 0) { + TempDigestSize = DigestSize; + TempDigestSize += NumberOfAlgorithms; + TempDigestSize->algorithmId = TPM_ALG_SHA384; + TempDigestSize->digestSize = SHA384_DIGEST_SIZE; + NumberOfAlgorithms++; + } + + CopyMem (TcgEfiSpecIdEventStruct + 1, &NumberOfAlgorithms, sizeof(NumberOfAlgorithms)); + TempDigestSize = DigestSize; + TempDigestSize += NumberOfAlgorithms; + VendorInfoSize = (UINT8 *)TempDigestSize; + *VendorInfoSize = 0; + + SpecIdEvent.PCRIndex = 0; + SpecIdEvent.EventType = EV_NO_ACTION; + ZeroMem (&SpecIdEvent.Digest, sizeof(SpecIdEvent.Digest)); + SpecIdEvent.EventSize = (UINT32)GetTcgEfiSpecIdEventStructSize (TcgEfiSpecIdEventStruct); + + // + // Log TcgEfiSpecIdEventStruct as the first Event. Event format is TCG_PCR_EVENT. + // TCG EFI Protocol Spec. Section 5.3 Event Log Header + // TCG PC Client PFP spec. Section 9.2 Measurement Event Entries and Log + // + Status = TcgDxeLogEvent ( + mTcg2EventInfo[Index].LogFormat, + &SpecIdEvent, + sizeof(SpecIdEvent), + (UINT8 *)TcgEfiSpecIdEventStruct, + SpecIdEvent.EventSize + ); + // + // record the offset at the end of 800-155 event. + // the future 800-155 event can be inserted here. + // + mTcgDxeData.EventLogAreaStruct[Index].Next800155EventOffset = \ + mTcgDxeData.EventLogAreaStruct[Index].EventLogSize; + + // + // Tcg800155PlatformIdEvent. Event format is TCG_PCR_EVENT2 + // TDVF needs this event??? + // + GuidHob.Guid = GetFirstGuidHob (&gTcg800155PlatformIdEventHobGuid); + while (GuidHob.Guid != NULL) { + InitNoActionEvent(&NoActionEvent, GET_GUID_HOB_DATA_SIZE (GuidHob.Guid)); + + Status = TcgDxeLogEvent ( + mTcg2EventInfo[Index].LogFormat, + &NoActionEvent, + sizeof(NoActionEvent.PCRIndex) + sizeof(NoActionEvent.EventType) + GetDigestListBinSize (&NoActionEvent.Digests) + sizeof(NoActionEvent.EventSize), + GET_GUID_HOB_DATA (GuidHob.Guid), + GET_GUID_HOB_DATA_SIZE (GuidHob.Guid) + ); + + GuidHob.Guid = GET_NEXT_HOB (GuidHob); + GuidHob.Guid = GetNextGuidHob (&gTcg800155PlatformIdEventHobGuid, GuidHob.Guid); + } + } + } + } + + // + // 2. Create Final Log Area + // + for (Index = 0; Index < sizeof(mTcg2EventInfo)/sizeof(mTcg2EventInfo[0]); Index++) { + if ((mTcgDxeData.BsCap.SupportedEventLogs & mTcg2EventInfo[Index].LogFormat) != 0) { + if (mTcg2EventInfo[Index].LogFormat == EFI_TCG2_EVENT_LOG_FORMAT_TCG_2) { + Status = gBS->AllocatePages ( + AllocateAnyPages, + EfiACPIMemoryNVS, + EFI_SIZE_TO_PAGES (PcdGet32 (PcdTcg2FinalLogAreaLen)), + &Lasa + ); + if (EFI_ERROR (Status)) { + return Status; + } + SetMem ((VOID *)(UINTN)Lasa, PcdGet32 (PcdTcg2FinalLogAreaLen), 0xFF); + + // + // Initialize + // + mTcgDxeData.FinalEventsTable[Index] = (VOID *)(UINTN)Lasa; + (mTcgDxeData.FinalEventsTable[Index])->Version = EFI_TCG2_FINAL_EVENTS_TABLE_VERSION; + (mTcgDxeData.FinalEventsTable[Index])->NumberOfEvents = 0; + + mTcgDxeData.FinalEventLogAreaStruct[Index].EventLogFormat = mTcg2EventInfo[Index].LogFormat; + mTcgDxeData.FinalEventLogAreaStruct[Index].Lasa = Lasa + sizeof(EFI_TCG2_FINAL_EVENTS_TABLE); + mTcgDxeData.FinalEventLogAreaStruct[Index].Laml = PcdGet32 (PcdTcg2FinalLogAreaLen) - sizeof(EFI_TCG2_FINAL_EVENTS_TABLE); + mTcgDxeData.FinalEventLogAreaStruct[Index].EventLogSize = 0; + mTcgDxeData.FinalEventLogAreaStruct[Index].LastEvent = (VOID *)(UINTN)mTcgDxeData.FinalEventLogAreaStruct[Index].Lasa; + mTcgDxeData.FinalEventLogAreaStruct[Index].EventLogStarted = FALSE; + mTcgDxeData.FinalEventLogAreaStruct[Index].EventLogTruncated = FALSE; + mTcgDxeData.FinalEventLogAreaStruct[Index].Next800155EventOffset = 0; + + // + // Install to configuration table for EFI_TCG2_EVENT_LOG_FORMAT_TCG_2 + // + Status = gBS->InstallConfigurationTable (&gEfiTcg2FinalEventsTableGuid, (VOID *)mTcgDxeData.FinalEventsTable[Index]); + if (EFI_ERROR (Status)) { + return Status; + } + } + } + } + + return Status; +} + +/** + Measure and log an action string, and extend the measurement result into PCR[PCRIndex]. + + @param[in] PCRIndex PCRIndex to extend + @param[in] String A specific string that indicates an Action event. + + @retval EFI_SUCCESS Operation completed successfully. + @retval EFI_DEVICE_ERROR The operation was unsuccessful. + +**/ +EFI_STATUS +TcgMeasureAction ( + IN TPM_PCRINDEX PCRIndex, + IN CHAR8 *String + ) +{ + TCG_PCR_EVENT_HDR TcgEvent; + + TcgEvent.PCRIndex = PCRIndex; + TcgEvent.EventType = EV_EFI_ACTION; + TcgEvent.EventSize = (UINT32)AsciiStrLen (String); + return TcgDxeHashLogExtendEvent ( + 0, + (UINT8*)String, + TcgEvent.EventSize, + &TcgEvent, + (UINT8 *) String + ); +} + +/** + Measure and log EFI handoff tables, and extend the measurement result into PCR[1]. + + @retval EFI_SUCCESS Operation completed successfully. + @retval EFI_DEVICE_ERROR The operation was unsuccessful. + +**/ +EFI_STATUS +MeasureHandoffTables ( + VOID + ) +{ + EFI_STATUS Status; + TCG_PCR_EVENT_HDR TcgEvent; + EFI_HANDOFF_TABLE_POINTERS HandoffTables; + UINTN ProcessorNum; + EFI_CPU_PHYSICAL_LOCATION *ProcessorLocBuf; + + ProcessorLocBuf = NULL; + Status = EFI_SUCCESS; + + if (PcdGet8 (PcdTpmPlatformClass) == TCG_PLATFORM_TYPE_SERVER) { + // + // Tcg Server spec. + // Measure each processor EFI_CPU_PHYSICAL_LOCATION with EV_TABLE_OF_DEVICES to PCR[1] + // + Status = GetProcessorsCpuLocation(&ProcessorLocBuf, &ProcessorNum); + + if (!EFI_ERROR(Status)){ + TcgEvent.PCRIndex = 1; + TcgEvent.EventType = EV_TABLE_OF_DEVICES; + TcgEvent.EventSize = sizeof (HandoffTables); + + HandoffTables.NumberOfTables = 1; + HandoffTables.TableEntry[0].VendorGuid = gEfiMpServiceProtocolGuid; + HandoffTables.TableEntry[0].VendorTable = ProcessorLocBuf; + + Status = TcgDxeHashLogExtendEvent ( + 0, + (UINT8*)(UINTN)ProcessorLocBuf, + sizeof(EFI_CPU_PHYSICAL_LOCATION) * ProcessorNum, + &TcgEvent, + (UINT8*)&HandoffTables + ); + + FreePool(ProcessorLocBuf); + } + } + + return Status; +} + +/** + Measure and log Separator event, and extend the measurement result into a specific PCR. + + @param[in] PCRIndex PCR index. + + @retval EFI_SUCCESS Operation completed successfully. + @retval EFI_DEVICE_ERROR The operation was unsuccessful. + +**/ +EFI_STATUS +MeasureSeparatorEvent ( + IN TPM_PCRINDEX PCRIndex + ) +{ + TCG_PCR_EVENT_HDR TcgEvent; + UINT32 EventData; + + DEBUG ((EFI_D_INFO, "MeasureSeparatorEvent Pcr - %x\n", PCRIndex)); + + EventData = 0; + TcgEvent.PCRIndex = PCRIndex; + TcgEvent.EventType = EV_SEPARATOR; + TcgEvent.EventSize = (UINT32)sizeof (EventData); + return TcgDxeHashLogExtendEvent ( + 0, + (UINT8 *)&EventData, + sizeof (EventData), + &TcgEvent, + (UINT8 *)&EventData + ); +} + +/** + Measure and log an EFI variable, and extend the measurement result into a specific PCR. + + @param[in] PCRIndex PCR Index. + @param[in] EventType Event type. + @param[in] VarName A Null-terminated string that is the name of the vendor's variable. + @param[in] VendorGuid A unique identifier for the vendor. + @param[in] VarData The content of the variable data. + @param[in] VarSize The size of the variable data. + + @retval EFI_SUCCESS Operation completed successfully. + @retval EFI_OUT_OF_RESOURCES Out of memory. + @retval EFI_DEVICE_ERROR The operation was unsuccessful. + +**/ +EFI_STATUS +MeasureVariable ( + IN TPM_PCRINDEX PCRIndex, + IN TCG_EVENTTYPE EventType, + IN CHAR16 *VarName, + IN EFI_GUID *VendorGuid, + IN VOID *VarData, + IN UINTN VarSize + ) +{ + EFI_STATUS Status; + TCG_PCR_EVENT_HDR TcgEvent; + UINTN VarNameLength; + UEFI_VARIABLE_DATA *VarLog; + + DEBUG ((EFI_D_INFO, "TdTcg2Dxe: MeasureVariable (Pcr - %x, EventType - %x, ", (UINTN)PCRIndex, (UINTN)EventType)); + DEBUG ((EFI_D_INFO, "VariableName - %s, VendorGuid - %g)\n", VarName, VendorGuid)); + + VarNameLength = StrLen (VarName); + TcgEvent.PCRIndex = PCRIndex; + TcgEvent.EventType = EventType; + + TcgEvent.EventSize = (UINT32)(sizeof (*VarLog) + VarNameLength * sizeof (*VarName) + VarSize + - sizeof (VarLog->UnicodeName) - sizeof (VarLog->VariableData)); + + VarLog = (UEFI_VARIABLE_DATA *)AllocatePool (TcgEvent.EventSize); + if (VarLog == NULL) { + return EFI_OUT_OF_RESOURCES; + } + + VarLog->VariableName = *VendorGuid; + VarLog->UnicodeNameLength = VarNameLength; + VarLog->VariableDataLength = VarSize; + CopyMem ( + VarLog->UnicodeName, + VarName, + VarNameLength * sizeof (*VarName) + ); + if (VarSize != 0 && VarData != NULL) { + CopyMem ( + (CHAR16 *)VarLog->UnicodeName + VarNameLength, + VarData, + VarSize + ); + } + + if (EventType == EV_EFI_VARIABLE_DRIVER_CONFIG) { + // + // Digest is the event data (UEFI_VARIABLE_DATA) + // + Status = TcgDxeHashLogExtendEvent ( + 0, + (UINT8*)VarLog, + TcgEvent.EventSize, + &TcgEvent, + (UINT8*)VarLog + ); + } else { + ASSERT (VarData != NULL); + Status = TcgDxeHashLogExtendEvent ( + 0, + (UINT8*)VarData, + VarSize, + &TcgEvent, + (UINT8*)VarLog + ); + } + FreePool (VarLog); + return Status; +} + +/** + Read then Measure and log an EFI variable, and extend the measurement result into a specific PCR. + + @param[in] PCRIndex PCR Index. + @param[in] EventType Event type. + @param[in] VarName A Null-terminated string that is the name of the vendor's variable. + @param[in] VendorGuid A unique identifier for the vendor. + @param[out] VarSize The size of the variable data. + @param[out] VarData Pointer to the content of the variable. + + @retval EFI_SUCCESS Operation completed successfully. + @retval EFI_OUT_OF_RESOURCES Out of memory. + @retval EFI_DEVICE_ERROR The operation was unsuccessful. + +**/ +EFI_STATUS +ReadAndMeasureVariable ( + IN TPM_PCRINDEX PCRIndex, + IN TCG_EVENTTYPE EventType, + IN CHAR16 *VarName, + IN EFI_GUID *VendorGuid, + OUT UINTN *VarSize, + OUT VOID **VarData + ) +{ + EFI_STATUS Status; + + Status = GetVariable2 (VarName, VendorGuid, VarData, VarSize); + if (EventType == EV_EFI_VARIABLE_DRIVER_CONFIG) { + if (EFI_ERROR (Status)) { + // + // It is valid case, so we need handle it. + // + *VarData = NULL; + *VarSize = 0; + } + } else { + // + // if status error, VarData is freed and set NULL by GetVariable2 + // + if (EFI_ERROR (Status)) { + return EFI_NOT_FOUND; + } + } + + Status = MeasureVariable ( + PCRIndex, + EventType, + VarName, + VendorGuid, + *VarData, + *VarSize + ); + return Status; +} + +/** + Read then Measure and log an EFI boot variable, and extend the measurement result into PCR[1]. +according to TCG PC Client PFP spec 0021 Section 2.4.4.2 + + @param[in] VarName A Null-terminated string that is the name of the vendor's variable. + @param[in] VendorGuid A unique identifier for the vendor. + @param[out] VarSize The size of the variable data. + @param[out] VarData Pointer to the content of the variable. + + @retval EFI_SUCCESS Operation completed successfully. + @retval EFI_OUT_OF_RESOURCES Out of memory. + @retval EFI_DEVICE_ERROR The operation was unsuccessful. + +**/ +EFI_STATUS +ReadAndMeasureBootVariable ( + IN CHAR16 *VarName, + IN EFI_GUID *VendorGuid, + OUT UINTN *VarSize, + OUT VOID **VarData + ) +{ + return ReadAndMeasureVariable ( + 1, + EV_EFI_VARIABLE_BOOT, + VarName, + VendorGuid, + VarSize, + VarData + ); +} + +/** + Read then Measure and log an EFI Secure variable, and extend the measurement result into PCR[7]. + + @param[in] VarName A Null-terminated string that is the name of the vendor's variable. + @param[in] VendorGuid A unique identifier for the vendor. + @param[out] VarSize The size of the variable data. + @param[out] VarData Pointer to the content of the variable. + + @retval EFI_SUCCESS Operation completed successfully. + @retval EFI_OUT_OF_RESOURCES Out of memory. + @retval EFI_DEVICE_ERROR The operation was unsuccessful. + +**/ +EFI_STATUS +ReadAndMeasureSecureVariable ( + IN CHAR16 *VarName, + IN EFI_GUID *VendorGuid, + OUT UINTN *VarSize, + OUT VOID **VarData + ) +{ + return ReadAndMeasureVariable ( + 7, + EV_EFI_VARIABLE_DRIVER_CONFIG, + VarName, + VendorGuid, + VarSize, + VarData + ); +} + +/** + Measure and log all EFI boot variables, and extend the measurement result into a specific PCR. + + The EFI boot variables are BootOrder and Boot#### variables. + + @retval EFI_SUCCESS Operation completed successfully. + @retval EFI_OUT_OF_RESOURCES Out of memory. + @retval EFI_DEVICE_ERROR The operation was unsuccessful. + +**/ +EFI_STATUS +MeasureAllBootVariables ( + VOID + ) +{ + EFI_STATUS Status; + UINT16 *BootOrder; + UINTN BootCount; + UINTN Index; + VOID *BootVarData; + UINTN Size; + + Status = ReadAndMeasureBootVariable ( + mBootVarName, + &gEfiGlobalVariableGuid, + &BootCount, + (VOID **) &BootOrder + ); + if (Status == EFI_NOT_FOUND || BootOrder == NULL) { + return EFI_SUCCESS; + } + + if (EFI_ERROR (Status)) { + // + // BootOrder can't be NULL if status is not EFI_NOT_FOUND + // + FreePool (BootOrder); + return Status; + } + + BootCount /= sizeof (*BootOrder); + for (Index = 0; Index < BootCount; Index++) { + UnicodeSPrint (mBootVarName, sizeof (mBootVarName), L"Boot%04x", BootOrder[Index]); + Status = ReadAndMeasureBootVariable ( + mBootVarName, + &gEfiGlobalVariableGuid, + &Size, + &BootVarData + ); + if (!EFI_ERROR (Status)) { + FreePool (BootVarData); + } + } + + FreePool (BootOrder); + return EFI_SUCCESS; +} + +/** + Measure and log all EFI Secure variables, and extend the measurement result into a specific PCR. + + The EFI boot variables are BootOrder and Boot#### variables. + + @retval EFI_SUCCESS Operation completed successfully. + @retval EFI_OUT_OF_RESOURCES Out of memory. + @retval EFI_DEVICE_ERROR The operation was unsuccessful. + +**/ +EFI_STATUS +MeasureAllSecureVariables ( + VOID + ) +{ + EFI_STATUS Status; + VOID *Data; + UINTN DataSize; + UINTN Index; + + Status = EFI_NOT_FOUND; + for (Index = 0; Index < sizeof(mVariableType)/sizeof(mVariableType[0]); Index++) { + Status = ReadAndMeasureSecureVariable ( + mVariableType[Index].VariableName, + mVariableType[Index].VendorGuid, + &DataSize, + &Data + ); + if (!EFI_ERROR (Status)) { + if (Data != NULL) { + FreePool (Data); + } + } + } + + // + // Measure DBT if present and not empty + // + Status = GetVariable2 (EFI_IMAGE_SECURITY_DATABASE2, &gEfiImageSecurityDatabaseGuid, &Data, &DataSize); + if (!EFI_ERROR(Status)) { + Status = MeasureVariable ( + 7, + EV_EFI_VARIABLE_DRIVER_CONFIG, + EFI_IMAGE_SECURITY_DATABASE2, + &gEfiImageSecurityDatabaseGuid, + Data, + DataSize + ); + FreePool(Data); + } else { + DEBUG((DEBUG_INFO, "Skip measuring variable %s since it's deleted\n", EFI_IMAGE_SECURITY_DATABASE2)); + } + + return EFI_SUCCESS; +} + +/** + Measure and log launch of FirmwareDebugger, and extend the measurement result into a specific PCR. + + @retval EFI_SUCCESS Operation completed successfully. + @retval EFI_OUT_OF_RESOURCES Out of memory. + @retval EFI_DEVICE_ERROR The operation was unsuccessful. + +**/ +EFI_STATUS +MeasureLaunchOfFirmwareDebugger ( + VOID + ) +{ + TCG_PCR_EVENT_HDR TcgEvent; + + TcgEvent.PCRIndex = 7; + TcgEvent.EventType = EV_EFI_ACTION; + TcgEvent.EventSize = sizeof(FIRMWARE_DEBUGGER_EVENT_STRING) - 1; + return TcgDxeHashLogExtendEvent ( + 0, + (UINT8 *)FIRMWARE_DEBUGGER_EVENT_STRING, + sizeof(FIRMWARE_DEBUGGER_EVENT_STRING) - 1, + &TcgEvent, + (UINT8 *)FIRMWARE_DEBUGGER_EVENT_STRING + ); +} + +/** + Measure and log all Secure Boot Policy, and extend the measurement result into a specific PCR. + + Platform firmware adhering to the policy must therefore measure the following values into PCR[7]: (in order listed) + - The contents of the SecureBoot variable + - The contents of the PK variable + - The contents of the KEK variable + - The contents of the EFI_IMAGE_SECURITY_DATABASE variable + - The contents of the EFI_IMAGE_SECURITY_DATABASE1 variable + - Separator + - Entries in the EFI_IMAGE_SECURITY_DATABASE that are used to validate EFI Drivers or EFI Boot Applications in the boot path + + NOTE: Because of the above, UEFI variables PK, KEK, EFI_IMAGE_SECURITY_DATABASE, + EFI_IMAGE_SECURITY_DATABASE1 and SecureBoot SHALL NOT be measured into PCR[3]. + + @param[in] Event Event whose notification function is being invoked + @param[in] Context Pointer to the notification function's context +**/ +VOID +EFIAPI +MeasureSecureBootPolicy ( + IN EFI_EVENT Event, + IN VOID *Context + ) +{ + EFI_STATUS Status; + VOID *Protocol; + + Status = gBS->LocateProtocol (&gEfiVariableWriteArchProtocolGuid, NULL, (VOID **)&Protocol); + if (EFI_ERROR (Status)) { + return; + } + + if (PcdGetBool (PcdFirmwareDebuggerInitialized)) { + Status = MeasureLaunchOfFirmwareDebugger (); + DEBUG ((EFI_D_INFO, "MeasureLaunchOfFirmwareDebugger - %r\n", Status)); + } + + Status = MeasureAllSecureVariables (); + DEBUG ((EFI_D_INFO, "MeasureAllSecureVariables - %r\n", Status)); + + // + // We need measure Separator(7) here, because this event must be between SecureBootPolicy (Configure) + // and ImageVerification (Authority) + // There might be a case that we need measure UEFI image from DriverOrder, besides BootOrder. So + // the Authority measurement happen before ReadToBoot event. + // + Status = MeasureSeparatorEvent (7); + DEBUG ((EFI_D_INFO, "MeasureSeparatorEvent - %r\n", Status)); + return ; +} + +/** + Ready to Boot Event notification handler. + + Sequence of OS boot events is measured in this event notification handler. + + @param[in] Event Event whose notification function is being invoked + @param[in] Context Pointer to the notification function's context + +**/ +VOID +EFIAPI +OnReadyToBoot ( + IN EFI_EVENT Event, + IN VOID *Context + ) +{ + EFI_STATUS Status; + TPM_PCRINDEX PcrIndex; + + PERF_START_EX (mImageHandle, "EventRec", "Tcg2Dxe", 0, PERF_ID_TD_TCG2_DXE); + if (mBootAttempts == 0) { + + // + // Measure handoff tables. + // + Status = MeasureHandoffTables (); + if (EFI_ERROR (Status)) { + DEBUG ((EFI_D_ERROR, "HOBs not Measured. Error!\n")); + } + + // + // Measure BootOrder & Boot#### variables. + // + Status = MeasureAllBootVariables (); + if (EFI_ERROR (Status)) { + DEBUG ((EFI_D_ERROR, "Boot Variables not Measured. Error!\n")); + } + + // + // 1. This is the first boot attempt. + // + Status = TcgMeasureAction ( + 4, + EFI_CALLING_EFI_APPLICATION + ); + if (EFI_ERROR (Status)) { + DEBUG ((EFI_D_ERROR, "%a not Measured. Error!\n", EFI_CALLING_EFI_APPLICATION)); + } + + // + // 2. Draw a line between pre-boot env and entering post-boot env. + // PCR[7] is already done. + // + for (PcrIndex = 0; PcrIndex < 7; PcrIndex++) { + Status = MeasureSeparatorEvent (PcrIndex); + if (EFI_ERROR (Status)) { + DEBUG ((DEBUG_ERROR, "Separator Event not Measured. Error!\n")); + } + } + + // + // 3. Measure GPT. It would be done in SAP driver. + // + + // + // 4. Measure PE/COFF OS loader. It would be done in SAP driver. + // + + // + // 5. Read & Measure variable. BootOrder already measured. + // + } else { + // + // 6. Not first attempt, meaning a return from last attempt + // + Status = TcgMeasureAction ( + 4, + EFI_RETURNING_FROM_EFI_APPLICATION + ); + if (EFI_ERROR (Status)) { + DEBUG ((EFI_D_ERROR, "%a not Measured. Error!\n", EFI_RETURNING_FROM_EFI_APPLICATION)); + } + + // + // 7. Next boot attempt, measure "Calling EFI Application from Boot Option" again + // TCG PC Client PFP spec Section 2.4.4.5 Step 4 + // + Status = TcgMeasureAction ( + 4, + EFI_CALLING_EFI_APPLICATION + ); + if (EFI_ERROR (Status)) { + DEBUG ((EFI_D_ERROR, "%a not Measured. Error!\n", EFI_CALLING_EFI_APPLICATION)); + } + } + + DEBUG ((EFI_D_INFO, "TdTcg2Dxe Measure Data when ReadyToBoot\n")); + // + // Increase boot attempt counter. + // + mBootAttempts++; + PERF_END_EX (mImageHandle, "EventRec", "Tcg2Dxe", 0, PERF_ID_TD_TCG2_DXE + 1); +} + +/** + Exit Boot Services Event notification handler. + + Measure invocation and success of ExitBootServices. + + @param[in] Event Event whose notification function is being invoked + @param[in] Context Pointer to the notification function's context + +**/ +VOID +EFIAPI +OnExitBootServices ( + IN EFI_EVENT Event, + IN VOID *Context + ) +{ + EFI_STATUS Status; + + // + // Measure invocation of ExitBootServices, + // + Status = TcgMeasureAction ( + 5, + EFI_EXIT_BOOT_SERVICES_INVOCATION + ); + if (EFI_ERROR (Status)) { + DEBUG ((EFI_D_ERROR, "%a not Measured. Error!\n", EFI_EXIT_BOOT_SERVICES_INVOCATION)); + } + + // + // Measure success of ExitBootServices + // + Status = TcgMeasureAction ( + 5, + EFI_EXIT_BOOT_SERVICES_SUCCEEDED + ); + if (EFI_ERROR (Status)) { + DEBUG ((EFI_D_ERROR, "%a not Measured. Error!\n", EFI_EXIT_BOOT_SERVICES_SUCCEEDED)); + } +} + +/** + Exit Boot Services Failed Event notification handler. + + Measure Failure of ExitBootServices. + + @param[in] Event Event whose notification function is being invoked + @param[in] Context Pointer to the notification function's context + +**/ +VOID +EFIAPI +OnExitBootServicesFailed ( + IN EFI_EVENT Event, + IN VOID *Context + ) +{ + EFI_STATUS Status; + + // + // Measure Failure of ExitBootServices, + // + Status = TcgMeasureAction ( + 5, + EFI_EXIT_BOOT_SERVICES_FAILED + ); + if (EFI_ERROR (Status)) { + DEBUG ((EFI_D_ERROR, "%a not Measured. Error!\n", EFI_EXIT_BOOT_SERVICES_FAILED)); + } + +} + +EFI_STATUS +SyncTdTcgEvent() +{ + EFI_STATUS Status; + EFI_PEI_HOB_POINTERS GuidHob; + VOID *TcgEvent; + VOID *DigestListBin; + UINT32 DigestListBinSize; + UINT8 *Event; + UINT32 EventSize; + + DEBUG ((DEBUG_INFO, "Sync Tdx event from SEC\n")); + + Status = EFI_SUCCESS; + GuidHob.Guid = GetFirstGuidHob (&gTcgEvent2EntryHobGuid); + + while (!EFI_ERROR(Status) && GuidHob.Guid != NULL) { + TcgEvent = AllocateCopyPool (GET_GUID_HOB_DATA_SIZE (GuidHob.Guid), GET_GUID_HOB_DATA (GuidHob.Guid)); + + GuidHob.Guid = GET_NEXT_HOB (GuidHob); + GuidHob.Guid = GetNextGuidHob (&gTcgEvent2EntryHobGuid, GuidHob.Guid); + + DigestListBin = (UINT8 *)TcgEvent + sizeof(TCG_PCRINDEX) + sizeof(TCG_EVENTTYPE); + DigestListBinSize = GetDigestListBinSize(DigestListBin); + + // + // Event size. + // + EventSize = *(UINT32*)((UINT8 *) DigestListBin + DigestListBinSize); + Event = (UINT8 *)DigestListBin + DigestListBinSize + sizeof(UINT32); + + // + // Log the event + // + Status = TcgDxeLogEvent ( + mTcg2EventInfo[0].LogFormat, + TcgEvent, + sizeof (TCG_PCRINDEX) + sizeof (TCG_EVENTTYPE) + DigestListBinSize + sizeof (UINT32), + Event, + EventSize + ); + + DumpEvent2 ((TCG_PCR_EVENT2*) TcgEvent); + FreePool (TcgEvent); + } + + return Status; +} + + +/** + Install TDVF ACPI Table when ACPI Table Protocol is available. + + @param[in] Event Event whose notification function is being invoked + @param[in] Context Pointer to the notification function's context +**/ +VOID +EFIAPI +InstallAcpiTable ( + IN EFI_EVENT Event, + IN VOID* Context + ) +{ + UINTN TableKey; + EFI_STATUS Status; + EFI_ACPI_TABLE_PROTOCOL *AcpiTable; + UINT64 OemTableId; + + Status = gBS->LocateProtocol (&gEfiAcpiTableProtocolGuid, NULL, (VOID **)&AcpiTable); + if (EFI_ERROR (Status)) { + DEBUG((DEBUG_ERROR, "TCG2: AcpiTableProtocol is not installed. %r\n", Status)); + return; + } + + mTdxEventlogAcpiTemplate.Laml = (UINT64)PcdGet32(PcdTdxEventlogAcpiTableLaml); + mTdxEventlogAcpiTemplate.Lasa = PcdGet64(PcdTdxEventlogAcpiTableLasa); + CopyMem (mTdxEventlogAcpiTemplate.Header.OemId, PcdGetPtr (PcdAcpiDefaultOemId), sizeof (mTdxEventlogAcpiTemplate.Header.OemId)); + OemTableId = PcdGet64 (PcdAcpiDefaultOemTableId); + CopyMem (&mTdxEventlogAcpiTemplate.Header.OemTableId, &OemTableId, sizeof (UINT64)); + mTdxEventlogAcpiTemplate.Header.OemRevision = PcdGet32 (PcdAcpiDefaultOemRevision); + mTdxEventlogAcpiTemplate.Header.CreatorId = PcdGet32 (PcdAcpiDefaultCreatorId); + mTdxEventlogAcpiTemplate.Header.CreatorRevision = PcdGet32 (PcdAcpiDefaultCreatorRevision); + + // + // Construct ACPI Table + Status = AcpiTable->InstallAcpiTable( + AcpiTable, + &mTdxEventlogAcpiTemplate, + mTdxEventlogAcpiTemplate.Header.Length, + &TableKey + ); + ASSERT_EFI_ERROR (Status); + + DEBUG((DEBUG_INFO, "TDVF Eventlog ACPI Table is installed.\n")); + + Status = TpmMeasureAndLogData( + 1, + EV_POST_CODE, + EV_POSTCODE_INFO_ACPI_DATA, + ACPI_DATA_LEN, + &mTdxEventlogAcpiTemplate, + mTdxEventlogAcpiTemplate.Header.Length + ); + ASSERT_EFI_ERROR (Status); + + DEBUG((DEBUG_INFO, "TDX Eventlog ACPI Table is measured and logged\n")); +} + +/** + The function install Tcg2 protocol. + + @retval EFI_SUCCESS Tcg2 protocol is installed. + @retval other Some error occurs. +**/ +EFI_STATUS +InstallTdTcg2 ( + VOID + ) +{ + EFI_STATUS Status; + EFI_HANDLE Handle; + + Handle = NULL; + Status = gBS->InstallMultipleProtocolInterfaces ( + &Handle, + &gTdTcg2ProtocolGuid, + &mTcg2Protocol, + NULL + ); + return Status; +} + +/** + The driver's entry point. It publishes EFI Tcg2 Protocol. + + @param[in] ImageHandle The firmware allocated handle for the EFI image. + @param[in] SystemTable A pointer to the EFI System Table. + + @retval EFI_SUCCESS The entry point is executed successfully. + @retval other Some error occurs when executing this entry point. +**/ +EFI_STATUS +EFIAPI +DriverEntry ( + IN EFI_HANDLE ImageHandle, + IN EFI_SYSTEM_TABLE *SystemTable + ) +{ + EFI_STATUS Status; + EFI_EVENT Event; + VOID *Registration; + + if (!ProbeTdGuest()) { + DEBUG ((DEBUG_INFO, "TdTcg2Dxe is not loaded in Non-Td guest.\n")); + return EFI_UNSUPPORTED; + } + + mImageHandle = ImageHandle; + + // + // Fill information + // + ASSERT (TCG_EVENT_LOG_AREA_COUNT_MAX == sizeof(mTcg2EventInfo)/sizeof(mTcg2EventInfo[0])); + + mTcgDxeData.BsCap.Size = sizeof(EFI_TCG2_BOOT_SERVICE_CAPABILITY); + mTcgDxeData.BsCap.ProtocolVersion.Major = 1; + mTcgDxeData.BsCap.ProtocolVersion.Minor = 1; + mTcgDxeData.BsCap.StructureVersion.Major = 1; + mTcgDxeData.BsCap.StructureVersion.Minor = 1; + + // + // Get supported PCR and current Active PCRs + // For TD gueset HA384 is supported. + // + mTcgDxeData.BsCap.HashAlgorithmBitmap = HASH_ALG_SHA384; + mTcgDxeData.BsCap.ActivePcrBanks = HASH_ALG_SHA384; + mTcgDxeData.BsCap.NumberOfPCRBanks = 1; + + // TD guest only supports EFI_TCG2_EVENT_LOG_FORMAT_TCG_2 + mTcgDxeData.BsCap.SupportedEventLogs = EFI_TCG2_EVENT_LOG_FORMAT_TCG_2; + + DEBUG ((EFI_D_INFO, "TdTcg2.SupportedEventLogs - 0x%08x\n", mTcgDxeData.BsCap.SupportedEventLogs)); + DEBUG ((EFI_D_INFO, "TdTcg2.HashAlgorithmBitmap - 0x%08x\n", mTcgDxeData.BsCap.HashAlgorithmBitmap)); + DEBUG ((EFI_D_INFO, "TdTcg2.NumberOfPCRBanks - 0x%08x\n", mTcgDxeData.BsCap.NumberOfPCRBanks)); + DEBUG ((EFI_D_INFO, "TdTcg2.ActivePcrBanks - 0x%08x\n", mTcgDxeData.BsCap.ActivePcrBanks)); + + // + // Setup the log area and copy event log from hob list to it + // + Status = SetupEventLog (); + ASSERT_EFI_ERROR (Status); + + if (!EFI_ERROR (Status)) { + Status = SyncTdTcgEvent(); + ASSERT_EFI_ERROR (Status); + } + + // + // Measure handoff tables, Boot#### variables etc. + // + Status = EfiCreateEventReadyToBootEx ( + TPL_CALLBACK, + OnReadyToBoot, + NULL, + &Event + ); + + Status = gBS->CreateEventEx ( + EVT_NOTIFY_SIGNAL, + TPL_NOTIFY, + OnExitBootServices, + NULL, + &gEfiEventExitBootServicesGuid, + &Event + ); + + // + // Measure Exit Boot Service failed + // + Status = gBS->CreateEventEx ( + EVT_NOTIFY_SIGNAL, + TPL_NOTIFY, + OnExitBootServicesFailed, + NULL, + &gEventExitBootServicesFailedGuid, + &Event + ); + + // + // Create event callback, because we need access variable on SecureBootPolicyVariable + // We should use VariableWriteArch instead of VariableArch, because Variable driver + // may update SecureBoot value based on last setting. + // + EfiCreateProtocolNotifyEvent (&gEfiVariableWriteArchProtocolGuid, TPL_CALLBACK, MeasureSecureBootPolicy, NULL, &Registration); + + // + // Install TdTcg2Protocol + // + Status = InstallTdTcg2 (); + DEBUG ((EFI_D_INFO, "InstallTdTcg2 - %r\n", Status)); + + if (Status == EFI_SUCCESS) { + // + // Create event callback to install TDX ACPI Table + EfiCreateProtocolNotifyEvent (&gEfiAcpiTableProtocolGuid, TPL_CALLBACK, InstallAcpiTable, NULL, &Registration); + } + + return Status; +} diff --git a/OvmfPkg/Tcg/Tcg2Dxe/TdTcg2Dxe.inf b/OvmfPkg/Tcg/Tcg2Dxe/TdTcg2Dxe.inf new file mode 100644 index 000000000000..e27ea7955f51 --- /dev/null +++ b/OvmfPkg/Tcg/Tcg2Dxe/TdTcg2Dxe.inf @@ -0,0 +1,111 @@ +## @file +# +# Produces EFI_TD protocol and measure boot environment +# +# +# Copyright (c) 2020 - 2021, Intel Corporation. All rights reserved.
+# SPDX-License-Identifier: BSD-2-Clause-Patent +# +## + +[Defines] + INF_VERSION = 0x00010005 + BASE_NAME = TdTcg2Dxe + MODULE_UNI_FILE = TdTcg2Dxe.uni + FILE_GUID = F062221E-C607-44C2-B0B4-C3886331D351 + MODULE_TYPE = DXE_DRIVER + VERSION_STRING = 1.0 + ENTRY_POINT = DriverEntry + +# +# The following information is for reference only and not required by the build tools. +# +# VALID_ARCHITECTURES = X64 +# + +[Sources] + TdTcg2Dxe.c + MeasureBootPeCoff.c + +[Packages] + MdePkg/MdePkg.dec + MdeModulePkg/MdeModulePkg.dec + SecurityPkg/SecurityPkg.dec + CryptoPkg/CryptoPkg.dec + OvmfPkg/OvmfPkg.dec + +[LibraryClasses] + MemoryAllocationLib + BaseLib + UefiBootServicesTableLib + HobLib + UefiDriverEntryPoint + UefiRuntimeServicesTableLib + BaseMemoryLib + DebugLib + PrintLib + UefiLib + HashLib + PerformanceLib + ReportStatusCodeLib + #Tcg2PhysicalPresenceLib ? + PeCoffLib + TpmMeasurementLib + TdxProbeLib + TdxLib + +[Guids] + ## SOMETIMES_CONSUMES ## Variable:L"SecureBoot" + ## SOMETIMES_CONSUMES ## Variable:L"PK" + ## SOMETIMES_CONSUMES ## Variable:L"KEK" + ## SOMETIMES_CONSUMES ## Variable:L"BootXXXX" + gEfiGlobalVariableGuid + + ## SOMETIMES_CONSUMES ## Variable:L"db" + ## SOMETIMES_CONSUMES ## Variable:L"dbx" + gEfiImageSecurityDatabaseGuid + + gTcgEventEntryHobGuid ## SOMETIMES_CONSUMES ## HOB + gEfiEventExitBootServicesGuid ## CONSUMES ## Event + gEventExitBootServicesFailedGuid ## SOMETIMES_CONSUMES ## Event + + gTcgEvent2EntryHobGuid ## SOMETIMES_CONSUMES ## HOB + gTcg800155PlatformIdEventHobGuid ## SOMETIMES_CONSUMES ## HOB + #gTdTcg2ProtocolGuid + +[Protocols] + gTdTcg2ProtocolGuid ## PRODUCES + gEfiTcg2FinalEventsTableGuid ## PRODUCES + gEfiMpServiceProtocolGuid ## SOMETIMES_CONSUMES + gEfiVariableWriteArchProtocolGuid ## NOTIFY + gEfiResetNotificationProtocolGuid ## CONSUMES + gEfiAcpiTableProtocolGuid ## NOTIFY + +[Pcd] + gEfiSecurityPkgTokenSpaceGuid.PcdTpmPlatformClass ## SOMETIMES_CONSUMES + gEfiSecurityPkgTokenSpaceGuid.PcdFirmwareDebuggerInitialized ## SOMETIMES_CONSUMES + gEfiSecurityPkgTokenSpaceGuid.PcdStatusCodeSubClassTpmDevice ## SOMETIMES_CONSUMES + gEfiSecurityPkgTokenSpaceGuid.PcdTcg2HashAlgorithmBitmap ## CONSUMES + gEfiSecurityPkgTokenSpaceGuid.PcdTcg2NumberOfPCRBanks ## CONSUMES + gEfiSecurityPkgTokenSpaceGuid.PcdTcgLogAreaMinLen ## CONSUMES + gEfiSecurityPkgTokenSpaceGuid.PcdTcg2FinalLogAreaLen ## CONSUMES + #gEfiSecurityPkgTokenSpaceGuid.PcdTpm2AcpiTableRev ## CONSUMES + gUefiOvmfPkgTokenSpaceGuid.PcdTdxEventlogAcpiTableLaml ## PRODUCES + gUefiOvmfPkgTokenSpaceGuid.PcdTdxEventlogAcpiTableLasa ## PRODUCES + gEfiMdeModulePkgTokenSpaceGuid.PcdAcpiDefaultOemId ## CONSUMES + gEfiMdeModulePkgTokenSpaceGuid.PcdAcpiDefaultOemTableId ## CONSUMES + gEfiMdeModulePkgTokenSpaceGuid.PcdAcpiDefaultOemRevision ## CONSUMES + gEfiMdeModulePkgTokenSpaceGuid.PcdAcpiDefaultCreatorId ## CONSUMES + gEfiMdeModulePkgTokenSpaceGuid.PcdAcpiDefaultCreatorRevision ## CONSUMES + #gUefiTdvfPkgTokenSpaceGuid.PcdTdvfAcpiTableRtmr ## CONSUMES + #gUefiTdvfPkgTokenSpaceGuid.PcdTdvfAcpiTableRtmrLen ## CONSUMES + +[Depex] + # According to PcdTpm2AcpiTableRev definition in SecurityPkg.dec + # This PCD should be configured at DynamicHii or DynamicHiiEx. + # So, this PCD read operation depends on GetVariable service. + # Add VariableArch protocol dependency to make sure PCD read works. + gEfiVariableArchProtocolGuid AND gEfiAcpiTableProtocolGuid + +[UserExtensions.TianoCore."ExtraFiles"] + TdTcg2DxeExtra.uni diff --git a/OvmfPkg/Tcg/Tcg2Dxe/TdTcg2Dxe.uni b/OvmfPkg/Tcg/Tcg2Dxe/TdTcg2Dxe.uni new file mode 100644 index 000000000000..b7addf94c624 --- /dev/null +++ b/OvmfPkg/Tcg/Tcg2Dxe/TdTcg2Dxe.uni @@ -0,0 +1,21 @@ +// /** @file +// Produces TCG2 protocol and measure boot environment +// +// This module will produce TCG2 protocol and measure boot environment. +// +// Caution: This module requires additional review when modified. +// This driver will have external input - PE/COFF image. +// This external input must be validated carefully to avoid security issue like +// buffer overflow, integer overflow. +// +// Copyright (c) 2015 - 2018, Intel Corporation. All rights reserved.
+// +// SPDX-License-Identifier: BSD-2-Clause-Patent +// +// **/ + + +#string STR_MODULE_ABSTRACT #language en-US "Produces TCG2 protocol and measure boot environment" + +#string STR_MODULE_DESCRIPTION #language en-US "This module will produce TCG2 protocol and measure boot environment." + diff --git a/OvmfPkg/Tcg/Tcg2Dxe/TdTcg2DxeExtra.uni b/OvmfPkg/Tcg/Tcg2Dxe/TdTcg2DxeExtra.uni new file mode 100644 index 000000000000..2dfe868fccf9 --- /dev/null +++ b/OvmfPkg/Tcg/Tcg2Dxe/TdTcg2DxeExtra.uni @@ -0,0 +1,12 @@ +// /** @file +// Tcg2Dxe Localized Strings and Content +// +// Copyright (c) 2015 - 2018, Intel Corporation. All rights reserved.
+// +// SPDX-License-Identifier: BSD-2-Clause-Patent +// +// **/ + +#string STR_PROPERTIES_MODULE_NAME +#language en-US +"TCG2 (Trusted Computing Group) DXE" diff --git a/OvmfPkg/TdxDxe/TdxDxe.c b/OvmfPkg/TdxDxe/TdxDxe.c new file mode 100644 index 000000000000..cec3aea7670b --- /dev/null +++ b/OvmfPkg/TdxDxe/TdxDxe.c @@ -0,0 +1,188 @@ +/** @file + + TDX Dxe driver. This driver is dispatched early in DXE, due to being list + in APRIORI. + + This module is responsible for: + - Installing exception handle for #VE as early as possible. MSRs and MMIO may + generate an exception + - Sets PCDs indicating we are using protected mode resets + - Sets max logical cpus based on TDINFO + - Sets PCI PCDs based on resource hobs + + Copyright (c) 2020, Intel Corporation. All rights reserved.
+ + SPDX-License-Identifier: BSD-2-Clause-Patent + +**/ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/** + Location of resource hob matching type and starting address + + @param[in] Type The type of resource hob to locate. + + @param[in] Start The resource hob must at least begin at address. + + @retval pointer to resource Return pointer to a resource hob that matches or + NULL. +**/ +STATIC +EFI_HOB_RESOURCE_DESCRIPTOR * +GetResourceDescriptor( + EFI_RESOURCE_TYPE Type, + EFI_PHYSICAL_ADDRESS Start, + EFI_PHYSICAL_ADDRESS End + ) +{ + EFI_PEI_HOB_POINTERS Hob; + EFI_HOB_RESOURCE_DESCRIPTOR *ResourceDescriptor = NULL; + + Hob.Raw = GetFirstHob (EFI_HOB_TYPE_RESOURCE_DESCRIPTOR); + while (Hob.Raw != NULL) { + + DEBUG ((DEBUG_INFO, "%a:%d: resource type 0x%x %llx %llx\n", + __func__, __LINE__, + Hob.ResourceDescriptor->ResourceType, + Hob.ResourceDescriptor->PhysicalStart, + Hob.ResourceDescriptor->ResourceLength)); + + if ((Hob.ResourceDescriptor->ResourceType == Type) && + (Hob.ResourceDescriptor->PhysicalStart >= Start) && + ((Hob.ResourceDescriptor->PhysicalStart + Hob.ResourceDescriptor->ResourceLength) < End)) { + ResourceDescriptor = Hob.ResourceDescriptor; + break; + } + Hob.Raw = GET_NEXT_HOB (Hob); + Hob.Raw = GetNextHob (EFI_HOB_TYPE_RESOURCE_DESCRIPTOR, Hob.Raw); + } + + return ResourceDescriptor; +} + +/** + Location of resource hob matching type and highest address below end + + @param[in] Type The type of resource hob to locate. + + @param[in] End The resource hob return is the closest to the End address + + @retval pointer to resource Return pointer to a resource hob that matches or + NULL. +**/ +STATIC +EFI_HOB_RESOURCE_DESCRIPTOR * +GetHighestResourceDescriptor( + EFI_RESOURCE_TYPE Type, + EFI_PHYSICAL_ADDRESS End + ) +{ + EFI_PEI_HOB_POINTERS Hob; + EFI_HOB_RESOURCE_DESCRIPTOR *ResourceDescriptor = NULL; + + Hob.Raw = GetFirstHob (EFI_HOB_TYPE_RESOURCE_DESCRIPTOR); + while (Hob.Raw != NULL) { + if ((Hob.ResourceDescriptor->ResourceType == Type) && + (Hob.ResourceDescriptor->PhysicalStart < End)) { + if (!ResourceDescriptor || + (ResourceDescriptor->PhysicalStart < Hob.ResourceDescriptor->PhysicalStart)) { + ResourceDescriptor = Hob.ResourceDescriptor; + } + } + Hob.Raw = GET_NEXT_HOB (Hob); + Hob.Raw = GetNextHob (EFI_HOB_TYPE_RESOURCE_DESCRIPTOR, Hob.Raw); + } + + return ResourceDescriptor; +} + +EFI_STATUS +EFIAPI +TdxDxeEntryPoint ( + IN EFI_HANDLE ImageHandle, + IN EFI_SYSTEM_TABLE *SystemTable + ) +{ + + EFI_STATUS Status; + RETURN_STATUS PcdStatus; + EFI_HOB_RESOURCE_DESCRIPTOR *Res = NULL; + EFI_HOB_RESOURCE_DESCRIPTOR *MemRes = NULL; + EFI_HOB_PLATFORM_INFO *PlatformInfo = NULL; + EFI_HOB_GUID_TYPE *GuidHob; + UINT32 CpuMaxLogicalProcessorNumber; + TD_RETURN_DATA TdReturnData; + + GuidHob = GetFirstGuidHob(&gUefiOvmfPkgTdxPlatformGuid); + + if(GuidHob == NULL) { + return EFI_UNSUPPORTED; + } + + PlatformInfo = (EFI_HOB_PLATFORM_INFO *)GET_GUID_HOB_DATA (GuidHob); + + // + // Call TDINFO to get actual number of cpus in domain + // + Status = TdCall(TDCALL_TDINFO, 0, 0, 0, &TdReturnData); + ASSERT(Status == EFI_SUCCESS); + + CpuMaxLogicalProcessorNumber = PcdGet32 (PcdCpuMaxLogicalProcessorNumber); + + // + // Adjust PcdCpuMaxLogicalProcessorNumber, if needed. If firmware is configured for + // more than number of reported cpus, update. + // + if (CpuMaxLogicalProcessorNumber > TdReturnData.TdInfo.NumVcpus) { + PcdStatus = PcdSet32S (PcdCpuMaxLogicalProcessorNumber, TdReturnData.TdInfo.NumVcpus); + ASSERT_RETURN_ERROR(PcdStatus); + } + +#define INIT_PCDSET(NAME, RES) do { \ + PcdStatus = PcdSet64S (NAME##Base, (RES)->PhysicalStart); \ + ASSERT_RETURN_ERROR (PcdStatus); \ + PcdStatus = PcdSet64S (NAME##Size, (RES)->ResourceLength); \ + ASSERT_RETURN_ERROR (PcdStatus); \ +} while(0) + + if (PlatformInfo) { + PcdSet16S (PcdOvmfHostBridgePciDevId, PlatformInfo->HostBridgePciDevId); + + if ((Res = GetResourceDescriptor(EFI_RESOURCE_MEMORY_MAPPED_IO, (EFI_PHYSICAL_ADDRESS)0x100000000, (EFI_PHYSICAL_ADDRESS)-1)) != NULL) { + INIT_PCDSET(PcdPciMmio64, Res); + } + + if ((Res = GetResourceDescriptor(EFI_RESOURCE_IO, 0, 0x10001)) != NULL) { + INIT_PCDSET(PcdPciIo, Res); + } + + // + // To find low mmio, first find top of low memory, and then search for io space. + // + if ((MemRes = GetHighestResourceDescriptor(EFI_RESOURCE_SYSTEM_MEMORY, 0xffc00000)) != NULL) { + if ((Res = GetResourceDescriptor(EFI_RESOURCE_MEMORY_MAPPED_IO, MemRes->PhysicalStart, 0x100000000)) != NULL) { + INIT_PCDSET(PcdPciMmio32, Res); + } + } + // + // Set initial protected mode reset address to our initial mailbox + // After DXE, will update address before exiting + // + PcdStatus = PcdSet64S (PcdTdRelocatedMailboxBase, PlatformInfo->RelocatedMailBox); + ASSERT_RETURN_ERROR(PcdStatus); + } + + return EFI_SUCCESS; +} diff --git a/OvmfPkg/TdxDxe/TdxDxe.inf b/OvmfPkg/TdxDxe/TdxDxe.inf new file mode 100644 index 000000000000..c8f78cdf4f15 --- /dev/null +++ b/OvmfPkg/TdxDxe/TdxDxe.inf @@ -0,0 +1,55 @@ +#/** @file +# +# Driver clears the encryption attribute from MMIO regions when TDX is enabled +# +# Copyright (c) 2017, AMD Inc. All rights reserved.
+# +# SPDX-License-Identifier: BSD-2-Clause-Patent +# +#**/ + +[Defines] + INF_VERSION = 1.25 + BASE_NAME = TdxDxe + FILE_GUID = E750224E-7BCE-40AF-B5BB-47E3611EB5C2 + MODULE_TYPE = DXE_DRIVER + VERSION_STRING = 1.0 + ENTRY_POINT = TdxDxeEntryPoint + +[Sources] + TdxDxe.c + +[Packages] + MdeModulePkg/MdeModulePkg.dec + MdePkg/MdePkg.dec + UefiCpuPkg/UefiCpuPkg.dec + OvmfPkg/OvmfPkg.dec + +[LibraryClasses] + BaseLib + BaseMemoryLib + DebugLib + DxeServicesTableLib + MemoryAllocationLib + PcdLib + UefiDriverEntryPoint + TdxLib + HobLib + +[Depex] + TRUE + +[Guids] + gUefiOvmfPkgTdxPlatformGuid ## CONSUMES + +[Pcd] + gUefiOvmfPkgTokenSpaceGuid.PcdPciIoBase + gUefiOvmfPkgTokenSpaceGuid.PcdPciIoSize + gUefiOvmfPkgTokenSpaceGuid.PcdPciMmio32Base + gUefiOvmfPkgTokenSpaceGuid.PcdPciMmio32Size + gUefiOvmfPkgTokenSpaceGuid.PcdPciMmio64Base + gUefiOvmfPkgTokenSpaceGuid.PcdPciMmio64Size + gUefiOvmfPkgTokenSpaceGuid.PcdOvmfHostBridgePciDevId + gUefiCpuPkgTokenSpaceGuid.PcdCpuMaxLogicalProcessorNumber + gUefiOvmfPkgTokenSpaceGuid.PcdUseTdxEmulation + gUefiOvmfPkgTokenSpaceGuid.PcdTdRelocatedMailboxBase diff --git a/SecurityPkg/Library/DxeTpm2MeasureBootLib/DxeTpm2MeasureBootLib.c b/SecurityPkg/Library/DxeTpm2MeasureBootLib/DxeTpm2MeasureBootLib.c index 92eac715800f..a59a65a799c8 100644 --- a/SecurityPkg/Library/DxeTpm2MeasureBootLib/DxeTpm2MeasureBootLib.c +++ b/SecurityPkg/Library/DxeTpm2MeasureBootLib/DxeTpm2MeasureBootLib.c @@ -41,6 +41,7 @@ SPDX-License-Identifier: BSD-2-Clause-Patent #include #include #include +#include // // Flag to check GPT partition. It only need be measured once. @@ -374,6 +375,51 @@ Tcg2MeasurePeImage ( return Status; } +EFI_STATUS +EFIAPI +GetTcg2Protocol ( + EFI_TCG2_PROTOCOL **Tcg2Protocol + ) +{ + EFI_STATUS Status; + EFI_TCG2_BOOT_SERVICE_CAPABILITY ProtocolCapability; + + if (ProbeTdGuest()) { + Status = gBS->LocateProtocol (&gTdTcg2ProtocolGuid, NULL, (VOID **) Tcg2Protocol); + if (EFI_ERROR (Status)) { + // + // TdTcg2 protocol is not installed. + // + DEBUG ((EFI_D_VERBOSE, "DxeTpm2MeasureBootHandler - TdTcg2 - %r\n", Status)); + return EFI_UNSUPPORTED; + } + } else { + Status = gBS->LocateProtocol (&gEfiTcg2ProtocolGuid, NULL, (VOID **) Tcg2Protocol); + if (EFI_ERROR (Status)) { + // + // Tcg2 protocol is not installed. So, TPM2 is not present. + // + DEBUG ((EFI_D_VERBOSE, "DxeTpm2MeasureBootHandler - Tcg2 - %r\n", Status)); + return EFI_UNSUPPORTED; + } + + ProtocolCapability.Size = (UINT8) sizeof (ProtocolCapability); + Status = (*Tcg2Protocol)->GetCapability ( + *Tcg2Protocol, + &ProtocolCapability + ); + if (EFI_ERROR (Status) || (!ProtocolCapability.TPMPresentFlag)) { + // + // TPM device doesn't work or activate. + // + DEBUG ((EFI_D_ERROR, "DxeTpm2MeasureBootHandler (%r) - TPMPresentFlag - %x\n", Status, ProtocolCapability.TPMPresentFlag)); + return EFI_UNSUPPORTED; + } + } + + return EFI_SUCCESS; +} + /** The security handler is used to abstract platform-specific policy from the DXE core response to an attempt to use a file that returns a @@ -424,7 +470,7 @@ DxeTpm2MeasureBootHandler ( { EFI_TCG2_PROTOCOL *Tcg2Protocol; EFI_STATUS Status; - EFI_TCG2_BOOT_SERVICE_CAPABILITY ProtocolCapability; + //EFI_TCG2_BOOT_SERVICE_CAPABILITY ProtocolCapability; EFI_DEVICE_PATH_PROTOCOL *DevicePathNode; EFI_DEVICE_PATH_PROTOCOL *OrigDevicePathNode; EFI_HANDLE Handle; @@ -435,26 +481,10 @@ DxeTpm2MeasureBootHandler ( EFI_PHYSICAL_ADDRESS FvAddress; UINT32 Index; - Status = gBS->LocateProtocol (&gEfiTcg2ProtocolGuid, NULL, (VOID **) &Tcg2Protocol); + Tcg2Protocol = NULL; + Status = GetTcg2Protocol(&Tcg2Protocol); + DEBUG ((DEBUG_INFO, "Tcg2Protocol=%p, Status=%r\n", Tcg2Protocol, Status)); if (EFI_ERROR (Status)) { - // - // Tcg2 protocol is not installed. So, TPM2 is not present. - // Don't do any measurement, and directly return EFI_SUCCESS. - // - DEBUG ((EFI_D_VERBOSE, "DxeTpm2MeasureBootHandler - Tcg2 - %r\n", Status)); - return EFI_SUCCESS; - } - - ProtocolCapability.Size = (UINT8) sizeof (ProtocolCapability); - Status = Tcg2Protocol->GetCapability ( - Tcg2Protocol, - &ProtocolCapability - ); - if (EFI_ERROR (Status) || (!ProtocolCapability.TPMPresentFlag)) { - // - // TPM device doesn't work or activate. - // - DEBUG ((EFI_D_ERROR, "DxeTpm2MeasureBootHandler (%r) - TPMPresentFlag - %x\n", Status, ProtocolCapability.TPMPresentFlag)); return EFI_SUCCESS; } diff --git a/SecurityPkg/Library/DxeTpm2MeasureBootLib/DxeTpm2MeasureBootLib.inf b/SecurityPkg/Library/DxeTpm2MeasureBootLib/DxeTpm2MeasureBootLib.inf index 2506abbe7c8b..68b4d003fa2b 100644 --- a/SecurityPkg/Library/DxeTpm2MeasureBootLib/DxeTpm2MeasureBootLib.inf +++ b/SecurityPkg/Library/DxeTpm2MeasureBootLib/DxeTpm2MeasureBootLib.inf @@ -55,12 +55,14 @@ BaseLib SecurityManagementLib HobLib + TdxProbeLib [Guids] gMeasuredFvHobGuid ## SOMETIMES_CONSUMES ## HOB [Protocols] gEfiTcg2ProtocolGuid ## SOMETIMES_CONSUMES + gTdTcg2ProtocolGuid gEfiFirmwareVolumeBlockProtocolGuid ## SOMETIMES_CONSUMES gEfiBlockIoProtocolGuid ## SOMETIMES_CONSUMES gEfiDiskIoProtocolGuid ## SOMETIMES_CONSUMES diff --git a/SecurityPkg/Library/DxeTpmMeasurementLib/DxeTpmMeasurementLib.c b/SecurityPkg/Library/DxeTpmMeasurementLib/DxeTpmMeasurementLib.c index 061136ee7860..2d1540066d61 100644 --- a/SecurityPkg/Library/DxeTpmMeasurementLib/DxeTpmMeasurementLib.c +++ b/SecurityPkg/Library/DxeTpmMeasurementLib/DxeTpmMeasurementLib.c @@ -19,8 +19,9 @@ SPDX-License-Identifier: BSD-2-Clause-Patent #include #include - - +#include +#include +#include /** Tpm12 measure and log data, and extend the measurement result into a specific PCR. @@ -149,6 +150,68 @@ Tpm20MeasureAndLogData ( return Status; } +/** + Tdx measure and log data, and extend the measurement result into a + specific TDX RTMR. + + @param[in] Index RTMR Index. + @param[in] EventType Event type. + @param[in] EventLog Measurement event log. + @param[in] LogLen Event log length in bytes. + @param[in] HashData The start of the data buffer to be hashed, extended. + @param[in] HashDataLen The length, in bytes, of the buffer referenced by HashData + + @retval EFI_SUCCESS Operation completed successfully. + @retval EFI_UNSUPPORTED Tdx device not available. + @retval EFI_OUT_OF_RESOURCES Out of memory. + @retval EFI_DEVICE_ERROR The operation was unsuccessful. +**/ +EFI_STATUS +EFIAPI +TdxMeasureAndLogData ( + IN UINT32 Index, + IN UINT32 EventType, + IN VOID *EventLog, + IN UINT32 LogLen, + IN VOID *HashData, + IN UINT64 HashDataLen + ) +{ + EFI_STATUS Status; + EFI_TCG2_PROTOCOL *Tcg2Protocol; + EFI_TCG2_EVENT *Tcg2Event; + + DEBUG ((DEBUG_INFO, "TdxMeasureAndLogData\n")); + Status = gBS->LocateProtocol (&gTdTcg2ProtocolGuid, NULL, (VOID **) &Tcg2Protocol); + if (EFI_ERROR (Status)) { + return Status; + } + + Tcg2Event = (EFI_TCG2_EVENT *) AllocateZeroPool (LogLen + sizeof (EFI_TCG2_EVENT)); + if(Tcg2Event == NULL) { + return EFI_OUT_OF_RESOURCES; + } + + Tcg2Event->Size = (UINT32) LogLen + sizeof (EFI_TCG2_EVENT) - sizeof (Tcg2Event->Event); + Tcg2Event->Header.HeaderSize = sizeof (EFI_TCG2_EVENT_HEADER); + Tcg2Event->Header.HeaderVersion = EFI_TCG2_EVENT_HEADER_VERSION; + Tcg2Event->Header.PCRIndex = Index; + Tcg2Event->Header.EventType = EventType; + CopyMem (&Tcg2Event->Event[0], EventLog, LogLen); + + Status = Tcg2Protocol->HashLogExtendEvent ( + Tcg2Protocol, + 0, + (EFI_PHYSICAL_ADDRESS) (UINTN) HashData, + HashDataLen, + Tcg2Event + ); + FreePool (Tcg2Event); + + return Status; +} + + /** Tpm measure and log data, and extend the measurement result into a specific PCR. @@ -177,6 +240,20 @@ TpmMeasureAndLogData ( { EFI_STATUS Status; + // + // Try to measure using TDX protocol + // + if (ProbeTdGuest()) { + return TdxMeasureAndLogData ( + PcrIndex, + EventType, + EventLog, + LogLen, + HashData, + HashDataLen + ); + } + // // Try to measure using Tpm20 protocol // diff --git a/SecurityPkg/Library/DxeTpmMeasurementLib/DxeTpmMeasurementLib.inf b/SecurityPkg/Library/DxeTpmMeasurementLib/DxeTpmMeasurementLib.inf index 7d41bc41f95d..84d2674877d4 100644 --- a/SecurityPkg/Library/DxeTpmMeasurementLib/DxeTpmMeasurementLib.inf +++ b/SecurityPkg/Library/DxeTpmMeasurementLib/DxeTpmMeasurementLib.inf @@ -38,7 +38,10 @@ DebugLib MemoryAllocationLib UefiBootServicesTableLib + TdxProbeLib + TdxLib [Protocols] gEfiTcgProtocolGuid ## SOMETIMES_CONSUMES gEfiTcg2ProtocolGuid ## SOMETIMES_CONSUMES + gTdTcg2ProtocolGuid diff --git a/SecurityPkg/Tcg/Tcg2Dxe/Tcg2Dxe.c b/SecurityPkg/Tcg/Tcg2Dxe/Tcg2Dxe.c index 6d17616c1ce4..8658135c7c82 100644 --- a/SecurityPkg/Tcg/Tcg2Dxe/Tcg2Dxe.c +++ b/SecurityPkg/Tcg/Tcg2Dxe/Tcg2Dxe.c @@ -44,6 +44,7 @@ SPDX-License-Identifier: BSD-2-Clause-Patent #include #include #include +#include #define PERF_ID_TCG2_DXE 0x3120 @@ -2637,6 +2638,10 @@ DriverEntry ( UINT32 ActivePCRBanks; UINT32 NumberOfPCRBanks; + if (ProbeTdGuest()) { + return EFI_UNSUPPORTED; + } + mImageHandle = ImageHandle; if (CompareGuid (PcdGetPtr(PcdTpmInstanceGuid), &gEfiTpmDeviceInstanceNoneGuid) || diff --git a/SecurityPkg/Tcg/Tcg2Dxe/Tcg2Dxe.inf b/SecurityPkg/Tcg/Tcg2Dxe/Tcg2Dxe.inf index 7dc7a2683d71..a427f7bbdd64 100644 --- a/SecurityPkg/Tcg/Tcg2Dxe/Tcg2Dxe.inf +++ b/SecurityPkg/Tcg/Tcg2Dxe/Tcg2Dxe.inf @@ -64,6 +64,7 @@ ReportStatusCodeLib Tcg2PhysicalPresenceLib PeCoffLib + TdxProbeLib [Guids] ## SOMETIMES_CONSUMES ## Variable:L"SecureBoot" diff --git a/UefiCpuPkg/Include/Library/VmTdExitLib.h b/UefiCpuPkg/Include/Library/VmTdExitLib.h new file mode 100644 index 000000000000..0971bedf9715 --- /dev/null +++ b/UefiCpuPkg/Include/Library/VmTdExitLib.h @@ -0,0 +1,70 @@ +/** @file + Public header file for the VMTDEXIT Support library class. + + This library class defines some routines used when invoking the VMEXIT + instruction in support of VMX and TDX to handle #VE exceptions. + + Copyright (c) 2020, Intel Inc. All rights reserved.
+ Copyright (C) 2020, Advanced Micro Devices, Inc. All rights reserved.
+ SPDX-License-Identifier: BSD-2-Clause-Patent + +**/ + +#ifndef __VM_TD_EXIT_LIB_H__ +#define __VM_TD_EXIT_LIB_H__ + +#include +#include +#include + +#define VE_EXCEPTION 20 + +EFI_STATUS +EFIAPI +TdCall( + IN UINT64 Leaf, + IN UINT64 Arg1, + IN UINT64 Arg2, + IN UINT64 Arg3, + IN VOID *Results + ); + +EFI_STATUS +EFIAPI +TdVmCall ( + IN UINT64 Leaf, + IN UINT64 Arg1, + IN UINT64 Arg2, + IN UINT64 Arg3, + IN UINT64 Arg4, + IN VOID *Results + ); + + +/** + Handle a #VE exception. + + Performs the necessary processing to handle a #VE exception. + + The base library function returns an error equal to VE_EXCEPTION, + to be propagated to the standard exception handling stack. + + @param[in, out] ExceptionType Pointer to an EFI_EXCEPTION_TYPE to be set + as value to use on error. + @param[in, out] SystemContext Pointer to EFI_SYSTEM_CONTEXT + + @retval EFI_SUCCESS Exception handled + @retval EFI_UNSUPPORTED #VE not supported, (new) exception value to + propagate provided + @retval EFI_PROTOCOL_ERROR #VE handling failed, (new) exception value to + propagate provided + +**/ +EFI_STATUS +EFIAPI +VmTdExitHandleVe ( + IN OUT EFI_EXCEPTION_TYPE *ExceptionType, + IN OUT EFI_SYSTEM_CONTEXT SystemContext + ); + +#endif diff --git a/UefiCpuPkg/Library/BaseXApicX2ApicLib/ApicLib.c b/UefiCpuPkg/Library/BaseXApicX2ApicLib/ApicLib.c new file mode 100644 index 000000000000..9ee9460b3c6b --- /dev/null +++ b/UefiCpuPkg/Library/BaseXApicX2ApicLib/ApicLib.c @@ -0,0 +1,30 @@ +/** @file + Local APIC Library. + + This local APIC library instance supports x2APIC capable processors + which have xAPIC and x2APIC modes. + + Copyright (c) 2010 - 2019, Intel Corporation. All rights reserved.
+ Copyright (c) 2017, AMD Inc. All rights reserved.
+ + SPDX-License-Identifier: BSD-2-Clause-Patent + +**/ + +#include +#include +//#include +#include "ApicLibInternal.h" +//#include + +BOOLEAN +EFIAPI +InitializeMsrAccess ( + VOID + ) +{ +// if(!mMsrAccessInitialized){ +// mMsrAccessInitialized = TRUE; +// } + return FALSE; +} diff --git a/UefiCpuPkg/Library/BaseXApicX2ApicLib/ApicLibInternal.h b/UefiCpuPkg/Library/BaseXApicX2ApicLib/ApicLibInternal.h new file mode 100644 index 000000000000..ef3913f79ef6 --- /dev/null +++ b/UefiCpuPkg/Library/BaseXApicX2ApicLib/ApicLibInternal.h @@ -0,0 +1,8 @@ +#ifndef __APIC_LIB_INTERNAL_H__ +#define __APIC_LIB_INTERNAL_H__ + +BOOLEAN +EFIAPI +InitializeMsrAccess(VOID); + +#endif diff --git a/UefiCpuPkg/Library/BaseXApicX2ApicLib/BaseXApicX2ApicLib.c b/UefiCpuPkg/Library/BaseXApicX2ApicLib/BaseXApicX2ApicLib.c index cdcbca046191..600cf69ac972 100644 --- a/UefiCpuPkg/Library/BaseXApicX2ApicLib/BaseXApicX2ApicLib.c +++ b/UefiCpuPkg/Library/BaseXApicX2ApicLib/BaseXApicX2ApicLib.c @@ -23,11 +23,132 @@ #include #include #include +#include +#include +#include "ApicLibInternal.h" // // Library internal functions // +STATIC +BOOLEAN +EFIAPI +UseMSR ( + IN UINT32 MsrIndex + ) +{ + switch (MsrIndex) { + case MSR_IA32_X2APIC_TPR: + case MSR_IA32_X2APIC_PPR: + case MSR_IA32_X2APIC_EOI: + case MSR_IA32_X2APIC_ISR0: + case MSR_IA32_X2APIC_ISR1: + case MSR_IA32_X2APIC_ISR2: + case MSR_IA32_X2APIC_ISR3: + case MSR_IA32_X2APIC_ISR4: + case MSR_IA32_X2APIC_ISR5: + case MSR_IA32_X2APIC_ISR6: + case MSR_IA32_X2APIC_ISR7: + case MSR_IA32_X2APIC_TMR0: + case MSR_IA32_X2APIC_TMR1: + case MSR_IA32_X2APIC_TMR2: + case MSR_IA32_X2APIC_TMR3: + case MSR_IA32_X2APIC_TMR4: + case MSR_IA32_X2APIC_TMR5: + case MSR_IA32_X2APIC_TMR6: + case MSR_IA32_X2APIC_TMR7: + case MSR_IA32_X2APIC_IRR0: + case MSR_IA32_X2APIC_IRR1: + case MSR_IA32_X2APIC_IRR2: + case MSR_IA32_X2APIC_IRR3: + case MSR_IA32_X2APIC_IRR4: + case MSR_IA32_X2APIC_IRR5: + case MSR_IA32_X2APIC_IRR6: + case MSR_IA32_X2APIC_IRR7: + return TRUE; + default: + break; + } + return FALSE; +} + +UINT64 +EFIAPI +ReadMsrReg64 ( + IN UINT32 MsrIndex + ) +{ + UINT64 Val; + UINT64 Status; + if (!UseMSR (MsrIndex) && InitializeMsrAccess()) { + Status = TdVmCall(TDVMCALL_RDMSR, (UINT64)MsrIndex, 0, 0, 0, &Val); + if (Status != 0) { + TdVmCall(TDVMCALL_HALT, 0, 0, 0, 0, 0); + } + } else { + Val = AsmReadMsr64 (MsrIndex); + } + return Val; +} + +VOID +EFIAPI +WriteMsrReg64 ( + IN UINT32 MsrIndex, + IN UINT64 Val + ) +{ + UINT64 Status; + if (!UseMSR (MsrIndex) && InitializeMsrAccess()) { + Status = TdVmCall(TDVMCALL_WRMSR, (UINT64)MsrIndex, Val, 0, 0, 0); + if (Status != 0) { + TdVmCall(TDVMCALL_HALT, 0, 0, 0, 0, 0); + } + } else { + AsmWriteMsr64(MsrIndex, Val); + } +} + +UINT32 +EFIAPI +ReadMsrReg32 ( + IN UINT32 MsrIndex + ) +{ + UINT64 Val; + UINT64 Status; + if (!UseMSR (MsrIndex) && InitializeMsrAccess()) { + Status = TdVmCall(TDVMCALL_RDMSR, (UINT64)MsrIndex, 0, 0, 0, &Val); + if (Status != 0) { + TdVmCall(TDVMCALL_HALT, 0, 0, 0, 0, 0); + } + } else { + Val = AsmReadMsr32 (MsrIndex); + } + return (UINT32)(UINTN)Val; +} + +VOID +EFIAPI +WriteMsrReg32 ( + IN UINT32 MsrIndex, + IN UINT32 Val + ) +{ + UINT64 Status; + if (!UseMSR (MsrIndex) && InitializeMsrAccess()) { + Status = TdVmCall(TDVMCALL_WRMSR, (UINT64)MsrIndex, (UINT64)Val, 0, 0, 0); + if (Status != 0) { + DEBUG((DEBUG_ERROR, "WriteMsrReg32 returned failure. Status=0x%llx\n", Status)); + TdVmCall(TDVMCALL_HALT, 0, 0, 0, 0, 0); + } + } else { + AsmWriteMsr32(MsrIndex, Val); + } +} + + /** Determine if the CPU supports the Local APIC Base Address MSR. @@ -77,7 +198,7 @@ GetLocalApicBaseAddress ( return PcdGet32 (PcdCpuLocalApicBaseAddress); } - ApicBaseMsr.Uint64 = AsmReadMsr64 (MSR_IA32_APIC_BASE); + ApicBaseMsr.Uint64 = ReadMsrReg64 (MSR_IA32_APIC_BASE); return (UINTN)(LShiftU64 ((UINT64) ApicBaseMsr.Bits.ApicBaseHi, 32)) + (((UINTN)ApicBaseMsr.Bits.ApicBase) << 12); @@ -108,12 +229,12 @@ SetLocalApicBaseAddress ( return; } - ApicBaseMsr.Uint64 = AsmReadMsr64 (MSR_IA32_APIC_BASE); + ApicBaseMsr.Uint64 = ReadMsrReg64 (MSR_IA32_APIC_BASE); ApicBaseMsr.Bits.ApicBase = (UINT32) (BaseAddress >> 12); ApicBaseMsr.Bits.ApicBaseHi = (UINT32) (RShiftU64((UINT64) BaseAddress, 32)); - AsmWriteMsr64 (MSR_IA32_APIC_BASE, ApicBaseMsr.Uint64); + WriteMsrReg64 (MSR_IA32_APIC_BASE, ApicBaseMsr.Uint64); } /** @@ -153,7 +274,7 @@ ReadLocalApicReg ( ASSERT (MmioOffset != XAPIC_ICR_HIGH_OFFSET); MsrIndex = (UINT32)(MmioOffset >> 4) + X2APIC_MSR_BASE_ADDRESS; - return AsmReadMsr32 (MsrIndex); + return ReadMsrReg32 (MsrIndex); } } @@ -202,7 +323,7 @@ WriteLocalApicReg ( // Use memory fence here to force the serializing semantics to be consisent with xAPIC mode. // MemoryFence (); - AsmWriteMsr32 (MsrIndex, Value); + WriteMsrReg32 (MsrIndex, Value); } } @@ -309,7 +430,7 @@ GetApicMode ( return LOCAL_APIC_MODE_XAPIC; } - ApicBaseMsr.Uint64 = AsmReadMsr64 (MSR_IA32_APIC_BASE); + ApicBaseMsr.Uint64 = ReadMsrReg64 (MSR_IA32_APIC_BASE); // // Local APIC should have been enabled // @@ -350,13 +471,14 @@ SetApicMode ( CurrentMode = GetApicMode (); if (CurrentMode == LOCAL_APIC_MODE_XAPIC) { + switch (ApicMode) { case LOCAL_APIC_MODE_XAPIC: break; case LOCAL_APIC_MODE_X2APIC: - ApicBaseMsr.Uint64 = AsmReadMsr64 (MSR_IA32_APIC_BASE); + ApicBaseMsr.Uint64 = ReadMsrReg64 (MSR_IA32_APIC_BASE); ApicBaseMsr.Bits.EXTD = 1; - AsmWriteMsr64 (MSR_IA32_APIC_BASE, ApicBaseMsr.Uint64); + WriteMsrReg64 (MSR_IA32_APIC_BASE, ApicBaseMsr.Uint64); break; default: ASSERT (FALSE); @@ -817,6 +939,8 @@ InitializeApicTimer ( LOCAL_APIC_LVT_TIMER LvtTimer; UINT32 Divisor; + InitializeMsrAccess(); + // // Ensure local APIC is in software-enabled state. // diff --git a/UefiCpuPkg/Library/BaseXApicX2ApicLib/BaseXApicX2ApicLib.inf b/UefiCpuPkg/Library/BaseXApicX2ApicLib/BaseXApicX2ApicLib.inf index 1e2a4f8b790f..b49219fbfb6a 100644 --- a/UefiCpuPkg/Library/BaseXApicX2ApicLib/BaseXApicX2ApicLib.inf +++ b/UefiCpuPkg/Library/BaseXApicX2ApicLib/BaseXApicX2ApicLib.inf @@ -26,6 +26,7 @@ # [Sources] + ApicLib.c BaseXApicX2ApicLib.c [Packages] @@ -39,6 +40,7 @@ IoLib PcdLib UefiCpuLib + TdxProbeLib [Pcd] gUefiCpuPkgTokenSpaceGuid.PcdCpuInitIpiDelayInMicroSeconds ## SOMETIMES_CONSUMES diff --git a/UefiCpuPkg/Library/BaseXApicX2ApicLib/BaseXApicX2ApicLibDxe.inf b/UefiCpuPkg/Library/BaseXApicX2ApicLib/BaseXApicX2ApicLibDxe.inf new file mode 100644 index 000000000000..955709765e68 --- /dev/null +++ b/UefiCpuPkg/Library/BaseXApicX2ApicLib/BaseXApicX2ApicLibDxe.inf @@ -0,0 +1,51 @@ +## @file +# The Local Apic library supports x2APIC capable processors which have xAPIC and x2APIC modes. +# +# Note: Local APIC library assumes local APIC is enabled. It does not handle cases +# where local APIC is disabled. +# +# Copyright (c) 2010 - 2018, Intel Corporation. All rights reserved.
+# SPDX-License-Identifier: BSD-2-Clause-Patent +# +## + +[Defines] + INF_VERSION = 0x00010005 + BASE_NAME = BaseXApicX2ApicLibDxe + MODULE_UNI_FILE = BaseXApicX2ApicLib.uni + FILE_GUID = 53DDFFC8-5812-44E2-8994-9542716B61CF + MODULE_TYPE = BASE + VERSION_STRING = 1.1 + LIBRARY_CLASS = LocalApicLib + +# +# The following information is for reference only and not required by the build tools. +# +# VALID_ARCHITECTURES = X64 +# + +[Sources] + BaseXApicX2ApicLib.c + DxeApicLib.c + +[Packages] + MdePkg/MdePkg.dec + UefiCpuPkg/UefiCpuPkg.dec + +[LibraryClasses] + BaseLib + DebugLib + TimerLib + IoLib + PcdLib + UefiCpuLib + TdxLib + TdxProbeLib + +[Pcd] + gUefiCpuPkgTokenSpaceGuid.PcdCpuInitIpiDelayInMicroSeconds ## SOMETIMES_CONSUMES + gUefiCpuPkgTokenSpaceGuid.PcdCpuLocalApicBaseAddress ## SOMETIMES_CONSUMES + +[Protocols] + gEfiCpuArchProtocolGuid ## CONSUMES + diff --git a/UefiCpuPkg/Library/BaseXApicX2ApicLib/BaseXApicX2ApicLibSec.inf b/UefiCpuPkg/Library/BaseXApicX2ApicLib/BaseXApicX2ApicLibSec.inf new file mode 100644 index 000000000000..d14b9edebc78 --- /dev/null +++ b/UefiCpuPkg/Library/BaseXApicX2ApicLib/BaseXApicX2ApicLibSec.inf @@ -0,0 +1,51 @@ +## @file +# The Local Apic library supports x2APIC capable processors which have xAPIC and x2APIC modes. +# +# Note: Local APIC library assumes local APIC is enabled. It does not handle cases +# where local APIC is disabled. +# +# Copyright (c) 2010 - 2018, Intel Corporation. All rights reserved.
+# SPDX-License-Identifier: BSD-2-Clause-Patent +# +## + +[Defines] + INF_VERSION = 0x00010005 + BASE_NAME = BaseXApicX2ApicLibSec + MODULE_UNI_FILE = BaseXApicX2ApicLib.uni + FILE_GUID = BBDA39E1-1F30-4A77-8F96-DB3052BBC7BB + MODULE_TYPE = BASE + VERSION_STRING = 1.1 + LIBRARY_CLASS = LocalApicLib|SEC + +# +# The following information is for reference only and not required by the build tools. +# +# VALID_ARCHITECTURES = X64 +# + +[Sources] + BaseXApicX2ApicLib.c + SecApicLib.c + +[Packages] + MdePkg/MdePkg.dec + UefiCpuPkg/UefiCpuPkg.dec + +[LibraryClasses] + BaseLib + DebugLib + TimerLib + IoLib + PcdLib + UefiCpuLib + TdxLib + TdxProbeLib + +[Pcd] + gUefiCpuPkgTokenSpaceGuid.PcdCpuInitIpiDelayInMicroSeconds ## SOMETIMES_CONSUMES + gUefiCpuPkgTokenSpaceGuid.PcdCpuLocalApicBaseAddress ## SOMETIMES_CONSUMES + +[Protocols] + gEfiCpuArchProtocolGuid ## CONSUMES + diff --git a/UefiCpuPkg/Library/BaseXApicX2ApicLib/DxeApicLib.c b/UefiCpuPkg/Library/BaseXApicX2ApicLib/DxeApicLib.c new file mode 100644 index 000000000000..f679a21c66ed --- /dev/null +++ b/UefiCpuPkg/Library/BaseXApicX2ApicLib/DxeApicLib.c @@ -0,0 +1,79 @@ +/** @file + Local APIC Library. + + This local APIC library instance supports x2APIC capable processors + which have xAPIC and x2APIC modes. + + Copyright (c) 2010 - 2019, Intel Corporation. All rights reserved.
+ Copyright (c) 2017, AMD Inc. All rights reserved.
+ + SPDX-License-Identifier: BSD-2-Clause-Patent + +**/ + +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include "ApicLibInternal.h" + +static BOOLEAN mUseTdvmcall = FALSE; +static BOOLEAN mMsrAccessInitialized = FALSE; + +BOOLEAN +EFIAPI +InitializeMsrAccess ( + VOID + ) +{ + EFI_STATUS Status; + EFI_CPU_ARCH_PROTOCOL *Cpu; + BOOLEAN TdGuest; + + // + // We can't handle #ve until after the Cpu Arch Protocol has been installed + // When using the performance counters, it can cause apic access before this + // protocol is enabled. + // + if (!mMsrAccessInitialized) { + + TdGuest = ProbeTdGuest(); + + if(!TdGuest){ + // + // Non-TD guest + // + mMsrAccessInitialized = TRUE; + mUseTdvmcall = FALSE; + + }else{ + // + // TD guest + // We can't handle #ve until the Cpu Arch Protocol has been installed + // When using the performance counters, it can cause apic access before this + // protocol is enabled. + // + Status = gBS->LocateProtocol (&gEfiCpuArchProtocolGuid, NULL, (VOID **) &Cpu); + + if (!EFI_ERROR (Status)) { + mMsrAccessInitialized = TRUE; + mUseTdvmcall = TRUE; + } + } + + } + return mUseTdvmcall; +} diff --git a/UefiCpuPkg/Library/BaseXApicX2ApicLib/SecApicLib.c b/UefiCpuPkg/Library/BaseXApicX2ApicLib/SecApicLib.c new file mode 100644 index 000000000000..bc0acfd5d281 --- /dev/null +++ b/UefiCpuPkg/Library/BaseXApicX2ApicLib/SecApicLib.c @@ -0,0 +1,36 @@ +/** @file + Local APIC Library. + + This local APIC library instance supports x2APIC capable processors + which have xAPIC and x2APIC modes. + + Copyright (c) 2010 - 2019, Intel Corporation. All rights reserved.
+ Copyright (c) 2017, AMD Inc. All rights reserved.
+ + SPDX-License-Identifier: BSD-2-Clause-Patent + +**/ + +#include +#include +#include +#include + +#include "ApicLibInternal.h" + +static BOOLEAN mUseTdvmcall = FALSE; +static BOOLEAN mMsrAccessInitialized = FALSE; + +BOOLEAN +EFIAPI +InitializeMsrAccess ( + VOID + ) +{ + if (!mMsrAccessInitialized) { + mUseTdvmcall = ProbeTdGuest(); + + mMsrAccessInitialized = TRUE; + } + return mUseTdvmcall; +} diff --git a/UefiCpuPkg/Library/CpuExceptionHandlerLib/DxeCpuExceptionHandlerLib.inf b/UefiCpuPkg/Library/CpuExceptionHandlerLib/DxeCpuExceptionHandlerLib.inf index 07b34c92a892..0305ed55e560 100644 --- a/UefiCpuPkg/Library/CpuExceptionHandlerLib/DxeCpuExceptionHandlerLib.inf +++ b/UefiCpuPkg/Library/CpuExceptionHandlerLib/DxeCpuExceptionHandlerLib.inf @@ -58,3 +58,4 @@ MemoryAllocationLib DebugLib VmgExitLib + VmTdExitLib diff --git a/UefiCpuPkg/Library/CpuExceptionHandlerLib/PeiCpuExceptionHandlerLib.inf b/UefiCpuPkg/Library/CpuExceptionHandlerLib/PeiCpuExceptionHandlerLib.inf index feae7b3e06de..255e1e285006 100644 --- a/UefiCpuPkg/Library/CpuExceptionHandlerLib/PeiCpuExceptionHandlerLib.inf +++ b/UefiCpuPkg/Library/CpuExceptionHandlerLib/PeiCpuExceptionHandlerLib.inf @@ -53,6 +53,8 @@ MemoryAllocationLib SynchronizationLib VmgExitLib + VmTdExitLib + [Pcd] gEfiMdeModulePkgTokenSpaceGuid.PcdCpuStackGuard # CONSUMES diff --git a/UefiCpuPkg/Library/CpuExceptionHandlerLib/PeiDxeSmmCpuException.c b/UefiCpuPkg/Library/CpuExceptionHandlerLib/PeiDxeSmmCpuException.c index 892d349d4b37..0976a880825b 100644 --- a/UefiCpuPkg/Library/CpuExceptionHandlerLib/PeiDxeSmmCpuException.c +++ b/UefiCpuPkg/Library/CpuExceptionHandlerLib/PeiDxeSmmCpuException.c @@ -8,6 +8,7 @@ SPDX-License-Identifier: BSD-2-Clause-Patent #include #include +#include #include "CpuExceptionCommon.h" /** @@ -45,6 +46,23 @@ CommonExceptionHandlerWorker ( } } + if (ExceptionType == VE_EXCEPTION) { + EFI_STATUS Status; + // + // #VE needs to be handled immediately upon enabling exception handling + // and therefore can't use the RegisterCpuInterruptHandler() interface. + // + // Handle the #VE: + // On EFI_SUCCESS - Exception has been handled, return + // On other - ExceptionType contains (possibly new) exception + // value + // + Status = VmTdExitHandleVe (&ExceptionType, SystemContext); + if (!EFI_ERROR (Status)) { + return; + } + } + ExceptionHandlerContext = (EXCEPTION_HANDLER_CONTEXT *) (UINTN) (SystemContext.SystemContextIa32); ReservedVectors = ExceptionHandlerData->ReservedVectors; ExternalInterruptHandler = ExceptionHandlerData->ExternalInterruptHandler; diff --git a/UefiCpuPkg/Library/CpuExceptionHandlerLib/SecPeiCpuException.c b/UefiCpuPkg/Library/CpuExceptionHandlerLib/SecPeiCpuException.c index 01b5a2f1f4fc..173047a6b494 100644 --- a/UefiCpuPkg/Library/CpuExceptionHandlerLib/SecPeiCpuException.c +++ b/UefiCpuPkg/Library/CpuExceptionHandlerLib/SecPeiCpuException.c @@ -8,6 +8,7 @@ SPDX-License-Identifier: BSD-2-Clause-Patent #include #include +#include #include "CpuExceptionCommon.h" CONST UINTN mDoFarReturnFlag = 0; @@ -43,6 +44,24 @@ CommonExceptionHandler ( } } + if (ExceptionType == VE_EXCEPTION) { + EFI_STATUS Status; + // + // #VE needs to be handled immediately upon enabling exception handling + // and therefore can't use the RegisterCpuInterruptHandler() interface + // (which isn't supported under Sec and Pei anyway). + // + // Handle the #VE: + // On EFI_SUCCESS - Exception has been handled, return + // On other - ExceptionType contains (possibly new) exception + // value + // + Status = VmTdExitHandleVe (&ExceptionType, SystemContext); + if (!EFI_ERROR (Status)) { + return; + } + } + // // Initialize the serial port before dumping. // diff --git a/UefiCpuPkg/Library/CpuExceptionHandlerLib/SecPeiCpuExceptionHandlerLib.inf b/UefiCpuPkg/Library/CpuExceptionHandlerLib/SecPeiCpuExceptionHandlerLib.inf index 967cb61ba6d9..94a9a18f1865 100644 --- a/UefiCpuPkg/Library/CpuExceptionHandlerLib/SecPeiCpuExceptionHandlerLib.inf +++ b/UefiCpuPkg/Library/CpuExceptionHandlerLib/SecPeiCpuExceptionHandlerLib.inf @@ -49,3 +49,4 @@ LocalApicLib PeCoffGetEntryPointLib VmgExitLib + VmTdExitLib diff --git a/UefiCpuPkg/Library/CpuExceptionHandlerLib/SmmCpuExceptionHandlerLib.inf b/UefiCpuPkg/Library/CpuExceptionHandlerLib/SmmCpuExceptionHandlerLib.inf index 4cdb11c04ea0..fee14d027b43 100644 --- a/UefiCpuPkg/Library/CpuExceptionHandlerLib/SmmCpuExceptionHandlerLib.inf +++ b/UefiCpuPkg/Library/CpuExceptionHandlerLib/SmmCpuExceptionHandlerLib.inf @@ -52,4 +52,5 @@ PeCoffGetEntryPointLib DebugLib VmgExitLib + VmTdExitLib diff --git a/UefiCpuPkg/Library/CpuExceptionHandlerLib/Xcode5SecPeiCpuExceptionHandlerLib.inf b/UefiCpuPkg/Library/CpuExceptionHandlerLib/Xcode5SecPeiCpuExceptionHandlerLib.inf index 743c2aa76684..336c9e23b453 100644 --- a/UefiCpuPkg/Library/CpuExceptionHandlerLib/Xcode5SecPeiCpuExceptionHandlerLib.inf +++ b/UefiCpuPkg/Library/CpuExceptionHandlerLib/Xcode5SecPeiCpuExceptionHandlerLib.inf @@ -54,3 +54,4 @@ LocalApicLib PeCoffGetEntryPointLib VmgExitLib + VmTdExitLib diff --git a/UefiCpuPkg/Library/MpInitLib/DxeMpInitLib.inf b/UefiCpuPkg/Library/MpInitLib/DxeMpInitLib.inf index 1771575c69c1..6d514aca5b72 100644 --- a/UefiCpuPkg/Library/MpInitLib/DxeMpInitLib.inf +++ b/UefiCpuPkg/Library/MpInitLib/DxeMpInitLib.inf @@ -34,6 +34,7 @@ MpLib.c MpLib.h Microcode.c + DxeMpLibTdx.c [Packages] MdePkg/MdePkg.dec @@ -53,6 +54,7 @@ SynchronizationLib PcdLib VmgExitLib + TdxProbeLib [Protocols] gEfiTimerArchProtocolGuid ## SOMETIMES_CONSUMES diff --git a/UefiCpuPkg/Library/MpInitLib/DxeMpLib.c b/UefiCpuPkg/Library/MpInitLib/DxeMpLib.c index 7839c249760e..0597707293e9 100644 --- a/UefiCpuPkg/Library/MpInitLib/DxeMpLib.c +++ b/UefiCpuPkg/Library/MpInitLib/DxeMpLib.c @@ -15,7 +15,7 @@ #include #include #include - +#include #include #define AP_SAFE_STACK_SIZE 128 @@ -784,6 +784,10 @@ MpInitLibStartupThisAP ( { EFI_STATUS Status; + if(ProbeTdGuest()) { + return EFI_UNSUPPORTED; + } + // // temporarily stop checkAllApsStatus for avoid resource dead-lock. // @@ -840,6 +844,10 @@ MpInitLibSwitchBSP ( EFI_TIMER_ARCH_PROTOCOL *Timer; UINT64 TimerPeriod; + if(ProbeTdGuest()) { + return EFI_UNSUPPORTED; + } + TimerPeriod = 0; // // Locate Timer Arch Protocol @@ -913,6 +921,10 @@ MpInitLibEnableDisableAP ( EFI_STATUS Status; BOOLEAN TempStopCheckState; + if(ProbeTdGuest()) { + return EFI_UNSUPPORTED; + } + TempStopCheckState = FALSE; // // temporarily stop checkAllAPsStatus for initialize parameters. diff --git a/UefiCpuPkg/Library/MpInitLib/DxeMpLibTdx.c b/UefiCpuPkg/Library/MpInitLib/DxeMpLibTdx.c new file mode 100644 index 000000000000..2cd1f5620a06 --- /dev/null +++ b/UefiCpuPkg/Library/MpInitLib/DxeMpLibTdx.c @@ -0,0 +1,124 @@ +/** @file + CPU MP Initialize Library common functions. + + Copyright (c) 2016 - 2020, Intel Corporation. All rights reserved.
+ Copyright (c) 2020, AMD Inc. All rights reserved.
+ + SPDX-License-Identifier: BSD-2-Clause-Patent + +**/ + +#include "MpLib.h" + +#include +#include +#include + + +/** + Gets detailed MP-related information on the requested processor at the + instant this call is made. This service may only be called from the BSP. + + @param[in] ProcessorNumber The handle number of processor. + @param[out] ProcessorInfoBuffer A pointer to the buffer where information for + the requested processor is deposited. + @param[out] HealthData Return processor health data. + + @retval EFI_SUCCESS Processor information was returned. + @retval EFI_DEVICE_ERROR The calling processor is an AP. + @retval EFI_INVALID_PARAMETER ProcessorInfoBuffer is NULL. + @retval EFI_NOT_FOUND The processor with the handle specified by + ProcessorNumber does not exist in the platform. + @retval EFI_NOT_READY MP Initialize Library is not initialized. + +**/ +EFI_STATUS +EFIAPI +TdxMpInitLibGetProcessorInfo ( + IN UINTN ProcessorNumber, + OUT EFI_PROCESSOR_INFORMATION *ProcessorInfoBuffer, + OUT EFI_HEALTH_FLAGS *HealthData OPTIONAL + ) +{ + EFI_STATUS Status; + TD_RETURN_DATA TdReturnData; + + if (ProcessorInfoBuffer == NULL) { + return EFI_INVALID_PARAMETER; + } + + Status = TdCall(TDCALL_TDINFO, 0, 0, 0, &TdReturnData); + ASSERT(Status == EFI_SUCCESS); + + if (ProcessorNumber >= TdReturnData.TdInfo.NumVcpus) { + return EFI_NOT_FOUND; + } + + ProcessorInfoBuffer->StatusFlag = 0; + if (ProcessorNumber == 0) { + ProcessorInfoBuffer->StatusFlag |= PROCESSOR_AS_BSP_BIT; + } + ProcessorInfoBuffer->StatusFlag |= PROCESSOR_ENABLED_BIT; + + // + // Get processor location information + // + GetProcessorLocationByApicId ( + (UINT32)ProcessorNumber, + &ProcessorInfoBuffer->Location.Package, + &ProcessorInfoBuffer->Location.Core, + &ProcessorInfoBuffer->Location.Thread + ); + + if (HealthData != NULL) { + HealthData->Uint32 = 0; + } + + return Status; +} +/** + Retrieves the number of logical processor in the platform and the number of + those logical processors that are enabled on this boot. This service may only + be called from the BSP. + + @param[out] NumberOfProcessors Pointer to the total number of logical + processors in the system, including the BSP + and disabled APs. + @param[out] NumberOfEnabledProcessors Pointer to the number of enabled logical + processors that exist in system, including + the BSP. + + @retval EFI_SUCCESS The number of logical processors and enabled + logical processors was retrieved. + @retval EFI_DEVICE_ERROR The calling processor is an AP. + @retval EFI_INVALID_PARAMETER NumberOfProcessors is NULL and NumberOfEnabledProcessors + is NULL. + @retval EFI_NOT_READY MP Initialize Library is not initialized. + +**/ +EFI_STATUS +EFIAPI +TdxMpInitLibGetNumberOfProcessors ( + OUT UINTN *NumberOfProcessors, OPTIONAL + OUT UINTN *NumberOfEnabledProcessors OPTIONAL + ) +{ + EFI_STATUS Status; + TD_RETURN_DATA TdReturnData; + + if ((NumberOfProcessors == NULL) && (NumberOfEnabledProcessors == NULL)) { + return EFI_INVALID_PARAMETER; + } + + Status = TdCall(TDCALL_TDINFO, 0, 0, 0, &TdReturnData); + ASSERT(Status == EFI_SUCCESS); + + if (NumberOfProcessors != NULL) { + *NumberOfProcessors = TdReturnData.TdInfo.NumVcpus; + } + if (NumberOfEnabledProcessors != NULL) { + *NumberOfEnabledProcessors = TdReturnData.TdInfo.MaxVcpus; + } + + return Status; +} diff --git a/UefiCpuPkg/Library/MpInitLib/MpLib.c b/UefiCpuPkg/Library/MpInitLib/MpLib.c index 681fa79b4cff..bf06e6d8d25d 100644 --- a/UefiCpuPkg/Library/MpInitLib/MpLib.c +++ b/UefiCpuPkg/Library/MpInitLib/MpLib.c @@ -12,6 +12,7 @@ #include #include #include +#include EFI_GUID mCpuInitMpLibHobGuid = CPU_INIT_MP_LIB_HOB_GUID; @@ -1950,6 +1951,10 @@ MpInitLibInitialize ( UINTN BackupBufferAddr; UINTN ApIdtBase; + if(ProbeTdGuest()) { + return EFI_SUCCESS; + } + OldCpuMpData = GetCpuMpDataFromGuidedHob (); if (OldCpuMpData == NULL) { MaxLogicalProcessorNumber = PcdGet32(PcdCpuMaxLogicalProcessorNumber); @@ -2175,6 +2180,10 @@ MpInitLibGetProcessorInfo ( CPU_INFO_IN_HOB *CpuInfoInHob; UINTN OriginalProcessorNumber; + if(ProbeTdGuest()) { + return TdxMpInitLibGetProcessorInfo(ProcessorNumber, ProcessorInfoBuffer, HealthData); + } + CpuMpData = GetCpuMpData (); CpuInfoInHob = (CPU_INFO_IN_HOB *) (UINTN) CpuMpData->CpuInfoInHob; @@ -2406,6 +2415,10 @@ EnableDisableApWorker ( CPU_MP_DATA *CpuMpData; UINTN CallerNumber; + if(ProbeTdGuest()) { + return EFI_UNSUPPORTED; + } + CpuMpData = GetCpuMpData (); // @@ -2466,6 +2479,11 @@ MpInitLibWhoAmI ( return EFI_INVALID_PARAMETER; } + if(ProbeTdGuest()) { + *ProcessorNumber = 0; + return EFI_SUCCESS; + } + CpuMpData = GetCpuMpData (); return GetProcessorNumber (CpuMpData, ProcessorNumber); @@ -2504,6 +2522,10 @@ MpInitLibGetNumberOfProcessors ( UINTN EnabledProcessorNumber; UINTN Index; + if(ProbeTdGuest()) { + return TdxMpInitLibGetNumberOfProcessors(NumberOfProcessors, NumberOfEnabledProcessors); + } + CpuMpData = GetCpuMpData (); if ((NumberOfProcessors == NULL) && (NumberOfEnabledProcessors == NULL)) { @@ -2589,6 +2611,10 @@ StartupAllCPUsWorker ( BOOLEAN HasEnabledAp; CPU_STATE ApState; + if(ProbeTdGuest()) { + return EFI_SUCCESS; + } + CpuMpData = GetCpuMpData (); if (FailedCpuList != NULL) { diff --git a/UefiCpuPkg/Library/MpInitLib/MpLib.h b/UefiCpuPkg/Library/MpInitLib/MpLib.h index 02652eaae126..0bf309423f36 100644 --- a/UefiCpuPkg/Library/MpInitLib/MpLib.h +++ b/UefiCpuPkg/Library/MpInitLib/MpLib.h @@ -740,5 +740,58 @@ PlatformShadowMicrocode ( IN OUT CPU_MP_DATA *CpuMpData ); +/** + Gets detailed MP-related information on the requested processor at the + instant this call is made. This service may only be called from the BSP. + + @param[in] ProcessorNumber The handle number of processor. + @param[out] ProcessorInfoBuffer A pointer to the buffer where information for + the requested processor is deposited. + @param[out] HealthData Return processor health data. + + @retval EFI_SUCCESS Processor information was returned. + @retval EFI_DEVICE_ERROR The calling processor is an AP. + @retval EFI_INVALID_PARAMETER ProcessorInfoBuffer is NULL. + @retval EFI_NOT_FOUND The processor with the handle specified by + ProcessorNumber does not exist in the platform. + @retval EFI_NOT_READY MP Initialize Library is not initialized. + +**/ +EFI_STATUS +EFIAPI +TdxMpInitLibGetProcessorInfo ( + IN UINTN ProcessorNumber, + OUT EFI_PROCESSOR_INFORMATION *ProcessorInfoBuffer, + OUT EFI_HEALTH_FLAGS *HealthData OPTIONAL + ); + +/** + Retrieves the number of logical processor in the platform and the number of + those logical processors that are enabled on this boot. This service may only + be called from the BSP. + + @param[out] NumberOfProcessors Pointer to the total number of logical + processors in the system, including the BSP + and disabled APs. + @param[out] NumberOfEnabledProcessors Pointer to the number of enabled logical + processors that exist in system, including + the BSP. + + @retval EFI_SUCCESS The number of logical processors and enabled + logical processors was retrieved. + @retval EFI_DEVICE_ERROR The calling processor is an AP. + @retval EFI_INVALID_PARAMETER NumberOfProcessors is NULL and NumberOfEnabledProcessors + is NULL. + @retval EFI_NOT_READY MP Initialize Library is not initialized. + +**/ +EFI_STATUS +EFIAPI +TdxMpInitLibGetNumberOfProcessors ( + OUT UINTN *NumberOfProcessors, OPTIONAL + OUT UINTN *NumberOfEnabledProcessors OPTIONAL + ); + + #endif diff --git a/UefiCpuPkg/Library/MpInitLib/PeiMpInitLib.inf b/UefiCpuPkg/Library/MpInitLib/PeiMpInitLib.inf index 34abf25d43cd..bcbe186e54f1 100644 --- a/UefiCpuPkg/Library/MpInitLib/PeiMpInitLib.inf +++ b/UefiCpuPkg/Library/MpInitLib/PeiMpInitLib.inf @@ -34,6 +34,7 @@ MpLib.c MpLib.h Microcode.c + PeiMpLibTdx.c [Packages] MdePkg/MdePkg.dec @@ -52,6 +53,7 @@ PeiServicesLib PcdLib VmgExitLib + TdxProbeLib [Pcd] gUefiCpuPkgTokenSpaceGuid.PcdCpuMaxLogicalProcessorNumber ## CONSUMES diff --git a/UefiCpuPkg/Library/MpInitLib/PeiMpLibTdx.c b/UefiCpuPkg/Library/MpInitLib/PeiMpLibTdx.c new file mode 100644 index 000000000000..a7d4f6d21984 --- /dev/null +++ b/UefiCpuPkg/Library/MpInitLib/PeiMpLibTdx.c @@ -0,0 +1,72 @@ +/** @file + CPU MP Initialize Library common functions. + + Copyright (c) 2016 - 2020, Intel Corporation. All rights reserved.
+ Copyright (c) 2020, AMD Inc. All rights reserved.
+ + SPDX-License-Identifier: BSD-2-Clause-Patent + +**/ + +#include "MpLib.h" + + +/** + Gets detailed MP-related information on the requested processor at the + instant this call is made. This service may only be called from the BSP. + + @param[in] ProcessorNumber The handle number of processor. + @param[out] ProcessorInfoBuffer A pointer to the buffer where information for + the requested processor is deposited. + @param[out] HealthData Return processor health data. + + @retval EFI_SUCCESS Processor information was returned. + @retval EFI_DEVICE_ERROR The calling processor is an AP. + @retval EFI_INVALID_PARAMETER ProcessorInfoBuffer is NULL. + @retval EFI_NOT_FOUND The processor with the handle specified by + ProcessorNumber does not exist in the platform. + @retval EFI_NOT_READY MP Initialize Library is not initialized. + +**/ +EFI_STATUS +EFIAPI +TdxMpInitLibGetProcessorInfo ( + IN UINTN ProcessorNumber, + OUT EFI_PROCESSOR_INFORMATION *ProcessorInfoBuffer, + OUT EFI_HEALTH_FLAGS *HealthData OPTIONAL + ) +{ + ASSERT(FALSE); + + return EFI_UNSUPPORTED; +} +/** + Retrieves the number of logical processor in the platform and the number of + those logical processors that are enabled on this boot. This service may only + be called from the BSP. + + @param[out] NumberOfProcessors Pointer to the total number of logical + processors in the system, including the BSP + and disabled APs. + @param[out] NumberOfEnabledProcessors Pointer to the number of enabled logical + processors that exist in system, including + the BSP. + + @retval EFI_SUCCESS The number of logical processors and enabled + logical processors was retrieved. + @retval EFI_DEVICE_ERROR The calling processor is an AP. + @retval EFI_INVALID_PARAMETER NumberOfProcessors is NULL and NumberOfEnabledProcessors + is NULL. + @retval EFI_NOT_READY MP Initialize Library is not initialized. + +**/ +EFI_STATUS +EFIAPI +TdxMpInitLibGetNumberOfProcessors ( + OUT UINTN *NumberOfProcessors, OPTIONAL + OUT UINTN *NumberOfEnabledProcessors OPTIONAL + ) +{ + ASSERT(FALSE); + return EFI_UNSUPPORTED; +} diff --git a/UefiCpuPkg/Library/VmTdExitLibNull/VmTdExitLibNull.c b/UefiCpuPkg/Library/VmTdExitLibNull/VmTdExitLibNull.c new file mode 100644 index 000000000000..08868c24d230 --- /dev/null +++ b/UefiCpuPkg/Library/VmTdExitLibNull/VmTdExitLibNull.c @@ -0,0 +1,30 @@ +#include +#include + +/** + Handle a #VE exception. + + Performs the necessary processing to handle a #VE exception. + + @param[in, out] ExceptionType Pointer to an EFI_EXCEPTION_TYPE to be set + as value to use on error. + @param[in, out] SystemContext Pointer to EFI_SYSTEM_CONTEXT + + @retval EFI_SUCCESS Exception handled + @retval EFI_UNSUPPORTED #VE not supported, (new) exception value to + propagate provided + @retval EFI_PROTOCOL_ERROR #VE handling failed, (new) exception value to + propagate provided + +**/ +EFI_STATUS +EFIAPI +VmTdExitHandleVe ( + IN OUT EFI_EXCEPTION_TYPE *ExceptionType, + IN OUT EFI_SYSTEM_CONTEXT SystemContext + ) +{ + *ExceptionType = VE_EXCEPTION; + + return EFI_UNSUPPORTED; +} diff --git a/UefiCpuPkg/Library/VmTdExitLibNull/VmTdExitLibNull.inf b/UefiCpuPkg/Library/VmTdExitLibNull/VmTdExitLibNull.inf new file mode 100644 index 000000000000..e3357de2a6a9 --- /dev/null +++ b/UefiCpuPkg/Library/VmTdExitLibNull/VmTdExitLibNull.inf @@ -0,0 +1,34 @@ +## @file +# VMTDEXIT Support Library. +# +# Copyright (c) 2020, Intel Inc. All rights reserved.
+# Copyright (C) 2020, Advanced Micro Devices, Inc. All rights reserved.
+# SPDX-License-Identifier: BSD-2-Clause-Patent +# +## + +[Defines] + INF_VERSION = 0x00010005 + BASE_NAME = VmTdExitLibNull + FILE_GUID = b29eabb0-f9a3-11ea-8b6e-0800200c9a66 + MODULE_TYPE = BASE + VERSION_STRING = 1.0 + LIBRARY_CLASS = VmTdExitLib + +# +# The following information is for reference only and not required by the build tools. +# +# VALID_ARCHITECTURES = X64 IA32 +# + +[Sources.common] + VmTdExitLibNull.c + +[Packages] + MdePkg/MdePkg.dec + UefiCpuPkg/UefiCpuPkg.dec + +[LibraryClasses] + BaseLib + BaseMemoryLib + DebugLib diff --git a/UefiCpuPkg/ResetVector/Vtf0/CommonMacros.inc b/UefiCpuPkg/ResetVector/Vtf0/CommonMacros.inc index 7deee8b6ad5d..ef31f29edb31 100644 --- a/UefiCpuPkg/ResetVector/Vtf0/CommonMacros.inc +++ b/UefiCpuPkg/ResetVector/Vtf0/CommonMacros.inc @@ -19,6 +19,35 @@ jmp %1 %+ OneTimerCallReturn %endmacro +%define PAGE_PRESENT 0x01 +%define PAGE_READ_WRITE 0x02 +%define PAGE_USER_SUPERVISOR 0x04 +%define PAGE_WRITE_THROUGH 0x08 +%define PAGE_CACHE_DISABLE 0x010 +%define PAGE_ACCESSED 0x020 +%define PAGE_DIRTY 0x040 +%define PAGE_PAT 0x080 +%define PAGE_GLOBAL 0x0100 +%define PAGE_2M_MBO 0x080 +%define PAGE_2M_PAT 0x01000 + +%define PAGE_4K_PDE_ATTR (PAGE_ACCESSED + \ + PAGE_DIRTY + \ + PAGE_READ_WRITE + \ + PAGE_PRESENT) + +%define PAGE_2M_PDE_ATTR (PAGE_2M_MBO + \ + PAGE_ACCESSED + \ + PAGE_DIRTY + \ + PAGE_READ_WRITE + \ + PAGE_PRESENT) + +%define PAGE_PDP_ATTR (PAGE_ACCESSED + \ + PAGE_READ_WRITE + \ + PAGE_PRESENT) + +%define PT_ADDR(Base,Offset) ((Base) + (Offset)) + StartOfResetVectorCode: %define ADDR_OF_START_OF_RESET_CODE ADDR_OF(StartOfResetVectorCode) diff --git a/UefiCpuPkg/ResetVector/Vtf0/Ia16/Init16.asm b/UefiCpuPkg/ResetVector/Vtf0/Ia16/Init16.asm index cbdadee166a0..34c741827687 100644 --- a/UefiCpuPkg/ResetVector/Vtf0/Ia16/Init16.asm +++ b/UefiCpuPkg/ResetVector/Vtf0/Ia16/Init16.asm @@ -40,3 +40,8 @@ EarlyInit16: OneTimeCallRet EarlyInit16 +; +; Protected Mode entry point for Early BSP +; +EarlyBspPmEntry: + jmp MainTd diff --git a/UefiCpuPkg/ResetVector/Vtf0/Ia16/Real16ToFlat32.asm b/UefiCpuPkg/ResetVector/Vtf0/Ia16/Real16ToFlat32.asm index 0e79a3984b16..8b03f7087a77 100644 --- a/UefiCpuPkg/ResetVector/Vtf0/Ia16/Real16ToFlat32.asm +++ b/UefiCpuPkg/ResetVector/Vtf0/Ia16/Real16ToFlat32.asm @@ -119,6 +119,35 @@ LINEAR_CODE_SEL equ $-GDT_BASE DB 0 ; base 31:24 %ifdef ARCH_X64 +NULL_SEL1 equ $-GDT_BASE + DW 0 ; limit 15:0 + DW 0 ; base 15:0 + DB 0 ; base 23:16 + DB 0 ; sys flag, dpl, type + DB 0 ; limit 19:16, flags + DB 0 ; base 31:24 +NULL_SEL2 equ $-GDT_BASE + DW 0 ; limit 15:0 + DW 0 ; base 15:0 + DB 0 ; base 23:16 + DB 0 ; sys flag, dpl, type + DB 0 ; limit 19:16, flags + DB 0 ; base 31:24 +NULL_SEL3 equ $-GDT_BASE + DW 0 ; limit 15:0 + DW 0 ; base 15:0 + DB 0 ; base 23:16 + DB 0 ; sys flag, dpl, type + DB 0 ; limit 19:16, flags + DB 0 ; base 31:24 +NULL_SEL4 equ $-GDT_BASE + DW 0 ; limit 15:0 + DW 0 ; base 15:0 + DB 0 ; base 23:16 + DB 0 ; sys flag, dpl, type + DB 0 ; limit 19:16, flags + DB 0 ; base 31:24 + ; linear code (64-bit) segment descriptor LINEAR_CODE64_SEL equ $-GDT_BASE DW 0xffff ; limit 15:0 diff --git a/UefiCpuPkg/ResetVector/Vtf0/Main.asm b/UefiCpuPkg/ResetVector/Vtf0/Main.asm index 19d08482f831..f69c6d2dc6ef 100644 --- a/UefiCpuPkg/ResetVector/Vtf0/Main.asm +++ b/UefiCpuPkg/ResetVector/Vtf0/Main.asm @@ -35,7 +35,61 @@ Main16: OneTimeCall TransitionFromReal16To32BitFlat BITS 32 + jmp SearchBfv +; +; Modified: EBX, ECX, EDX, EBP, EDI, ESP +; +; @param[in,out] RAX/EAX 0 +; @param[in] RFLAGS 2 +; @param[in] RCX [31:0] TDINITVP - Untrusted Configuration +; [63:32] 0 +; @param[in] RDX [31:0] VCPUID +; [63:32] 0 +; @param[in] RBX [6:0] CPU supported GPA width +; [7:7] 5 level page table support +; [63:8] 0 +; @param[in] RSI [31:0] VCPU_Index +; [63:32] 0 +; @param[in] RDI/EDI 0 +; @param[in] RBP/EBP 0 +; @param[in] R8 Same as RCX +; @param[out] RBP/EBP Address of Boot Firmware Volume (BFV) +; @param[out] DS Selector allowing flat access to all addresses +; @param[out] ES Selector allowing flat access to all addresses +; @param[out] FS Selector allowing flat access to all addresses +; @param[out] GS Selector allowing flat access to all addresses +; @param[out] SS Selector allowing flat access to all addresses +; +; @return None This routine jumps to SEC and does not return + +MainTd: +%ifdef ARCH_X64 + ; + ; Save EBX in EBP because EBX will be changed in ReloadFlat32 + ; + mov ebp, ebx + OneTimeCall ReloadFlat32 + + ; + ; It is of Tdx Guest + ; Save the Tdx info in TDX_WORK_AREA to pass to SEC phase + ; + mov byte[TDX_WORK_AREA], 1 + + ; check 5-level paging support + and ebp, 0x3f + cmp ebp, 52 + jl NotPageLevel5 + mov byte[TDX_WORK_AREA_PAGELEVEL5], 1 + +NotPageLevel5: + mov DWORD[TDX_WORK_AREA_INITVP], ecx + mov DWORD[TDX_WORK_AREA_INFO], ebp + +%endif + +SearchBfv: ; ; Search for the Boot Firmware Volume (BFV) ;