text: /** @file
  
    Copyright (c) 2014 - 2021, Intel Corporation. All rights reserved.<BR>
    SPDX-License-Identifier: BSD-2-Clause-Patent
  
  **/
  
  #include <Guid/MemoryTypeInformation.h>
  #include "UefiPayloadEntry.h"
  
  STATIC UINT32  mTopOfLowerUsableDram = 0;
  
  EFI_MEMORY_TYPE_INFORMATION  mDefaultMemoryTypeInformation[] = {
    { EfiACPIReclaimMemory,   FixedPcdGet32 (PcdMemoryTypeEfiACPIReclaimMemory)   },
    { EfiACPIMemoryNVS,       FixedPcdGet32 (PcdMemoryTypeEfiACPIMemoryNVS)       },
    { EfiReservedMemoryType,  FixedPcdGet32 (PcdMemoryTypeEfiReservedMemoryType)  },
    { EfiRuntimeServicesData, FixedPcdGet32 (PcdMemoryTypeEfiRuntimeServicesData) },
    { EfiRuntimeServicesCode, FixedPcdGet32 (PcdMemoryTypeEfiRuntimeServicesCode) },
    { EfiMaxMemoryType,       0                                                   }
  };
  
  /**
     Function to reserve memory below 4GB for EDKII Modules.
  
     This causes the DXE to dispatch everything under 4GB and allows Operating
     System's that require EFI_LOADED_IMAGE to be under 4GB to start.
     e.g. Xen hypervisor used in Qubes.
  **/
  VOID
  ForceModulesBelow4G (
    VOID
    )
  {
    DEBUG ((DEBUG_INFO, "Building hob to restrict memory resorces to below 4G.\n"));
  
    //
    // Create Memory Type Information HOB
    //
    BuildGuidDataHob (
      &gEfiMemoryTypeInformationGuid,
      mDefaultMemoryTypeInformation,
      sizeof (mDefaultMemoryTypeInformation)
      );
  }
  
  /**
     Callback function to build resource descriptor HOB
  
     This function build a HOB based on the memory map entry info.
     It creates only EFI_RESOURCE_MEMORY_MAPPED_IO and EFI_RESOURCE_MEMORY_RESERVED
     resources.
  
     @param MemoryMapEntry         Memory map entry info got from bootloader.
     @param Params                 A pointer to ACPI_BOARD_INFO.
  
    @retval EFI_SUCCESS            Successfully build a HOB.
    @retval EFI_INVALID_PARAMETER  Invalid parameter provided.
  **/
  EFI_STATUS
  MemInfoCallbackMmio (
    IN MEMORY_MAP_ENTRY  *MemoryMapEntry,
    IN VOID              *Params
    )
  {
    EFI_PHYSICAL_ADDRESS         Base;
    EFI_RESOURCE_TYPE            Type;
    UINT64                       Size;
    EFI_RESOURCE_ATTRIBUTE_TYPE  Attribue;
    ACPI_BOARD_INFO              *AcpiBoardInfo;
  
    AcpiBoardInfo = (ACPI_BOARD_INFO *)Params;
    if (AcpiBoardInfo == NULL) {
      return EFI_INVALID_PARAMETER;
    }
  
    //
    // Skip types already handled in MemInfoCallback
    //
    if ((MemoryMapEntry->Type == E820_RAM) || (MemoryMapEntry->Type == E820_ACPI)) {
      return EFI_SUCCESS;
    }
  
    if (MemoryMapEntry->Base == AcpiBoardInfo->PcieBaseAddress) {
      //
      // MMCONF is always MMIO
      //
      Type = EFI_RESOURCE_MEMORY_MAPPED_IO;
    } else if (MemoryMapEntry->Base < mTopOfLowerUsableDram) {
      //
      // It's in DRAM and thus must be reserved
      //
      Type = EFI_RESOURCE_MEMORY_RESERVED;
    } else if ((MemoryMapEntry->Base < 0x100000000ULL) && (MemoryMapEntry->Base >= mTopOfLowerUsableDram)) {
      //
      // It's not in DRAM, must be MMIO
      //
      Type = EFI_RESOURCE_MEMORY_MAPPED_IO;
    } else {
      Type = EFI_RESOURCE_MEMORY_RESERVED;
    }
  
    Base = MemoryMapEntry->Base;
    Size = MemoryMapEntry->Size;
  
    Attribue = EFI_RESOURCE_ATTRIBUTE_PRESENT |
               EFI_RESOURCE_ATTRIBUTE_INITIALIZED |
               EFI_RESOURCE_ATTRIBUTE_TESTED |
               EFI_RESOURCE_ATTRIBUTE_UNCACHEABLE |
               EFI_RESOURCE_ATTRIBUTE_WRITE_COMBINEABLE |
               EFI_RESOURCE_ATTRIBUTE_WRITE_THROUGH_CACHEABLE |
               EFI_RESOURCE_ATTRIBUTE_WRITE_BACK_CACHEABLE;
  
    BuildResourceDescriptorHob (Type, Attribue, (EFI_PHYSICAL_ADDRESS)Base, Size);
    DEBUG ((DEBUG_INFO, "buildhob: base = 0x%lx, size = 0x%lx, type = 0x%x\n", Base, Size, Type));
  
    if ((MemoryMapEntry->Type == E820_UNUSABLE) ||
        (MemoryMapEntry->Type == E820_DISABLED))
    {
      BuildMemoryAllocationHob (Base, Size, EfiUnusableMemory);
    } else if (MemoryMapEntry->Type == E820_PMEM) {
      BuildMemoryAllocationHob (Base, Size, EfiPersistentMemory);
    }
  
    return EFI_SUCCESS;
  }
  
  /**
     Callback function to find TOLUD (Top of Lower Usable DRAM)
  
     Estimate where TOLUD (Top of Lower Usable DRAM) resides. The exact position
     would require platform specific code.
  
     @param MemoryMapEntry         Memory map entry info got from bootloader.
     @param Params                 Not used for now.
  
    @retval EFI_SUCCESS            Successfully updated mTopOfLowerUsableDram.
  **/
  EFI_STATUS
  FindToludCallback (
    IN MEMORY_MAP_ENTRY  *MemoryMapEntry,
    IN VOID              *Params
    )
  {
    //
    // This code assumes that the memory map on this x86 machine below 4GiB is continous
    // until TOLUD. In addition it assumes that the bootloader provided memory tables have
    // no "holes" and thus the first memory range not covered by e820 marks the end of
    // usable DRAM. In addition it's assumed that every reserved memory region touching
    // usable RAM is also covering DRAM, everything else that is marked reserved thus must be
    // MMIO not detectable by bootloader/OS
    //
  
    //
    // Skip memory types not RAM or reserved
    //
    if ((MemoryMapEntry->Type == E820_UNUSABLE) || (MemoryMapEntry->Type == E820_DISABLED) ||
        (MemoryMapEntry->Type == E820_PMEM))
    {
      return EFI_SUCCESS;
    }
  
    //
    // Skip resources above 4GiB
    //
    if ((MemoryMapEntry->Base + MemoryMapEntry->Size) > 0x100000000ULL) {
      return EFI_SUCCESS;
    }
  
    if ((MemoryMapEntry->Type == E820_RAM) || (MemoryMapEntry->Type == E820_ACPI) ||
        (MemoryMapEntry->Type == E820_NVS))
    {
      //
      // It's usable DRAM. Update TOLUD.
      //
      if (mTopOfLowerUsableDram < (MemoryMapEntry->Base + MemoryMapEntry->Size)) {
        mTopOfLowerUsableDram = (UINT32)(MemoryMapEntry->Base + MemoryMapEntry->Size);
      }
    } else {
      //
      // It might be 'reserved DRAM' or 'MMIO'.
      //
      // If it touches usable DRAM at Base assume it's DRAM as well,
      // as it could be bootloader installed tables, TSEG, GTT, ...
      //
      if (mTopOfLowerUsableDram == MemoryMapEntry->Base) {
        mTopOfLowerUsableDram = (UINT32)(MemoryMapEntry->Base + MemoryMapEntry->Size);
      }
    }
  
    return EFI_SUCCESS;
  }
  
  /**
     Callback function to build resource descriptor HOB
  
     This function build a HOB based on the memory map entry info.
     Only add EFI_RESOURCE_SYSTEM_MEMORY.
  
     @param MemoryMapEntry         Memory map entry info got from bootloader.
     @param Params                 Not used for now.
  
    @retval RETURN_SUCCESS        Successfully build a HOB.
  **/
  EFI_STATUS
  MemInfoCallback (
    IN MEMORY_MAP_ENTRY  *MemoryMapEntry,
    IN VOID              *Params
    )
  {
    EFI_PHYSICAL_ADDRESS         Base;
    EFI_RESOURCE_TYPE            Type;
    UINT64                       Size;
    EFI_RESOURCE_ATTRIBUTE_TYPE  Attribue;
  
    //
    // Skip everything not known to be usable DRAM.
    // It will be added later.
    //
    if ((MemoryMapEntry->Type != E820_RAM) && (MemoryMapEntry->Type != E820_ACPI) &&
        (MemoryMapEntry->Type != E820_NVS))
    {
      return RETURN_SUCCESS;
    }
  
    Type = EFI_RESOURCE_SYSTEM_MEMORY;
    Base = MemoryMapEntry->Base;
    Size = MemoryMapEntry->Size;
  
    Attribue = EFI_RESOURCE_ATTRIBUTE_PRESENT |
               EFI_RESOURCE_ATTRIBUTE_INITIALIZED |
               EFI_RESOURCE_ATTRIBUTE_TESTED |
               EFI_RESOURCE_ATTRIBUTE_UNCACHEABLE |
               EFI_RESOURCE_ATTRIBUTE_WRITE_COMBINEABLE |
               EFI_RESOURCE_ATTRIBUTE_WRITE_THROUGH_CACHEABLE |
               EFI_RESOURCE_ATTRIBUTE_WRITE_BACK_CACHEABLE;
  
    BuildResourceDescriptorHob (Type, Attribue, (EFI_PHYSICAL_ADDRESS)Base, Size);
    DEBUG ((DEBUG_INFO, "buildhob: base = 0x%lx, size = 0x%lx, type = 0x%x\n", Base, Size, Type));
  
    if (MemoryMapEntry->Type == E820_ACPI) {
      BuildMemoryAllocationHob (Base, Size, EfiACPIReclaimMemory);
    } else if (MemoryMapEntry->Type == E820_NVS) {
      BuildMemoryAllocationHob (Base, Size, EfiACPIMemoryNVS);
    }
  
    return RETURN_SUCCESS;
  }
  
  /**
    It will build HOBs based on information from bootloaders.
  
    @retval EFI_SUCCESS        If it completed successfully.
    @retval Others             If it failed to build required HOBs.
  **/
  EFI_STATUS
  BuildHobFromBl (
    VOID
    )
  {
    EFI_STATUS                        Status;
    ACPI_BOARD_INFO                   *AcpiBoardInfo;
    EFI_PEI_GRAPHICS_INFO_HOB         GfxInfo;
    EFI_PEI_GRAPHICS_INFO_HOB         *NewGfxInfo;
    EFI_PEI_GRAPHICS_DEVICE_INFO_HOB  GfxDeviceInfo;
    EFI_PEI_GRAPHICS_DEVICE_INFO_HOB  *NewGfxDeviceInfo;
    UNIVERSAL_PAYLOAD_SMBIOS_TABLE    *SmBiosTableHob;
    UNIVERSAL_PAYLOAD_ACPI_TABLE      *AcpiTableHob;
  
    //
    // First find TOLUD
    //
    DEBUG ((DEBUG_INFO, "Guessing Top of Lower Usable DRAM:\n"));
    Status = ParseMemoryInfo (FindToludCallback, NULL);
    if (EFI_ERROR (Status)) {
      return Status;
    }
  
    DEBUG ((DEBUG_INFO, "Assuming TOLUD = 0x%x\n", mTopOfLowerUsableDram));
  
    //
    // Parse memory info and build memory HOBs for Usable RAM
    //
    DEBUG ((DEBUG_INFO, "Building ResourceDescriptorHobs for usable memory:\n"));
    Status = ParseMemoryInfo (MemInfoCallback, NULL);
    if (EFI_ERROR (Status)) {
      return Status;
    }
  
    //
    // Create guid hob for frame buffer information
    //
    Status = ParseGfxInfo (&GfxInfo);
    if (!EFI_ERROR (Status)) {
      NewGfxInfo = BuildGuidHob (&gEfiGraphicsInfoHobGuid, sizeof (GfxInfo));
      ASSERT (NewGfxInfo != NULL);
      CopyMem (NewGfxInfo, &GfxInfo, sizeof (GfxInfo));
      DEBUG ((DEBUG_INFO, "Created graphics info hob\n"));
    }
  
    Status = ParseGfxDeviceInfo (&GfxDeviceInfo);
    if (!EFI_ERROR (Status)) {
      NewGfxDeviceInfo = BuildGuidHob (&gEfiGraphicsDeviceInfoHobGuid, sizeof (GfxDeviceInfo));
      ASSERT (NewGfxDeviceInfo != NULL);
      CopyMem (NewGfxDeviceInfo, &GfxDeviceInfo, sizeof (GfxDeviceInfo));
      DEBUG ((DEBUG_INFO, "Created graphics device info hob\n"));
    }
  
    //
    // Creat SmBios table Hob
    //
    SmBiosTableHob = BuildGuidHob (&gUniversalPayloadSmbiosTableGuid, sizeof (UNIVERSAL_PAYLOAD_SMBIOS_TABLE));
    ASSERT (SmBiosTableHob != NULL);
    SmBiosTableHob->Header.Revision = UNIVERSAL_PAYLOAD_SMBIOS_TABLE_REVISION;
    SmBiosTableHob->Header.Length   = sizeof (UNIVERSAL_PAYLOAD_SMBIOS_TABLE);
    DEBUG ((DEBUG_INFO, "Create smbios table gUniversalPayloadSmbiosTableGuid guid hob\n"));
    Status = ParseSmbiosTable (SmBiosTableHob);
    if (!EFI_ERROR (Status)) {
      DEBUG ((DEBUG_INFO, "Detected Smbios Table at 0x%lx\n", SmBiosTableHob->SmBiosEntryPoint));
    }
  
    //
    // Creat ACPI table Hob
    //
    AcpiTableHob = BuildGuidHob (&gUniversalPayloadAcpiTableGuid, sizeof (UNIVERSAL_PAYLOAD_ACPI_TABLE));
    ASSERT (AcpiTableHob != NULL);
    AcpiTableHob->Header.Revision = UNIVERSAL_PAYLOAD_ACPI_TABLE_REVISION;
    AcpiTableHob->Header.Length   = sizeof (UNIVERSAL_PAYLOAD_ACPI_TABLE);
    DEBUG ((DEBUG_INFO, "Create ACPI table gUniversalPayloadAcpiTableGuid guid hob\n"));
    Status = ParseAcpiTableInfo (AcpiTableHob);
    if (!EFI_ERROR (Status)) {
      DEBUG ((DEBUG_INFO, "Detected ACPI Table at 0x%lx\n", AcpiTableHob->Rsdp));
    }
  
    //
    // Create guid hob for acpi board information
    //
    AcpiBoardInfo = BuildHobFromAcpi (AcpiTableHob->Rsdp);
    ASSERT (AcpiBoardInfo != NULL);
  
    //
    // Parse memory info and build memory HOBs for reserved DRAM and MMIO
    //
    DEBUG ((DEBUG_INFO, "Building ResourceDescriptorHobs for reserved memory:\n"));
    Status = ParseMemoryInfo (MemInfoCallbackMmio, AcpiBoardInfo);
    if (EFI_ERROR (Status)) {
      return Status;
    }
  
    //
    // Parse the misc info provided by bootloader
    //
    Status = ParseMiscInfo ();
    if (EFI_ERROR (Status)) {
      DEBUG ((DEBUG_WARN, "Error when parsing misc info, Status = %r\n", Status));
    }
  
    //
    // Parse platform specific information.
    //
    Status = ParsePlatformInfo ();
    if (EFI_ERROR (Status)) {
      DEBUG ((DEBUG_ERROR, "Error when parsing platform info, Status = %r\n", Status));
      return Status;
    }
  
    return EFI_SUCCESS;
  }
  
  /**
    This function will build some generic HOBs that doesn't depend on information from bootloaders.
  
  **/
  VOID
  BuildGenericHob (
    VOID
    )
  {
    UINT32                       RegEax;
    UINT8                        PhysicalAddressBits;
    EFI_RESOURCE_ATTRIBUTE_TYPE  ResourceAttribute;
  
    // The UEFI payload FV
    BuildMemoryAllocationHob (PcdGet32 (PcdPayloadFdMemBase), PcdGet32 (PcdPayloadFdMemSize), EfiBootServicesData);
  
    //
    // Build CPU memory space and IO space hob
    //
    AsmCpuid (0x80000000, &RegEax, NULL, NULL, NULL);
    if (RegEax >= 0x80000008) {
      AsmCpuid (0x80000008, &RegEax, NULL, NULL, NULL);
      PhysicalAddressBits = (UINT8)RegEax;
    } else {
      PhysicalAddressBits = 36;
    }
  
    BuildCpuHob (PhysicalAddressBits, 16);
  
    //
    // Report Local APIC range, cause sbl HOB to be NULL, comment now
    //
    ResourceAttribute = (
                         EFI_RESOURCE_ATTRIBUTE_PRESENT |
                         EFI_RESOURCE_ATTRIBUTE_INITIALIZED |
                         EFI_RESOURCE_ATTRIBUTE_UNCACHEABLE |
                         EFI_RESOURCE_ATTRIBUTE_TESTED
                         );
    BuildResourceDescriptorHob (EFI_RESOURCE_MEMORY_MAPPED_IO, ResourceAttribute, 0xFEC80000, SIZE_512KB);
    BuildMemoryAllocationHob (0xFEC80000, SIZE_512KB, EfiMemoryMappedIO);
  }
  
  /**
    Entry point to the C language phase of UEFI payload.
  
    @param[in]   BootloaderParameter    The starting address of bootloader parameter block.
  
    @retval      It will not return if SUCCESS, and return error when passing bootloader parameter.
  **/
  EFI_STATUS
  EFIAPI
  _ModuleEntryPoint (
    IN UINTN  BootloaderParameter
    )
  {
    EFI_STATUS                          Status;
    PHYSICAL_ADDRESS                    DxeCoreEntryPoint;
    UINTN                               MemBase;
    UINTN                               HobMemBase;
    UINTN                               HobMemTop;
    EFI_PEI_HOB_POINTERS                Hob;
    SERIAL_PORT_INFO                    SerialPortInfo;
    UNIVERSAL_PAYLOAD_SERIAL_PORT_INFO  *UniversalSerialPort;
  
    Status = PcdSet64S (PcdBootloaderParameter, BootloaderParameter);
    ASSERT_EFI_ERROR (Status);
  
    // Initialize floating point operating environment to be compliant with UEFI spec.
    InitializeFloatingPointUnits ();
  
    // HOB region is used for HOB and memory allocation for this module
    MemBase    = PcdGet32 (PcdPayloadFdMemBase);
    HobMemBase = ALIGN_VALUE (MemBase + PcdGet32 (PcdPayloadFdMemSize), SIZE_1MB);
    HobMemTop  = HobMemBase + FixedPcdGet32 (PcdSystemMemoryUefiRegionSize);
  
    HobConstructor ((VOID *)MemBase, (VOID *)HobMemTop, (VOID *)HobMemBase, (VOID *)HobMemTop);
  
    //
    // Build serial port info
    //
    Status = ParseSerialInfo (&SerialPortInfo);
    if (!EFI_ERROR (Status)) {
      UniversalSerialPort = BuildGuidHob (&gUniversalPayloadSerialPortInfoGuid, sizeof (UNIVERSAL_PAYLOAD_SERIAL_PORT_INFO));
      ASSERT (UniversalSerialPort != NULL);
      UniversalSerialPort->Header.Revision = UNIVERSAL_PAYLOAD_SERIAL_PORT_INFO_REVISION;
      UniversalSerialPort->Header.Length   = sizeof (UNIVERSAL_PAYLOAD_SERIAL_PORT_INFO);
      UniversalSerialPort->UseMmio         = (SerialPortInfo.Type == 1) ? FALSE : TRUE;
      UniversalSerialPort->RegisterBase    = SerialPortInfo.BaseAddr;
      UniversalSerialPort->BaudRate        = SerialPortInfo.Baud;
      UniversalSerialPort->RegisterStride  = (UINT8)SerialPortInfo.RegWidth;
    }
  
    // The library constructors might depend on serial port, so call it after serial port hob
    ProcessLibraryConstructorList ();
    DEBUG ((DEBUG_INFO, "sizeof(UINTN) = 0x%x\n", sizeof (UINTN)));
  
    // Build HOB based on information from Bootloader
    Status = BuildHobFromBl ();
    if (EFI_ERROR (Status)) {
      DEBUG ((DEBUG_ERROR, "BuildHobFromBl Status = %r\n", Status));
      return Status;
    }
  
    // Build other HOBs required by DXE
    BuildGenericHob ();
  
    // Create a HOB to make resources for EDKII modules below 4G
    if (!FixedPcdGetBool (PcdDispatchModuleAbove4GMemory)) {
      ForceModulesBelow4G ();
    }
  
    // Load the DXE Core
    Status = LoadDxeCore (&DxeCoreEntryPoint);
    ASSERT_EFI_ERROR (Status);
  
    DEBUG ((DEBUG_INFO, "DxeCoreEntryPoint = 0x%lx\n", DxeCoreEntryPoint));
  
    //
    // Mask off all legacy 8259 interrupt sources
    //
    IoWrite8 (LEGACY_8259_MASK_REGISTER_MASTER, 0xFF);
    IoWrite8 (LEGACY_8259_MASK_REGISTER_SLAVE, 0xFF);
  
    Hob.HandoffInformationTable = (EFI_HOB_HANDOFF_INFO_TABLE *)GetFirstHob (EFI_HOB_TYPE_HANDOFF);
    HandOffToDxeCore (DxeCoreEntryPoint, Hob);
  
    // Should not get here
    CpuDeadLoop ();
    return EFI_SUCCESS;
  }
  