--- /dev/null
+/** @file\r
+ Initialize Intel TDX support.\r
+\r
+ Copyright (c) 2021, Intel Corporation. All rights reserved.<BR>\r
+\r
+ SPDX-License-Identifier: BSD-2-Clause-Patent\r
+\r
+**/\r
+\r
+#include <PiPei.h>\r
+#include <Library/BaseLib.h>\r
+#include <Library/DebugLib.h>\r
+#include <Library/HobLib.h>\r
+#include <Library/BaseMemoryLib.h>\r
+#include <Library/MemoryAllocationLib.h>\r
+#include <IndustryStandard/Tdx.h>\r
+#include <IndustryStandard/IntelTdx.h>\r
+#include <IndustryStandard/QemuFwCfg.h>\r
+#include <Library/QemuFwCfgLib.h>\r
+#include <Library/PeiServicesLib.h>\r
+#include <Library/TdxLib.h>\r
+#include <Library/SynchronizationLib.h>\r
+#include <WorkArea.h>\r
+#include <ConfidentialComputingGuestAttr.h>\r
+\r
+#define ALIGNED_2MB_MASK 0x1fffff\r
+#define EFI_RESOURCE_MEMORY_UNACCEPTED 7\r
+\r
+/**\r
+ This function will be called to accept pages. Only BSP accepts pages.\r
+\r
+ TDCALL(ACCEPT_PAGE) supports the accept page size of 4k and 2M. To\r
+ simplify the implementation, the Memory to be accpeted is splitted\r
+ into 3 parts:\r
+ ----------------- <-- StartAddress1 (not 2M aligned)\r
+ | part 1 | Length1 < 2M\r
+ |---------------| <-- StartAddress2 (2M aligned)\r
+ | | Length2 = Integer multiples of 2M\r
+ | part 2 |\r
+ | |\r
+ |---------------| <-- StartAddress3\r
+ | part 3 | Length3 < 2M\r
+ |---------------|\r
+\r
+ @param[in] PhysicalAddress Start physical adress\r
+ @param[in] PhysicalEnd End physical address\r
+\r
+ @retval EFI_SUCCESS Accept memory successfully\r
+ @retval Others Other errors as indicated\r
+**/\r
+EFI_STATUS\r
+EFIAPI\r
+BspAcceptMemoryResourceRange (\r
+ IN EFI_PHYSICAL_ADDRESS PhysicalAddress,\r
+ IN EFI_PHYSICAL_ADDRESS PhysicalEnd\r
+ )\r
+{\r
+ EFI_STATUS Status;\r
+ UINT32 AcceptPageSize;\r
+ UINT64 StartAddress1;\r
+ UINT64 StartAddress2;\r
+ UINT64 StartAddress3;\r
+ UINT64 TotalLength;\r
+ UINT64 Length1;\r
+ UINT64 Length2;\r
+ UINT64 Length3;\r
+ UINT64 Pages;\r
+\r
+ AcceptPageSize = FixedPcdGet32 (PcdTdxAcceptPageSize);\r
+ TotalLength = PhysicalEnd - PhysicalAddress;\r
+ StartAddress1 = 0;\r
+ StartAddress2 = 0;\r
+ StartAddress3 = 0;\r
+ Length1 = 0;\r
+ Length2 = 0;\r
+ Length3 = 0;\r
+\r
+ if (TotalLength == 0) {\r
+ return EFI_SUCCESS;\r
+ }\r
+\r
+ DEBUG ((DEBUG_INFO, "TdAccept: 0x%llx - 0x%llx\n", PhysicalAddress, TotalLength));\r
+\r
+ if (ALIGN_VALUE (PhysicalAddress, SIZE_2MB) != PhysicalAddress) {\r
+ StartAddress1 = PhysicalAddress;\r
+ Length1 = ALIGN_VALUE (PhysicalAddress, SIZE_2MB) - PhysicalAddress;\r
+ if (Length1 >= TotalLength) {\r
+ Length1 = TotalLength;\r
+ }\r
+\r
+ PhysicalAddress += Length1;\r
+ TotalLength -= Length1;\r
+ }\r
+\r
+ if (TotalLength > SIZE_2MB) {\r
+ StartAddress2 = PhysicalAddress;\r
+ Length2 = TotalLength & ~(UINT64)ALIGNED_2MB_MASK;\r
+ PhysicalAddress += Length2;\r
+ TotalLength -= Length2;\r
+ }\r
+\r
+ if (TotalLength) {\r
+ StartAddress3 = PhysicalAddress;\r
+ Length3 = TotalLength;\r
+ }\r
+\r
+ DEBUG ((DEBUG_INFO, " Part1: 0x%llx - 0x%llx\n", StartAddress1, Length1));\r
+ DEBUG ((DEBUG_INFO, " Part2: 0x%llx - 0x%llx\n", StartAddress2, Length2));\r
+ DEBUG ((DEBUG_INFO, " Part3: 0x%llx - 0x%llx\n", StartAddress3, Length3));\r
+ DEBUG ((DEBUG_INFO, " Page : 0x%x\n", AcceptPageSize));\r
+\r
+ Status = EFI_SUCCESS;\r
+ if (Length1 > 0) {\r
+ Pages = Length1 / SIZE_4KB;\r
+ Status = TdAcceptPages (StartAddress1, Pages, SIZE_4KB);\r
+ if (EFI_ERROR (Status)) {\r
+ return Status;\r
+ }\r
+ }\r
+\r
+ if (Length2 > 0) {\r
+ Pages = Length2 / AcceptPageSize;\r
+ Status = TdAcceptPages (StartAddress2, Pages, AcceptPageSize);\r
+ if (EFI_ERROR (Status)) {\r
+ return Status;\r
+ }\r
+ }\r
+\r
+ if (Length3 > 0) {\r
+ Pages = Length3 / SIZE_4KB;\r
+ Status = TdAcceptPages (StartAddress3, Pages, SIZE_4KB);\r
+ ASSERT (!EFI_ERROR (Status));\r
+ if (EFI_ERROR (Status)) {\r
+ return Status;\r
+ }\r
+ }\r
+\r
+ return Status;\r
+}\r
+\r
+/**\r
+ Check the value whether in the valid list.\r
+\r
+ @param[in] Value A value\r
+ @param[in] ValidList A pointer to valid list\r
+ @param[in] ValidListLength Length of valid list\r
+\r
+ @retval TRUE The value is in valid list.\r
+ @retval FALSE The value is not in valid list.\r
+\r
+**/\r
+BOOLEAN\r
+EFIAPI\r
+IsInValidList (\r
+ IN UINT32 Value,\r
+ IN UINT32 *ValidList,\r
+ IN UINT32 ValidListLength\r
+ )\r
+{\r
+ UINT32 index;\r
+\r
+ if (ValidList == NULL) {\r
+ return FALSE;\r
+ }\r
+\r
+ for (index = 0; index < ValidListLength; index++) {\r
+ if (ValidList[index] == Value) {\r
+ return TRUE;\r
+ }\r
+ }\r
+\r
+ return FALSE;\r
+}\r
+\r
+/**\r
+ Check the integrity of VMM Hob List.\r
+\r
+ @param[in] VmmHobList A pointer to Hob List\r
+\r
+ @retval TRUE The Hob List is valid.\r
+ @retval FALSE The Hob List is invalid.\r
+\r
+**/\r
+BOOLEAN\r
+EFIAPI\r
+ValidateHobList (\r
+ IN CONST VOID *VmmHobList\r
+ )\r
+{\r
+ EFI_PEI_HOB_POINTERS Hob;\r
+ UINT32 EFI_BOOT_MODE_LIST[] = {\r
+ BOOT_WITH_FULL_CONFIGURATION,\r
+ BOOT_WITH_MINIMAL_CONFIGURATION,\r
+ BOOT_ASSUMING_NO_CONFIGURATION_CHANGES,\r
+ BOOT_WITH_FULL_CONFIGURATION_PLUS_DIAGNOSTICS,\r
+ BOOT_WITH_DEFAULT_SETTINGS,\r
+ BOOT_ON_S4_RESUME,\r
+ BOOT_ON_S5_RESUME,\r
+ BOOT_WITH_MFG_MODE_SETTINGS,\r
+ BOOT_ON_S2_RESUME,\r
+ BOOT_ON_S3_RESUME,\r
+ BOOT_ON_FLASH_UPDATE,\r
+ BOOT_IN_RECOVERY_MODE\r
+ };\r
+\r
+ UINT32 EFI_RESOURCE_TYPE_LIST[] = {\r
+ EFI_RESOURCE_SYSTEM_MEMORY,\r
+ EFI_RESOURCE_MEMORY_MAPPED_IO,\r
+ EFI_RESOURCE_IO,\r
+ EFI_RESOURCE_FIRMWARE_DEVICE,\r
+ EFI_RESOURCE_MEMORY_MAPPED_IO_PORT,\r
+ EFI_RESOURCE_MEMORY_RESERVED,\r
+ EFI_RESOURCE_IO_RESERVED,\r
+ EFI_RESOURCE_MEMORY_UNACCEPTED\r
+ };\r
+\r
+ if (VmmHobList == NULL) {\r
+ DEBUG ((DEBUG_ERROR, "HOB: HOB data pointer is NULL\n"));\r
+ return FALSE;\r
+ }\r
+\r
+ Hob.Raw = (UINT8 *)VmmHobList;\r
+\r
+ //\r
+ // Parse the HOB list until end of list or matching type is found.\r
+ //\r
+ while (!END_OF_HOB_LIST (Hob)) {\r
+ if (Hob.Header->Reserved != (UINT32)0) {\r
+ DEBUG ((DEBUG_ERROR, "HOB: Hob header Reserved filed should be zero\n"));\r
+ return FALSE;\r
+ }\r
+\r
+ if (Hob.Header->HobLength == 0) {\r
+ DEBUG ((DEBUG_ERROR, "HOB: Hob header LEANGTH should not be zero\n"));\r
+ return FALSE;\r
+ }\r
+\r
+ switch (Hob.Header->HobType) {\r
+ case EFI_HOB_TYPE_HANDOFF:\r
+ if (Hob.Header->HobLength != sizeof (EFI_HOB_HANDOFF_INFO_TABLE)) {\r
+ DEBUG ((DEBUG_ERROR, "HOB: Hob length is not equal corresponding hob structure. Type: 0x%04x\n", EFI_HOB_TYPE_HANDOFF));\r
+ return FALSE;\r
+ }\r
+\r
+ if (IsInValidList (Hob.HandoffInformationTable->BootMode, EFI_BOOT_MODE_LIST, ARRAY_SIZE (EFI_BOOT_MODE_LIST)) == FALSE) {\r
+ DEBUG ((DEBUG_ERROR, "HOB: Unknow HandoffInformationTable BootMode type. Type: 0x%08x\n", Hob.HandoffInformationTable->BootMode));\r
+ return FALSE;\r
+ }\r
+\r
+ if ((Hob.HandoffInformationTable->EfiFreeMemoryTop % 4096) != 0) {\r
+ DEBUG ((DEBUG_ERROR, "HOB: HandoffInformationTable EfiFreeMemoryTop address must be 4-KB aligned to meet page restrictions of UEFI.\\r
+ Address: 0x%016lx\n", Hob.HandoffInformationTable->EfiFreeMemoryTop));\r
+ return FALSE;\r
+ }\r
+\r
+ break;\r
+\r
+ case EFI_HOB_TYPE_RESOURCE_DESCRIPTOR:\r
+ if (Hob.Header->HobLength != sizeof (EFI_HOB_RESOURCE_DESCRIPTOR)) {\r
+ DEBUG ((DEBUG_ERROR, "HOB: Hob length is not equal corresponding hob structure. Type: 0x%04x\n", EFI_HOB_TYPE_RESOURCE_DESCRIPTOR));\r
+ return FALSE;\r
+ }\r
+\r
+ if (IsInValidList (Hob.ResourceDescriptor->ResourceType, EFI_RESOURCE_TYPE_LIST, ARRAY_SIZE (EFI_RESOURCE_TYPE_LIST)) == FALSE) {\r
+ DEBUG ((DEBUG_ERROR, "HOB: Unknow ResourceDescriptor ResourceType type. Type: 0x%08x\n", Hob.ResourceDescriptor->ResourceType));\r
+ return FALSE;\r
+ }\r
+\r
+ if ((Hob.ResourceDescriptor->ResourceAttribute & (~(EFI_RESOURCE_ATTRIBUTE_PRESENT |\r
+ EFI_RESOURCE_ATTRIBUTE_INITIALIZED |\r
+ EFI_RESOURCE_ATTRIBUTE_TESTED |\r
+ EFI_RESOURCE_ATTRIBUTE_READ_PROTECTED |\r
+ EFI_RESOURCE_ATTRIBUTE_WRITE_PROTECTED |\r
+ EFI_RESOURCE_ATTRIBUTE_EXECUTION_PROTECTED |\r
+ EFI_RESOURCE_ATTRIBUTE_PERSISTENT |\r
+ EFI_RESOURCE_ATTRIBUTE_SINGLE_BIT_ECC |\r
+ EFI_RESOURCE_ATTRIBUTE_MULTIPLE_BIT_ECC |\r
+ EFI_RESOURCE_ATTRIBUTE_ECC_RESERVED_1 |\r
+ EFI_RESOURCE_ATTRIBUTE_ECC_RESERVED_2 |\r
+ EFI_RESOURCE_ATTRIBUTE_UNCACHEABLE |\r
+ EFI_RESOURCE_ATTRIBUTE_WRITE_COMBINEABLE |\r
+ EFI_RESOURCE_ATTRIBUTE_WRITE_THROUGH_CACHEABLE |\r
+ EFI_RESOURCE_ATTRIBUTE_WRITE_BACK_CACHEABLE |\r
+ EFI_RESOURCE_ATTRIBUTE_16_BIT_IO |\r
+ EFI_RESOURCE_ATTRIBUTE_32_BIT_IO |\r
+ EFI_RESOURCE_ATTRIBUTE_64_BIT_IO |\r
+ EFI_RESOURCE_ATTRIBUTE_UNCACHED_EXPORTED |\r
+ EFI_RESOURCE_ATTRIBUTE_READ_PROTECTABLE |\r
+ EFI_RESOURCE_ATTRIBUTE_WRITE_PROTECTABLE |\r
+ EFI_RESOURCE_ATTRIBUTE_EXECUTION_PROTECTABLE |\r
+ EFI_RESOURCE_ATTRIBUTE_PERSISTABLE |\r
+ EFI_RESOURCE_ATTRIBUTE_READ_ONLY_PROTECTED |\r
+ EFI_RESOURCE_ATTRIBUTE_READ_ONLY_PROTECTABLE |\r
+ EFI_RESOURCE_ATTRIBUTE_MORE_RELIABLE))) != 0)\r
+ {\r
+ DEBUG ((DEBUG_ERROR, "HOB: Unknow ResourceDescriptor ResourceAttribute type. Type: 0x%08x\n", Hob.ResourceDescriptor->ResourceAttribute));\r
+ return FALSE;\r
+ }\r
+\r
+ break;\r
+\r
+ // EFI_HOB_GUID_TYPE is variable length data, so skip check\r
+ case EFI_HOB_TYPE_GUID_EXTENSION:\r
+ break;\r
+\r
+ case EFI_HOB_TYPE_FV:\r
+ if (Hob.Header->HobLength != sizeof (EFI_HOB_FIRMWARE_VOLUME)) {\r
+ DEBUG ((DEBUG_ERROR, "HOB: Hob length is not equal corresponding hob structure. Type: 0x%04x\n", EFI_HOB_TYPE_FV));\r
+ return FALSE;\r
+ }\r
+\r
+ break;\r
+\r
+ case EFI_HOB_TYPE_FV2:\r
+ if (Hob.Header->HobLength != sizeof (EFI_HOB_FIRMWARE_VOLUME2)) {\r
+ DEBUG ((DEBUG_ERROR, "HOB: Hob length is not equal corresponding hob structure. Type: 0x%04x\n", EFI_HOB_TYPE_FV2));\r
+ return FALSE;\r
+ }\r
+\r
+ break;\r
+\r
+ case EFI_HOB_TYPE_FV3:\r
+ if (Hob.Header->HobLength != sizeof (EFI_HOB_FIRMWARE_VOLUME3)) {\r
+ DEBUG ((DEBUG_ERROR, "HOB: Hob length is not equal corresponding hob structure. Type: 0x%04x\n", EFI_HOB_TYPE_FV3));\r
+ return FALSE;\r
+ }\r
+\r
+ break;\r
+\r
+ case EFI_HOB_TYPE_CPU:\r
+ if (Hob.Header->HobLength != sizeof (EFI_HOB_CPU)) {\r
+ DEBUG ((DEBUG_ERROR, "HOB: Hob length is not equal corresponding hob structure. Type: 0x%04x\n", EFI_HOB_TYPE_CPU));\r
+ return FALSE;\r
+ }\r
+\r
+ for (UINT32 index = 0; index < 6; index++) {\r
+ if (Hob.Cpu->Reserved[index] != 0) {\r
+ DEBUG ((DEBUG_ERROR, "HOB: Cpu Reserved field will always be set to zero.\n"));\r
+ return FALSE;\r
+ }\r
+ }\r
+\r
+ break;\r
+\r
+ default:\r
+ DEBUG ((DEBUG_ERROR, "HOB: Hob type is not know. Type: 0x%04x\n", Hob.Header->HobType));\r
+ return FALSE;\r
+ }\r
+\r
+ // Get next HOB\r
+ Hob.Raw = (UINT8 *)(Hob.Raw + Hob.Header->HobLength);\r
+ }\r
+\r
+ return TRUE;\r
+}\r
+\r
+/**\r
+ Processing the incoming HobList for the TDX\r
+\r
+ Firmware must parse list, and accept the pages of memory before their can be\r
+ use by the guest.\r
+\r
+ @param[in] VmmHobList The Hoblist pass the firmware\r
+\r
+ @retval EFI_SUCCESS Process the HobList successfully\r
+ @retval Others Other errors as indicated\r
+\r
+**/\r
+EFI_STATUS\r
+EFIAPI\r
+ProcessHobList (\r
+ IN CONST VOID *VmmHobList\r
+ )\r
+{\r
+ EFI_STATUS Status;\r
+ EFI_PEI_HOB_POINTERS Hob;\r
+ EFI_PHYSICAL_ADDRESS PhysicalEnd;\r
+\r
+ Status = EFI_SUCCESS;\r
+ ASSERT (VmmHobList != NULL);\r
+ Hob.Raw = (UINT8 *)VmmHobList;\r
+\r
+ //\r
+ // Parse the HOB list until end of list or matching type is found.\r
+ //\r
+ while (!END_OF_HOB_LIST (Hob)) {\r
+ if (Hob.Header->HobType == EFI_HOB_TYPE_RESOURCE_DESCRIPTOR) {\r
+ DEBUG ((DEBUG_INFO, "\nResourceType: 0x%x\n", Hob.ResourceDescriptor->ResourceType));\r
+\r
+ if (Hob.ResourceDescriptor->ResourceType == EFI_RESOURCE_MEMORY_UNACCEPTED) {\r
+ DEBUG ((DEBUG_INFO, "ResourceAttribute: 0x%x\n", Hob.ResourceDescriptor->ResourceAttribute));\r
+ DEBUG ((DEBUG_INFO, "PhysicalStart: 0x%llx\n", Hob.ResourceDescriptor->PhysicalStart));\r
+ DEBUG ((DEBUG_INFO, "ResourceLength: 0x%llx\n", Hob.ResourceDescriptor->ResourceLength));\r
+ DEBUG ((DEBUG_INFO, "Owner: %g\n\n", &Hob.ResourceDescriptor->Owner));\r
+\r
+ PhysicalEnd = Hob.ResourceDescriptor->PhysicalStart + Hob.ResourceDescriptor->ResourceLength;\r
+\r
+ Status = BspAcceptMemoryResourceRange (\r
+ Hob.ResourceDescriptor->PhysicalStart,\r
+ PhysicalEnd\r
+ );\r
+ if (EFI_ERROR (Status)) {\r
+ break;\r
+ }\r
+ }\r
+ }\r
+\r
+ Hob.Raw = GET_NEXT_HOB (Hob);\r
+ }\r
+\r
+ return Status;\r
+}\r
+\r
+/**\r
+ In Tdx guest, some information need to be passed from host VMM to guest\r
+ firmware. For example, the memory resource, etc. These information are\r
+ prepared by host VMM and put in HobList which is described in TdxMetadata.\r
+\r
+ Information in HobList is treated as external input. From the security\r
+ perspective before it is consumed, it should be validated.\r
+\r
+ @retval EFI_SUCCESS Successfully process the hoblist\r
+ @retval Others Other error as indicated\r
+**/\r
+EFI_STATUS\r
+EFIAPI\r
+ProcessTdxHobList (\r
+ VOID\r
+ )\r
+{\r
+ EFI_STATUS Status;\r
+ VOID *TdHob;\r
+ TD_RETURN_DATA TdReturnData;\r
+\r
+ TdHob = (VOID *)(UINTN)FixedPcdGet32 (PcdOvmfSecGhcbBase);\r
+ Status = TdCall (TDCALL_TDINFO, 0, 0, 0, &TdReturnData);\r
+ if (EFI_ERROR (Status)) {\r
+ return Status;\r
+ }\r
+\r
+ DEBUG ((\r
+ DEBUG_INFO,\r
+ "Intel Tdx Started with (GPAW: %d, Cpus: %d)\n",\r
+ TdReturnData.TdInfo.Gpaw,\r
+ TdReturnData.TdInfo.NumVcpus\r
+ ));\r
+\r
+ //\r
+ // Validate HobList\r
+ //\r
+ if (ValidateHobList (TdHob) == FALSE) {\r
+ return EFI_INVALID_PARAMETER;\r
+ }\r
+\r
+ //\r
+ // Process Hoblist to accept memory\r
+ //\r
+ Status = ProcessHobList (TdHob);\r
+\r
+ return Status;\r
+}\r
+\r
+/**\r
+ Transfer the incoming HobList for the TD to the final HobList for Dxe.\r
+ The Hobs transferred in this function are ResourceDescriptor hob and\r
+ MemoryAllocation hob.\r
+\r
+ @param[in] VmmHobList The Hoblist pass the firmware\r
+\r
+**/\r
+VOID\r
+EFIAPI\r
+TransferTdxHobList (\r
+ VOID\r
+ )\r
+{\r
+ EFI_PEI_HOB_POINTERS Hob;\r
+ EFI_RESOURCE_TYPE ResourceType;\r
+ EFI_RESOURCE_ATTRIBUTE_TYPE ResourceAttribute;\r
+\r
+ //\r
+ // PcdOvmfSecGhcbBase is used as the TD_HOB in Tdx guest.\r
+ //\r
+ Hob.Raw = (UINT8 *)(UINTN)FixedPcdGet32 (PcdOvmfSecGhcbBase);\r
+ while (!END_OF_HOB_LIST (Hob)) {\r
+ switch (Hob.Header->HobType) {\r
+ case EFI_HOB_TYPE_RESOURCE_DESCRIPTOR:\r
+ ResourceType = Hob.ResourceDescriptor->ResourceType;\r
+ ResourceAttribute = Hob.ResourceDescriptor->ResourceAttribute;\r
+\r
+ if (ResourceType == EFI_RESOURCE_MEMORY_UNACCEPTED) {\r
+ ResourceType = EFI_RESOURCE_SYSTEM_MEMORY;\r
+ ResourceAttribute |= (EFI_RESOURCE_ATTRIBUTE_PRESENT | EFI_RESOURCE_ATTRIBUTE_INITIALIZED | EFI_RESOURCE_ATTRIBUTE_TESTED);\r
+ }\r
+\r
+ BuildResourceDescriptorHob (\r
+ ResourceType,\r
+ ResourceAttribute,\r
+ Hob.ResourceDescriptor->PhysicalStart,\r
+ Hob.ResourceDescriptor->ResourceLength\r
+ );\r
+ break;\r
+ case EFI_HOB_TYPE_MEMORY_ALLOCATION:\r
+ BuildMemoryAllocationHob (\r
+ Hob.MemoryAllocation->AllocDescriptor.MemoryBaseAddress,\r
+ Hob.MemoryAllocation->AllocDescriptor.MemoryLength,\r
+ Hob.MemoryAllocation->AllocDescriptor.MemoryType\r
+ );\r
+ break;\r
+ }\r
+\r
+ Hob.Raw = GET_NEXT_HOB (Hob);\r
+ }\r
+}\r