+/** @file\r
+\r
+ Copyright (c) 2021, Intel Corporation. All rights reserved.<BR>\r
+ SPDX-License-Identifier: BSD-2-Clause-Patent\r
+\r
+**/\r
+\r
+#include "UefiPayloadEntry.h"\r
+\r
+#define MEMORY_ATTRIBUTE_MASK (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_READ_ONLY_PROTECTED | \\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_PERSISTENT )\r
+\r
+#define TESTED_MEMORY_ATTRIBUTES (EFI_RESOURCE_ATTRIBUTE_PRESENT | \\r
+ EFI_RESOURCE_ATTRIBUTE_INITIALIZED | \\r
+ EFI_RESOURCE_ATTRIBUTE_TESTED )\r
+\r
+extern VOID *mHobList;\r
+\r
+/**\r
+ Add HOB into HOB list\r
+\r
+ @param[in] Hob The HOB to be added into the HOB list.\r
+**/\r
+VOID\r
+AddNewHob (\r
+ IN EFI_PEI_HOB_POINTERS *Hob\r
+ )\r
+{\r
+ EFI_PEI_HOB_POINTERS NewHob;\r
+\r
+ if (Hob->Raw == NULL) {\r
+ return;\r
+ }\r
+ NewHob.Header = CreateHob (Hob->Header->HobType, Hob->Header->HobLength);\r
+\r
+ if (NewHob.Header != NULL) {\r
+ CopyMem (NewHob.Header + 1, Hob->Header + 1, Hob->Header->HobLength - sizeof (EFI_HOB_GENERIC_HEADER));\r
+ }\r
+}\r
+\r
+/**\r
+ Found the Resource Descriptor HOB that contains a range\r
+\r
+ @param[in] Base Memory start address\r
+ @param[in] Top Memory Top.\r
+\r
+ @return The pointer to the Resource Descriptor HOB.\r
+**/\r
+EFI_HOB_RESOURCE_DESCRIPTOR *\r
+FindResourceDescriptorByRange (\r
+ VOID *HobList,\r
+ EFI_PHYSICAL_ADDRESS Base,\r
+ EFI_PHYSICAL_ADDRESS Top\r
+ )\r
+{\r
+ EFI_PEI_HOB_POINTERS Hob;\r
+ EFI_HOB_RESOURCE_DESCRIPTOR *ResourceHob;\r
+\r
+ for (Hob.Raw = (UINT8 *) HobList; !END_OF_HOB_LIST(Hob); Hob.Raw = GET_NEXT_HOB(Hob)) {\r
+ //\r
+ // Skip all HOBs except Resource Descriptor HOBs\r
+ //\r
+ if (GET_HOB_TYPE (Hob) != EFI_HOB_TYPE_RESOURCE_DESCRIPTOR) {\r
+ continue;\r
+ }\r
+\r
+ //\r
+ // Skip Resource Descriptor HOBs that do not describe tested system memory\r
+ //\r
+ ResourceHob = Hob.ResourceDescriptor;\r
+ if (ResourceHob->ResourceType != EFI_RESOURCE_SYSTEM_MEMORY) {\r
+ continue;\r
+ }\r
+ if ((ResourceHob->ResourceAttribute & MEMORY_ATTRIBUTE_MASK) != TESTED_MEMORY_ATTRIBUTES) {\r
+ continue;\r
+ }\r
+\r
+ //\r
+ // Skip Resource Descriptor HOBs that do not contain the PHIT range EfiFreeMemoryBottom..EfiFreeMemoryTop\r
+ //\r
+ if (Base < ResourceHob->PhysicalStart) {\r
+ continue;\r
+ }\r
+ if (Top > (ResourceHob->PhysicalStart + ResourceHob->ResourceLength)) {\r
+ continue;\r
+ }\r
+ return ResourceHob;\r
+ }\r
+ return NULL;\r
+}\r
+\r
+/**\r
+ Find the highest below 4G memory resource descriptor, except the input Resource Descriptor.\r
+\r
+ @param[in] HobList Hob start address\r
+ @param[in] MinimalNeededSize Minimal needed size.\r
+ @param[in] ExceptResourceHob Ignore this Resource Descriptor.\r
+\r
+ @return The pointer to the Resource Descriptor HOB.\r
+**/\r
+EFI_HOB_RESOURCE_DESCRIPTOR *\r
+FindAnotherHighestBelow4GResourceDescriptor (\r
+ IN VOID *HobList,\r
+ IN UINTN MinimalNeededSize,\r
+ IN EFI_HOB_RESOURCE_DESCRIPTOR *ExceptResourceHob\r
+ )\r
+{\r
+ EFI_PEI_HOB_POINTERS Hob;\r
+ EFI_HOB_RESOURCE_DESCRIPTOR *ResourceHob;\r
+ EFI_HOB_RESOURCE_DESCRIPTOR *ReturnResourceHob;\r
+ ReturnResourceHob = NULL;\r
+\r
+ for (Hob.Raw = (UINT8 *) HobList; !END_OF_HOB_LIST(Hob); Hob.Raw = GET_NEXT_HOB(Hob)) {\r
+ //\r
+ // Skip all HOBs except Resource Descriptor HOBs\r
+ //\r
+ if (GET_HOB_TYPE (Hob) != EFI_HOB_TYPE_RESOURCE_DESCRIPTOR) {\r
+ continue;\r
+ }\r
+\r
+ //\r
+ // Skip Resource Descriptor HOBs that do not describe tested system memory\r
+ //\r
+ ResourceHob = Hob.ResourceDescriptor;\r
+ if (ResourceHob->ResourceType != EFI_RESOURCE_SYSTEM_MEMORY) {\r
+ continue;\r
+ }\r
+ if ((ResourceHob->ResourceAttribute & MEMORY_ATTRIBUTE_MASK) != TESTED_MEMORY_ATTRIBUTES) {\r
+ continue;\r
+ }\r
+\r
+ //\r
+ // Skip if the Resource Descriptor HOB equals to ExceptResourceHob\r
+ //\r
+ if (ResourceHob == ExceptResourceHob) {\r
+ continue;\r
+ }\r
+ //\r
+ // Skip Resource Descriptor HOBs that are beyond 4G\r
+ //\r
+ if ((ResourceHob->PhysicalStart + ResourceHob->ResourceLength) > BASE_4GB) {\r
+ continue;\r
+ }\r
+ //\r
+ // Skip Resource Descriptor HOBs that are too small\r
+ //\r
+ if (ResourceHob->ResourceLength < MinimalNeededSize) {\r
+ continue;\r
+ }\r
+\r
+ //\r
+ // Return the topest Resource Descriptor\r
+ //\r
+ if (ReturnResourceHob == NULL) {\r
+ ReturnResourceHob = ResourceHob;\r
+ } else {\r
+ if (ReturnResourceHob->PhysicalStart < ResourceHob->PhysicalStart) {\r
+ ReturnResourceHob = ResourceHob;\r
+ }\r
+ }\r
+ }\r
+ return ReturnResourceHob;\r
+}\r
+\r
+/**\r
+ It will build HOBs based on information from bootloaders.\r
+\r
+ @retval EFI_SUCCESS If it completed successfully.\r
+ @retval Others If it failed to build required HOBs.\r
+**/\r
+EFI_STATUS\r
+BuildHobs (\r
+ IN UINTN BootloaderParameter\r
+ )\r
+{\r
+ EFI_PEI_HOB_POINTERS Hob;\r
+ UINTN MinimalNeededSize;\r
+ EFI_PHYSICAL_ADDRESS FreeMemoryBottom;\r
+ EFI_PHYSICAL_ADDRESS FreeMemoryTop;\r
+ EFI_PHYSICAL_ADDRESS MemoryBottom;\r
+ EFI_PHYSICAL_ADDRESS MemoryTop;\r
+ EFI_HOB_RESOURCE_DESCRIPTOR *PhitResourceHob;\r
+ EFI_HOB_RESOURCE_DESCRIPTOR *ResourceHob;\r
+\r
+ Hob.Raw = (UINT8 *) BootloaderParameter;\r
+ MinimalNeededSize = FixedPcdGet32 (PcdSystemMemoryUefiRegionSize);\r
+\r
+ ASSERT (Hob.Raw != NULL);\r
+ ASSERT ((UINTN) Hob.HandoffInformationTable->EfiFreeMemoryTop == Hob.HandoffInformationTable->EfiFreeMemoryTop);\r
+ ASSERT ((UINTN) Hob.HandoffInformationTable->EfiMemoryTop == Hob.HandoffInformationTable->EfiMemoryTop);\r
+ ASSERT ((UINTN) Hob.HandoffInformationTable->EfiFreeMemoryBottom == Hob.HandoffInformationTable->EfiFreeMemoryBottom);\r
+ ASSERT ((UINTN) Hob.HandoffInformationTable->EfiMemoryBottom == Hob.HandoffInformationTable->EfiMemoryBottom);\r
+\r
+\r
+ //\r
+ // Try to find Resource Descriptor HOB that contains Hob range EfiMemoryBottom..EfiMemoryTop\r
+ //\r
+ PhitResourceHob = FindResourceDescriptorByRange(Hob.Raw, Hob.HandoffInformationTable->EfiMemoryBottom, Hob.HandoffInformationTable->EfiMemoryTop);\r
+ if (PhitResourceHob == NULL) {\r
+ //\r
+ // Boot loader's Phit Hob is not in an available Resource Descriptor, find another Resource Descriptor for new Phit Hob\r
+ //\r
+ ResourceHob = FindAnotherHighestBelow4GResourceDescriptor(Hob.Raw, MinimalNeededSize, NULL);\r
+ if (ResourceHob == NULL) {\r
+ return EFI_NOT_FOUND;\r
+ }\r
+\r
+ MemoryBottom = ResourceHob->PhysicalStart + ResourceHob->ResourceLength - MinimalNeededSize;\r
+ FreeMemoryBottom = MemoryBottom;\r
+ FreeMemoryTop = ResourceHob->PhysicalStart + ResourceHob->ResourceLength;\r
+ MemoryTop = FreeMemoryTop;\r
+ } else if (PhitResourceHob->PhysicalStart + PhitResourceHob->ResourceLength - Hob.HandoffInformationTable->EfiMemoryTop >= MinimalNeededSize) {\r
+ //\r
+ // New availiable Memory range in new hob is right above memory top in old hob.\r
+ //\r
+ MemoryBottom = Hob.HandoffInformationTable->EfiFreeMemoryTop;\r
+ FreeMemoryBottom = Hob.HandoffInformationTable->EfiMemoryTop;\r
+ FreeMemoryTop = FreeMemoryBottom + MinimalNeededSize;\r
+ MemoryTop = FreeMemoryTop;\r
+ } else if (Hob.HandoffInformationTable->EfiMemoryBottom - PhitResourceHob->PhysicalStart >= MinimalNeededSize) {\r
+ //\r
+ // New availiable Memory range in new hob is right below memory bottom in old hob.\r
+ //\r
+ MemoryBottom = Hob.HandoffInformationTable->EfiMemoryBottom - MinimalNeededSize;\r
+ FreeMemoryBottom = MemoryBottom;\r
+ FreeMemoryTop = Hob.HandoffInformationTable->EfiMemoryBottom;\r
+ MemoryTop = Hob.HandoffInformationTable->EfiMemoryTop;\r
+ } else {\r
+ //\r
+ // In the Resource Descriptor HOB contains boot loader Hob, there is no enough free memory size for payload hob\r
+ // Find another Resource Descriptor Hob\r
+ //\r
+ ResourceHob = FindAnotherHighestBelow4GResourceDescriptor(Hob.Raw, MinimalNeededSize, PhitResourceHob);\r
+ if (ResourceHob == NULL) {\r
+ return EFI_NOT_FOUND;\r
+ }\r
+\r
+ MemoryBottom = ResourceHob->PhysicalStart + ResourceHob->ResourceLength - MinimalNeededSize;\r
+ FreeMemoryBottom = MemoryBottom;\r
+ FreeMemoryTop = ResourceHob->PhysicalStart + ResourceHob->ResourceLength;\r
+ MemoryTop = FreeMemoryTop;\r
+ }\r
+ HobConstructor ((VOID *) (UINTN) MemoryBottom, (VOID *) (UINTN) MemoryTop, (VOID *) (UINTN) FreeMemoryBottom, (VOID *) (UINTN) FreeMemoryTop);\r
+ //\r
+ // From now on, mHobList will point to the new Hob range.\r
+ //\r
+\r
+ //\r
+ // Since payload created new Hob, move all hobs except PHIT from boot loader hob list.\r
+ //\r
+ while (!END_OF_HOB_LIST (Hob)) {\r
+ if (Hob.Header->HobType != EFI_HOB_TYPE_HANDOFF) {\r
+ // Add this hob to payload HOB\r
+ AddNewHob (&Hob);\r
+ }\r
+ Hob.Raw = GET_NEXT_HOB (Hob);\r
+ }\r
+\r
+ return EFI_SUCCESS;\r
+}\r
+\r
+/**\r
+ Entry point to the C language phase of UEFI payload.\r
+\r
+ @retval It will not return if SUCCESS, and return error when passing bootloader parameter.\r
+**/\r
+EFI_STATUS\r
+EFIAPI\r
+_ModuleEntryPoint (\r
+ IN UINTN BootloaderParameter\r
+ )\r
+{\r
+ EFI_STATUS Status;\r
+ EFI_HOB_HANDOFF_INFO_TABLE *HandoffHobTable;\r
+ EFI_PEI_HOB_POINTERS Hob;\r
+\r
+ mHobList = (VOID *) BootloaderParameter;\r
+ // Call constructor for all libraries\r
+ ProcessLibraryConstructorList ();\r
+\r
+ DEBUG ((DEBUG_INFO, "Entering Universal Payload...\n"));\r
+ DEBUG ((DEBUG_INFO, "sizeof(UINTN) = 0x%x\n", sizeof(UINTN)));\r
+\r
+ // Initialize floating point operating environment to be compliant with UEFI spec.\r
+ InitializeFloatingPointUnits ();\r
+\r
+ // Build HOB based on information from Bootloader\r
+ Status = BuildHobs (BootloaderParameter);\r
+\r
+ //\r
+ // Mask off all legacy 8259 interrupt sources\r
+ //\r
+ IoWrite8 (LEGACY_8259_MASK_REGISTER_MASTER, 0xFF);\r
+ IoWrite8 (LEGACY_8259_MASK_REGISTER_SLAVE, 0xFF);\r
+\r
+ HandoffHobTable = (EFI_HOB_HANDOFF_INFO_TABLE *) GetFirstHob(EFI_HOB_TYPE_HANDOFF);\r
+ Hob.HandoffInformationTable = HandoffHobTable;\r
+\r
+ // Should not get here\r
+ CpuDeadLoop ();\r
+ return EFI_SUCCESS;\r
+}\r