/** @file
Framework PEIM to initialize memory on a Quark Memory Controller.

Copyright (c) 2013 - 2016, Intel Corporation.

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.

**/

#include "CommonHeader.h"
#include "MrcWrapper.h"
#include <Ioh.h>
#include "Platform.h"

#include <Library/PlatformHelperLib.h>

//
// ------------------------ TSEG Base
//
// ------------------------ RESERVED_CPU_S3_SAVE_OFFSET
// CPU S3 data
// ------------------------ RESERVED_ACPI_S3_RANGE_OFFSET
// S3 Memory base structure
// ------------------------ TSEG + 1 page

#define RESERVED_CPU_S3_SAVE_OFFSET (RESERVED_ACPI_S3_RANGE_OFFSET - sizeof (SMM_S3_RESUME_STATE))

// Strap configuration register specifying DDR setup
#define QUARK_SCSS_REG_STPDDRCFG   0x00

// Macro counting array elements
#define COUNT(a)                 (sizeof(a)/sizeof(*a))


EFI_MEMORY_TYPE_INFORMATION mDefaultQncMemoryTypeInformation[] = {
  { EfiReservedMemoryType,  EDKII_RESERVED_SIZE_PAGES },     // BIOS Reserved
  { EfiACPIMemoryNVS,       ACPI_NVS_SIZE_PAGES },    // S3, SMM, etc
  { EfiRuntimeServicesData, RUNTIME_SERVICES_DATA_SIZE_PAGES },
  { EfiRuntimeServicesCode, RUNTIME_SERVICES_CODE_SIZE_PAGES },
  { EfiACPIReclaimMemory,   ACPI_RECLAIM_SIZE_PAGES },     // ACPI ASL
  { EfiMaxMemoryType,       0 }
};

/**
  Configure Uart mmio base for MRC serial log purpose

  @param  MrcData  - MRC configuration data updated

**/
VOID
MrcUartConfig(
  MRC_PARAMS *MrcData
  )
{
  UINT8    UartIdx;
  UINT32   RegData32;
  UINT8    IohUartBus;
  UINT8    IohUartDev;

  UartIdx    = PcdGet8(PcdIohUartFunctionNumber);
  IohUartBus = PcdGet8(PcdIohUartBusNumber);
  IohUartDev = PcdGet8(PcdIohUartDevNumber);

  RegData32 = PciRead32 (PCI_LIB_ADDRESS(IohUartBus,  IohUartDev, UartIdx, PCI_BASE_ADDRESSREG_OFFSET));
  MrcData->uart_mmio_base = RegData32 & 0xFFFFFFF0;
}

/**
  Configure MRC from memory controller fuse settings.

  @param  MrcData      - MRC configuration data to be updated.

  @return EFI_SUCCESS    MRC Config parameters updated from platform data.
**/
EFI_STATUS
MrcConfigureFromMcFuses (
  OUT MRC_PARAMS                          *MrcData
  )
{
  UINT32                            McFuseStat;

  McFuseStat = QNCPortRead (
                 QUARK_NC_MEMORY_CONTROLLER_SB_PORT_ID,
                 QUARK_NC_MEMORY_CONTROLLER_REG_DFUSESTAT
                 );

  DEBUG ((EFI_D_INFO, "MRC McFuseStat 0x%08x\n", McFuseStat));

  if ((McFuseStat & B_DFUSESTAT_ECC_DIS) != 0) {
    DEBUG ((EFI_D_INFO, "MRC Fuse : fus_dun_ecc_dis.\n"));
    MrcData->ecc_enables = 0;
  } else {
    MrcData->ecc_enables = 1;
  }
  return EFI_SUCCESS;
}

/**
  Configure MRC from platform info hob.

  @param  MrcData      - MRC configuration data to be updated.

  @return EFI_SUCCESS    MRC Config parameters updated from hob.
  @return EFI_NOT_FOUND  Platform Info or MRC Config parameters not found.
  @return EFI_INVALID_PARAMETER  Wrong params in hob.
**/
EFI_STATUS
MrcConfigureFromInfoHob (
  OUT MRC_PARAMS  *MrcData
  )
{
  PDAT_MRC_ITEM  *ItemData;

  ItemData = (PDAT_MRC_ITEM *)PcdGetPtr (PcdMrcParameters);

  MrcData->channel_enables     = ItemData->ChanMask;
  MrcData->channel_width       = ItemData->ChanWidth;
  MrcData->address_mode        = ItemData->AddrMode;
  // Enable scrambling if requested.
  MrcData->scrambling_enables  = (ItemData->Flags & PDAT_MRC_FLAG_SCRAMBLE_EN) != 0;
  MrcData->ddr_type            = ItemData->DramType;
  MrcData->dram_width          = ItemData->DramWidth;
  MrcData->ddr_speed           = ItemData->DramSpeed;
  // Enable ECC if requested.
  MrcData->rank_enables        = ItemData->RankMask;
  MrcData->params.DENSITY      = ItemData->DramDensity;
  MrcData->params.tCL          = ItemData->tCL;
  MrcData->params.tRAS         = ItemData->tRAS;
  MrcData->params.tWTR         = ItemData->tWTR;
  MrcData->params.tRRD         = ItemData->tRRD;
  MrcData->params.tFAW         = ItemData->tFAW;

  MrcData->refresh_rate        = ItemData->SrInt;
  MrcData->sr_temp_range       = ItemData->SrTemp;
  MrcData->ron_value           = ItemData->DramRonVal;
  MrcData->rtt_nom_value       = ItemData->DramRttNomVal;
  MrcData->rd_odt_value        = ItemData->SocRdOdtVal;

  DEBUG ((EFI_D_INFO, "MRC dram_width %d\n",  MrcData->dram_width));
  DEBUG ((EFI_D_INFO, "MRC rank_enables %d\n",MrcData->rank_enables));
  DEBUG ((EFI_D_INFO, "MRC ddr_speed %d\n",   MrcData->ddr_speed));
  DEBUG ((EFI_D_INFO, "MRC flags: %s\n",
    (MrcData->scrambling_enables) ? L"SCRAMBLE_EN" : L""
    ));

  DEBUG ((EFI_D_INFO, "MRC density=%d tCL=%d tRAS=%d tWTR=%d tRRD=%d tFAW=%d\n",
    MrcData->params.DENSITY,
    MrcData->params.tCL,
    MrcData->params.tRAS,
    MrcData->params.tWTR,
    MrcData->params.tRRD,
    MrcData->params.tFAW
    ));

  return EFI_SUCCESS;
}

/**

  Configure ECC scrub

  @param MrcData - MRC configuration

**/
VOID
EccScrubSetup(
  const MRC_PARAMS *MrcData
  )
{
  UINT32 BgnAdr = 0;
  UINT32 EndAdr = MrcData->mem_size;
  UINT32 BlkSize = PcdGet8(PcdEccScrubBlkSize) & SCRUB_CFG_BLOCKSIZE_MASK;
  UINT32 Interval = PcdGet8(PcdEccScrubInterval) & SCRUB_CFG_INTERVAL_MASK;

  if( MrcData->ecc_enables == 0 || MrcData->boot_mode == bmS3 || Interval == 0) {
    // No scrub configuration needed if ECC not enabled
    // On S3 resume reconfiguration is done as part of resume
    // script, see SNCS3Save.c ==> SaveRuntimeScriptTable()
    // Also if PCD disables scrub, then we do nothing.
    return;
  }

  QNCPortWrite (QUARK_NC_RMU_SB_PORT_ID, QUARK_NC_ECC_SCRUB_END_MEM_REG, EndAdr);
  QNCPortWrite (QUARK_NC_RMU_SB_PORT_ID, QUARK_NC_ECC_SCRUB_START_MEM_REG, BgnAdr);
  QNCPortWrite (QUARK_NC_RMU_SB_PORT_ID, QUARK_NC_ECC_SCRUB_NEXT_READ_REG, BgnAdr);
  QNCPortWrite (QUARK_NC_RMU_SB_PORT_ID, QUARK_NC_ECC_SCRUB_CONFIG_REG,
    Interval << SCRUB_CFG_INTERVAL_SHIFT |
    BlkSize << SCRUB_CFG_BLOCKSIZE_SHIFT);

  McD0PciCfg32 (QNC_ACCESS_PORT_MCR) = SCRUB_RESUME_MSG();
}

/** Post InstallS3Memory / InstallEfiMemory tasks given MrcData context.

  @param[in]       MrcData  MRC configuration.
  @param[in]       IsS3     TRUE if after InstallS3Memory.

**/
VOID
PostInstallMemory (
  IN MRC_PARAMS                           *MrcData,
  IN BOOLEAN                              IsS3
  )
{
  UINT32                            RmuMainDestBaseAddress;
  UINT32                            *RmuMainSrcBaseAddress;
  UINTN                             RmuMainSize;
  EFI_STATUS                        Status;

  //
  // Setup ECC policy (All boot modes).
  //
  QNCPolicyDblEccBitErr (V_WDT_CONTROL_DBL_ECC_BIT_ERR_WARM);

  //
  // Find the 64KB of memory for Rmu Main at the top of available memory.
  //
  InfoPostInstallMemory (&RmuMainDestBaseAddress, NULL, NULL);
  DEBUG ((EFI_D_INFO, "RmuMain Base Address : 0x%x\n", RmuMainDestBaseAddress));

  //
  // Relocate RmuMain.
  //
  if (IsS3) {
    QNCSendOpcodeDramReady (RmuMainDestBaseAddress);
  } else {
    Status = PlatformFindFvFileRawDataSection (NULL, PcdGetPtr(PcdQuarkMicrocodeFile), (VOID **) &RmuMainSrcBaseAddress, &RmuMainSize);
    ASSERT_EFI_ERROR (Status);
    if (!EFI_ERROR (Status)) {
      DEBUG ((EFI_D_INFO, "Found Microcode ADDR:SIZE 0x%08x:0x%04x\n", (UINTN) RmuMainSrcBaseAddress, RmuMainSize));
    }

    RmuMainRelocation (RmuMainDestBaseAddress, (UINT32) RmuMainSrcBaseAddress, RmuMainSize);
    QNCSendOpcodeDramReady (RmuMainDestBaseAddress);
    EccScrubSetup (MrcData);
  }
}

/**

  Do memory initialisation for QNC DDR3 SDRAM Controller

  @param  FfsHeader    Not used.
  @param  PeiServices  General purpose services available to every PEIM.

  @return EFI_SUCCESS  Memory initialisation completed successfully.
          All other error conditions encountered result in an ASSERT.

**/
EFI_STATUS
MemoryInit (
  IN EFI_PEI_SERVICES          **PeiServices
  )
{
  MRC_PARAMS                                 MrcData;
  EFI_BOOT_MODE                               BootMode;
  EFI_STATUS                                  Status;
  EFI_PEI_READ_ONLY_VARIABLE2_PPI             *VariableServices;
  EFI_STATUS_CODE_VALUE                       ErrorCodeValue;
  PEI_QNC_MEMORY_INIT_PPI                     *QncMemoryInitPpi;
  UINT16                                      PmswAdr;

  ErrorCodeValue  = 0;

  //
  // It is critical that both of these data structures are initialized to 0.
  // This PEIM knows the number of DIMMs in the system and works with that
  // information.  The MCH PEIM that consumes these data structures does not
  // know the number of DIMMs so it expects the entire structure to be
  // properly initialized.  By initializing these to zero, all flags indicating
  // that the SPD is present or the row should be configured are set to false.
  //
  ZeroMem (&MrcData, sizeof(MrcData));

  //
  // Get necessary PPI
  //
  Status = PeiServicesLocatePpi (
             &gEfiPeiReadOnlyVariable2PpiGuid,           // GUID
             0,                                          // INSTANCE
             NULL,                                       // EFI_PEI_PPI_DESCRIPTOR
             (VOID **)&VariableServices                  // PPI
             );
  ASSERT_EFI_ERROR (Status);

  //
  // Determine boot mode
  //
  Status = PeiServicesGetBootMode (&BootMode);
  ASSERT_EFI_ERROR (Status);

  //
  // Initialize Error type for reporting status code
  //
  switch (BootMode) {
  case BOOT_ON_FLASH_UPDATE:
    ErrorCodeValue = EFI_COMPUTING_UNIT_MEMORY + EFI_CU_MEMORY_EC_UPDATE_FAIL;
    break;
  case BOOT_ON_S3_RESUME:
    ErrorCodeValue = EFI_COMPUTING_UNIT_MEMORY + EFI_CU_MEMORY_EC_S3_RESUME_FAIL;
    break;
  default:
    ErrorCodeValue = EFI_COMPUTING_UNIT_MEMORY;
    break;
  }

  //
  // Specify MRC boot mode
  //
  switch (BootMode) {
  case BOOT_ON_S3_RESUME:
  case BOOT_ON_FLASH_UPDATE:
    MrcData.boot_mode = bmS3;
    break;
  case BOOT_ASSUMING_NO_CONFIGURATION_CHANGES:
    MrcData.boot_mode = bmFast;
    break;
  default:
    MrcData.boot_mode = bmCold;
    break;
  }

  //
  // Configure MRC input parameters.
  //
  Status = MrcConfigureFromMcFuses (&MrcData);
  ASSERT_EFI_ERROR (Status);
  Status = MrcConfigureFromInfoHob (&MrcData);
  ASSERT_EFI_ERROR (Status);
  MrcUartConfig(&MrcData);

  if (BootMode == BOOT_IN_RECOVERY_MODE) {
    //
    // Always do bmCold on recovery.
    //
    DEBUG ((DEBUG_INFO, "MemoryInit:Force bmCold on Recovery\n"));
    MrcData.boot_mode = bmCold;
  } else {

    //
    // Load Memory configuration data saved in previous boot from variable
    //
    Status = LoadConfig (
               PeiServices,
               VariableServices,
               &MrcData
               );

    if (EFI_ERROR (Status)) {

      switch (BootMode) {
      case BOOT_ON_S3_RESUME:
      case BOOT_ON_FLASH_UPDATE:
        REPORT_STATUS_CODE (
          EFI_ERROR_CODE + EFI_ERROR_UNRECOVERED,
          ErrorCodeValue
        );
        PeiServicesResetSystem ();
        break;

      default:
        MrcData.boot_mode = bmCold;
        break;
      }
    }
  }

  //
  // Locate Memory Reference Code PPI
  //
  Status = PeiServicesLocatePpi (
             &gQNCMemoryInitPpiGuid,        // GUID
             0,                             // INSTANCE
             NULL,                          // EFI_PEI_PPI_DESCRIPTOR
             (VOID **)&QncMemoryInitPpi     // PPI
             );
  ASSERT_EFI_ERROR (Status);

  PmswAdr = (UINT16)(LpcPciCfg32 (R_QNC_LPC_GPE0BLK) & 0xFFFF) + R_QNC_GPE0BLK_PMSW;
  if( IoRead32 (PmswAdr) & B_QNC_GPE0BLK_PMSW_DRAM_INIT) {
    // MRC did not complete last execution, force cold boot path
    MrcData.boot_mode = bmCold;
  }

  // Mark MRC pending
  IoOr32 (PmswAdr, (UINT32)B_QNC_GPE0BLK_PMSW_DRAM_INIT);

  //
  // Call Memory Reference Code's Routines
  //
  QncMemoryInitPpi->MrcStart (&MrcData);

  // Mark MRC completed
  IoAnd32 (PmswAdr, ~(UINT32)B_QNC_GPE0BLK_PMSW_DRAM_INIT);


  //
  // Note emulation platform has to read actual memory size
  // MrcData.mem_size from PcdGet32 (PcdMemorySize);

  if (BootMode == BOOT_ON_S3_RESUME) {

    DEBUG ((EFI_D_INFO, "Following BOOT_ON_S3_RESUME boot path.\n"));

    Status = InstallS3Memory (PeiServices, VariableServices, MrcData.mem_size);
    if (EFI_ERROR (Status)) {
      REPORT_STATUS_CODE (
        EFI_ERROR_CODE + EFI_ERROR_UNRECOVERED,
        ErrorCodeValue
      );
      PeiServicesResetSystem ();
    }
    PostInstallMemory (&MrcData, TRUE);
    return EFI_SUCCESS;
  }

  //
  // Assign physical memory to PEI and DXE
  //
  DEBUG ((EFI_D_INFO, "InstallEfiMemory.\n"));

  Status = InstallEfiMemory (
             PeiServices,
             VariableServices,
             BootMode,
             MrcData.mem_size
             );
  ASSERT_EFI_ERROR (Status);

  PostInstallMemory (&MrcData, FALSE);

  //
  // Save current configuration into Hob and will save into Variable later in DXE
  //
  DEBUG ((EFI_D_INFO, "SaveConfig.\n"));
  Status = SaveConfig (
             &MrcData
             );
  ASSERT_EFI_ERROR (Status);

  DEBUG ((EFI_D_INFO, "MemoryInit Complete.\n"));

  return EFI_SUCCESS;
}

/**

  This function saves a config to a HOB.

  @param  RowInfo         The MCH row configuration information.
  @param  TimingData      Timing data to be saved.
  @param  RowConfArray    Row configuration information for each row in the system.
  @param  SpdData         SPD info read for each DIMM slot in the system.

  @return EFI_SUCCESS:    The function completed successfully.

**/
EFI_STATUS
SaveConfig (
  IN MRC_PARAMS *MrcData
  )
{
  //
  // Build HOB data for Memory Config
  // HOB data size (stored in variable) is required to be multiple of 8 bytes
  //
  BuildGuidDataHob (
    &gEfiMemoryConfigDataGuid,
    (VOID *) &MrcData->timings,
    ((sizeof (MrcData->timings) + 0x7) & (~0x7))
    );

  DEBUG ((EFI_D_INFO, "IIO IoApicBase  = %x IoApicLimit=%x\n", IOAPIC_BASE, (IOAPIC_BASE + IOAPIC_SIZE - 1)));
  DEBUG ((EFI_D_INFO, "IIO RcbaAddress = %x\n", (UINT32)PcdGet64 (PcdRcbaMmioBaseAddress)));

  return EFI_SUCCESS;
}

/**

  Load a configuration stored in a variable.

  @param  TimingData          Timing data to be loaded from NVRAM.
  @param  RowConfArray        Row configuration information for each row in the system.

  @return EFI_SUCCESS         The function completed successfully.
          Other               Could not read variable.

**/
EFI_STATUS
LoadConfig (
  IN      EFI_PEI_SERVICES                        **PeiServices,
  IN      EFI_PEI_READ_ONLY_VARIABLE2_PPI         *VariableServices,
  IN OUT  MRC_PARAMS                              *MrcData
  )
{
  EFI_STATUS                            Status;
  UINTN                                 BufferSize;
  PLATFORM_VARIABLE_MEMORY_CONFIG_DATA  VarData;

  BufferSize = ((sizeof (VarData.timings) + 0x7) & (~0x7));  // HOB data size (stored in variable) is required to be multiple of 8bytes

  Status = VariableServices->GetVariable (
                               VariableServices,
                               EFI_MEMORY_CONFIG_DATA_NAME,
                               &gEfiMemoryConfigDataGuid,
                               NULL,
                               &BufferSize,
                               &VarData.timings
                               );
  if (!EFI_ERROR (Status)) {
    CopyMem (&MrcData->timings, &VarData.timings, sizeof(MrcData->timings));
  }
  return Status;
}

/**

  This function installs memory.

  @param   PeiServices    PEI Services table.
  @param   BootMode       The specific boot path that is being followed
  @param   Mch            Pointer to the DualChannelDdrMemoryInit PPI
  @param   RowConfArray   Row configuration information for each row in the system.

  @return  EFI_SUCCESS            The function completed successfully.
           EFI_INVALID_PARAMETER  One of the input parameters was invalid.
           EFI_ABORTED            An error occurred.

**/
EFI_STATUS
InstallEfiMemory (
  IN      EFI_PEI_SERVICES                           **PeiServices,
  IN      EFI_PEI_READ_ONLY_VARIABLE2_PPI            *VariableServices,
  IN      EFI_BOOT_MODE                              BootMode,
  IN      UINT32                                     TotalMemorySize
  )
{
  EFI_PHYSICAL_ADDRESS                  PeiMemoryBaseAddress;
  EFI_SMRAM_HOB_DESCRIPTOR_BLOCK        *SmramHobDescriptorBlock;
  EFI_STATUS                            Status;
  EFI_PEI_HOB_POINTERS                  Hob;
  PEI_DUAL_CHANNEL_DDR_MEMORY_MAP_RANGE MemoryMap[MAX_RANGES];
  UINT8                                 Index;
  UINT8                                 NumRanges;
  UINT8                                 SmramIndex;
  UINT8                                 SmramRanges;
  UINT64                                PeiMemoryLength;
  UINTN                                 BufferSize;
  UINTN                                 PeiMemoryIndex;
  EFI_RESOURCE_ATTRIBUTE_TYPE           Attribute;
  EFI_PHYSICAL_ADDRESS                  BadMemoryAddress;
  EFI_SMRAM_DESCRIPTOR                  DescriptorAcpiVariable;
  VOID                                  *CapsuleBuffer;
  UINTN                                 CapsuleBufferLength;
  PEI_CAPSULE_PPI                       *Capsule;
  VOID                                  *LargeMemRangeBuf;
  UINTN                                 LargeMemRangeBufLen;
  UINT8                                 MorControl;
  UINTN                                 DataSize;

  //
  // Test the memory from 1M->TOM
  //
  if (BootMode != BOOT_ON_FLASH_UPDATE) {
    Status = BaseMemoryTest (
              PeiServices,
              0x100000,
              (TotalMemorySize - 0x100000),
              Quick,
              &BadMemoryAddress
              );
  ASSERT_EFI_ERROR (Status);
  }


  //
  // Get the Memory Map
  //
  NumRanges = MAX_RANGES;

  ZeroMem (MemoryMap, sizeof (PEI_DUAL_CHANNEL_DDR_MEMORY_MAP_RANGE) * NumRanges);

  Status = GetMemoryMap (
             PeiServices,
             TotalMemorySize,
             (PEI_DUAL_CHANNEL_DDR_MEMORY_MAP_RANGE *) MemoryMap,
             &NumRanges
             );
  ASSERT_EFI_ERROR (Status);

  //
  // Find the highest memory range in processor native address space to give to
  // PEI. Then take the top.
  //
  PeiMemoryBaseAddress = 0;

  //
  // Query the platform for the minimum memory size
  //

  Status = GetPlatformMemorySize (
             PeiServices,
             BootMode,
             &PeiMemoryLength
             );
  ASSERT_EFI_ERROR (Status);

  //
  // Detect MOR request by the OS.
  //
  MorControl = 0;
  DataSize = sizeof (MorControl);
  Status = VariableServices->GetVariable (
                               VariableServices,
                               MEMORY_OVERWRITE_REQUEST_VARIABLE_NAME,
                               &gEfiMemoryOverwriteControlDataGuid,
                               NULL,
                               &DataSize,
                               &MorControl
                               );

  PeiMemoryIndex = 0;

  for (Index = 0; Index < NumRanges; Index++)
  {
    DEBUG ((EFI_D_INFO, "Found 0x%x bytes at ", MemoryMap[Index].RangeLength));
    DEBUG ((EFI_D_INFO, "0x%x.\n", MemoryMap[Index].PhysicalAddress));

    //
    // If OS requested a memory overwrite perform it now.  Only do it for memory
    // used by the OS.
    //
    if (MOR_CLEAR_MEMORY_VALUE (MorControl) && MemoryMap[Index].Type == DualChannelDdrMainMemory) {
      DEBUG ((EFI_D_INFO, "Clear memory per MOR request.\n"));
      if ((UINTN)MemoryMap[Index].RangeLength > 0) {
        if ((UINTN)MemoryMap[Index].PhysicalAddress == 0) {
          //
          // ZeroMem() generates an ASSERT() if Buffer parameter is NULL.
          // Clear byte at 0 and start clear operation at address 1.
          //
          *(UINT8 *)(0) = 0;
          ZeroMem ((VOID *)1, (UINTN)MemoryMap[Index].RangeLength - 1);
        } else {
          ZeroMem (
            (VOID *)(UINTN)MemoryMap[Index].PhysicalAddress,
            (UINTN)MemoryMap[Index].RangeLength
            );
        }
      }
    }

    if ((MemoryMap[Index].Type == DualChannelDdrMainMemory) &&
        (MemoryMap[Index].PhysicalAddress + MemoryMap[Index].RangeLength < MAX_ADDRESS) &&
        (MemoryMap[Index].PhysicalAddress >= PeiMemoryBaseAddress) &&
        (MemoryMap[Index].RangeLength >= PeiMemoryLength)) {
      PeiMemoryBaseAddress = MemoryMap[Index].PhysicalAddress +
                             MemoryMap[Index].RangeLength -
                             PeiMemoryLength;
      PeiMemoryIndex = Index;
    }
  }

  //
  // Find the largest memory range excluding that given to PEI.
  //
  LargeMemRangeBuf = NULL;
  LargeMemRangeBufLen = 0;
  for (Index = 0; Index < NumRanges; Index++) {
    if ((MemoryMap[Index].Type == DualChannelDdrMainMemory) &&
        (MemoryMap[Index].PhysicalAddress + MemoryMap[Index].RangeLength < MAX_ADDRESS)) {
          if (Index != PeiMemoryIndex) {
            if (MemoryMap[Index].RangeLength > LargeMemRangeBufLen) {
              LargeMemRangeBuf = (VOID *) ((UINTN) MemoryMap[Index].PhysicalAddress);
              LargeMemRangeBufLen = (UINTN) MemoryMap[Index].RangeLength;
            }
          } else {
            if ((MemoryMap[Index].RangeLength - PeiMemoryLength) >= LargeMemRangeBufLen) {
              LargeMemRangeBuf = (VOID *) ((UINTN) MemoryMap[Index].PhysicalAddress);
              LargeMemRangeBufLen = (UINTN) (MemoryMap[Index].RangeLength - PeiMemoryLength);
            }
          }
    }
  }

  Capsule             = NULL;
  CapsuleBuffer       = NULL;
  CapsuleBufferLength = 0;
  if (BootMode == BOOT_ON_FLASH_UPDATE) {
    Status = PeiServicesLocatePpi (
               &gPeiCapsulePpiGuid,  // GUID
               0,                    // INSTANCE
               NULL,                 // EFI_PEI_PPI_DESCRIPTOR
               (VOID **)&Capsule     // PPI
               );
    ASSERT_EFI_ERROR (Status);

    if (Status == EFI_SUCCESS) {
      CapsuleBuffer = LargeMemRangeBuf;
      CapsuleBufferLength = LargeMemRangeBufLen;

      //
      // Call the Capsule PPI Coalesce function to coalesce the capsule data.
      //
      Status = Capsule->Coalesce (
                          PeiServices,
                          &CapsuleBuffer,
                          &CapsuleBufferLength
                          );
      //
      // If it failed, then NULL out our capsule PPI pointer so that the capsule
      // HOB does not get created below.
      //
      if (Status != EFI_SUCCESS) {
        Capsule = NULL;
      }
    }
  }

  //
  // Set up the IMR policy required for this platform
  //
  Status = SetPlatformImrPolicy (
              PeiMemoryBaseAddress,
              PeiMemoryLength
              );
  ASSERT_EFI_ERROR (Status);

  //
  // Carve out the top memory reserved for ACPI
  //
  Status        = PeiServicesInstallPeiMemory (PeiMemoryBaseAddress, PeiMemoryLength);
  ASSERT_EFI_ERROR (Status);

  BuildResourceDescriptorHob (
   EFI_RESOURCE_SYSTEM_MEMORY,                       // MemoryType,
   (
   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
   ),
   PeiMemoryBaseAddress,                             // MemoryBegin
   PeiMemoryLength                                   // MemoryLength
   );

  //
  // Install physical memory descriptor hobs for each memory range.
  //
  SmramRanges = 0;
  for (Index = 0; Index < NumRanges; Index++) {
    Attribute = 0;
    if (MemoryMap[Index].Type == DualChannelDdrMainMemory)
    {
      if (Index == PeiMemoryIndex) {
        //
        // This is a partially tested Main Memory range, give it to EFI
        //
        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
          ),
          MemoryMap[Index].PhysicalAddress,
          MemoryMap[Index].RangeLength - PeiMemoryLength
          );
      } else {
        //
        // This is an untested Main Memory range, give it to EFI
        //
        BuildResourceDescriptorHob (
          EFI_RESOURCE_SYSTEM_MEMORY,       // MemoryType,
          (
          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
          ),
          MemoryMap[Index].PhysicalAddress, // MemoryBegin
          MemoryMap[Index].RangeLength      // MemoryLength
          );
      }
    } else {
      if ((MemoryMap[Index].Type == DualChannelDdrSmramCacheable) ||
          (MemoryMap[Index].Type == DualChannelDdrSmramNonCacheable)) {
        SmramRanges++;
      }
      if ((MemoryMap[Index].Type == DualChannelDdrSmramNonCacheable) ||
          (MemoryMap[Index].Type == DualChannelDdrGraphicsMemoryNonCacheable)) {
        Attribute |= EFI_RESOURCE_ATTRIBUTE_UNCACHEABLE;
      }
      if ((MemoryMap[Index].Type == DualChannelDdrSmramCacheable)         ||
          (MemoryMap[Index].Type == DualChannelDdrGraphicsMemoryCacheable)) {
        //
        // TSEG and HSEG can be used with a write-back(WB) cache policy; however,
        // the specification requires that the TSEG and HSEG space be cached only
        // inside of the SMI handler. when using HSEG or TSEG an IA-32 processor
        // does not automatically write back and invalidate its cache before entering
        // SMM or before existing SMM therefore any MTRR defined for the active TSEG
        // or HSEG must be set to un-cacheable(UC) outside of SMM.
        //
        Attribute |= EFI_RESOURCE_ATTRIBUTE_WRITE_BACK_CACHEABLE | EFI_RESOURCE_ATTRIBUTE_UNCACHEABLE;
      }
      if (MemoryMap[Index].Type == DualChannelDdrReservedMemory) {
        Attribute |= EFI_RESOURCE_ATTRIBUTE_WRITE_BACK_CACHEABLE |
                     EFI_RESOURCE_ATTRIBUTE_UNCACHEABLE;
      }
      //
      // Make sure non-system memory is marked as reserved
      //
      BuildResourceDescriptorHob (
        EFI_RESOURCE_MEMORY_RESERVED,     // MemoryType,
        Attribute,                        // MemoryAttribute
        MemoryMap[Index].PhysicalAddress, // MemoryBegin
        MemoryMap[Index].RangeLength      // MemoryLength
        );
    }
  }

  //
  // Allocate one extra EFI_SMRAM_DESCRIPTOR to describe a page of SMRAM memory that contains a pointer
  // to the SMM Services Table that is required on the S3 resume path
  //
  ASSERT (SmramRanges > 0);
  BufferSize = sizeof (EFI_SMRAM_HOB_DESCRIPTOR_BLOCK);
  BufferSize += ((SmramRanges - 1) * sizeof (EFI_SMRAM_DESCRIPTOR));

  Hob.Raw = BuildGuidHob (
              &gEfiSmmPeiSmramMemoryReserveGuid,
              BufferSize
              );
  ASSERT (Hob.Raw);

  SmramHobDescriptorBlock = (EFI_SMRAM_HOB_DESCRIPTOR_BLOCK *) (Hob.Raw);
  SmramHobDescriptorBlock->NumberOfSmmReservedRegions = SmramRanges;

  SmramIndex = 0;
  for (Index = 0; Index < NumRanges; Index++) {
    if ((MemoryMap[Index].Type == DualChannelDdrSmramCacheable) ||
        (MemoryMap[Index].Type == DualChannelDdrSmramNonCacheable)
        ) {
      //
      // This is an SMRAM range, create an SMRAM descriptor
      //
      SmramHobDescriptorBlock->Descriptor[SmramIndex].PhysicalStart = MemoryMap[Index].PhysicalAddress;
      SmramHobDescriptorBlock->Descriptor[SmramIndex].CpuStart      = MemoryMap[Index].CpuAddress;
      SmramHobDescriptorBlock->Descriptor[SmramIndex].PhysicalSize  = MemoryMap[Index].RangeLength;
      if (MemoryMap[Index].Type == DualChannelDdrSmramCacheable) {
        SmramHobDescriptorBlock->Descriptor[SmramIndex].RegionState = EFI_SMRAM_CLOSED | EFI_CACHEABLE;
      } else {
        SmramHobDescriptorBlock->Descriptor[SmramIndex].RegionState = EFI_SMRAM_CLOSED;
      }

      SmramIndex++;
    }
  }

  //
  // Build a HOB with the location of the reserved memory range.
  //
  CopyMem(&DescriptorAcpiVariable, &SmramHobDescriptorBlock->Descriptor[SmramRanges-1], sizeof(EFI_SMRAM_DESCRIPTOR));
  DescriptorAcpiVariable.CpuStart += RESERVED_CPU_S3_SAVE_OFFSET;
  BuildGuidDataHob (
    &gEfiAcpiVariableGuid,
    &DescriptorAcpiVariable,
    sizeof (EFI_SMRAM_DESCRIPTOR)
    );

  //
  // If we found the capsule PPI (and we didn't have errors), then
  // call the capsule PEIM to allocate memory for the capsule.
  //
  if (Capsule != NULL) {
    Status = Capsule->CreateState (PeiServices, CapsuleBuffer, CapsuleBufferLength);
  }

  return EFI_SUCCESS;
}

/**

  Find memory that is reserved so PEI has some to use.

  @param  PeiServices      PEI Services table.
  @param  VariableSevices  Variable PPI instance.

  @return EFI_SUCCESS  The function completed successfully.
                       Error value from LocatePpi()
                       Error Value from VariableServices->GetVariable()

**/
EFI_STATUS
InstallS3Memory (
  IN      EFI_PEI_SERVICES                      **PeiServices,
  IN      EFI_PEI_READ_ONLY_VARIABLE2_PPI       *VariableServices,
  IN      UINT32                                TotalMemorySize
  )
{
  EFI_STATUS                            Status;
  UINTN                                 S3MemoryBase;
  UINTN                                 S3MemorySize;
  UINT8                                 SmramRanges;
  UINT8                                 NumRanges;
  UINT8                                 Index;
  UINT8                                 SmramIndex;
  UINTN                                 BufferSize;
  EFI_PEI_HOB_POINTERS                  Hob;
  EFI_SMRAM_HOB_DESCRIPTOR_BLOCK        *SmramHobDescriptorBlock;
  PEI_DUAL_CHANNEL_DDR_MEMORY_MAP_RANGE MemoryMap[MAX_RANGES];
  RESERVED_ACPI_S3_RANGE                *S3MemoryRangeData;
  EFI_SMRAM_DESCRIPTOR                  DescriptorAcpiVariable;

  //
  // Get the Memory Map
  //
  NumRanges = MAX_RANGES;

  ZeroMem (MemoryMap, sizeof (PEI_DUAL_CHANNEL_DDR_MEMORY_MAP_RANGE) * NumRanges);

  Status = GetMemoryMap (
             PeiServices,
             TotalMemorySize,
             (PEI_DUAL_CHANNEL_DDR_MEMORY_MAP_RANGE *) MemoryMap,
             &NumRanges
             );
  ASSERT_EFI_ERROR (Status);

  //
  // Install physical memory descriptor hobs for each memory range.
  //
  SmramRanges = 0;
  for (Index = 0; Index < NumRanges; Index++) {
    if ((MemoryMap[Index].Type == DualChannelDdrSmramCacheable)    ||
       (MemoryMap[Index].Type == DualChannelDdrSmramNonCacheable)) {
      SmramRanges++;
    }
  }

  ASSERT (SmramRanges > 0);

  //
  // Allocate one extra EFI_SMRAM_DESCRIPTOR to describe a page of SMRAM memory that contains a pointer
  // to the SMM Services Table that is required on the S3 resume path
  //
  BufferSize = sizeof (EFI_SMRAM_HOB_DESCRIPTOR_BLOCK);
  if (SmramRanges > 0) {
    BufferSize += ((SmramRanges - 1) * sizeof (EFI_SMRAM_DESCRIPTOR));
  }

  Hob.Raw = BuildGuidHob (
              &gEfiSmmPeiSmramMemoryReserveGuid,
              BufferSize
              );
  ASSERT (Hob.Raw);

  SmramHobDescriptorBlock = (EFI_SMRAM_HOB_DESCRIPTOR_BLOCK *) (Hob.Raw);
  SmramHobDescriptorBlock->NumberOfSmmReservedRegions = SmramRanges;

  SmramIndex = 0;
  for (Index = 0; Index < NumRanges; Index++) {
    if ((MemoryMap[Index].Type == DualChannelDdrSmramCacheable) ||
        (MemoryMap[Index].Type == DualChannelDdrSmramNonCacheable)
        ) {
      //
      // This is an SMRAM range, create an SMRAM descriptor
      //
      SmramHobDescriptorBlock->Descriptor[SmramIndex].PhysicalStart = MemoryMap[Index].PhysicalAddress;
      SmramHobDescriptorBlock->Descriptor[SmramIndex].CpuStart      = MemoryMap[Index].CpuAddress;
      SmramHobDescriptorBlock->Descriptor[SmramIndex].PhysicalSize  = MemoryMap[Index].RangeLength;
      if (MemoryMap[Index].Type == DualChannelDdrSmramCacheable) {
        SmramHobDescriptorBlock->Descriptor[SmramIndex].RegionState = EFI_SMRAM_CLOSED | EFI_CACHEABLE;
      } else {
        SmramHobDescriptorBlock->Descriptor[SmramIndex].RegionState = EFI_SMRAM_CLOSED;
      }

      SmramIndex++;
    }
  }

  //
  // Build a HOB with the location of the reserved memory range.
  //
  CopyMem(&DescriptorAcpiVariable, &SmramHobDescriptorBlock->Descriptor[SmramRanges-1], sizeof(EFI_SMRAM_DESCRIPTOR));
  DescriptorAcpiVariable.CpuStart += RESERVED_CPU_S3_SAVE_OFFSET;
  BuildGuidDataHob (
    &gEfiAcpiVariableGuid,
    &DescriptorAcpiVariable,
    sizeof (EFI_SMRAM_DESCRIPTOR)
    );

  //
  // Get the location and size of the S3 memory range in the reserved page and
  // install it as PEI Memory.
  //

  DEBUG ((EFI_D_INFO, "TSEG Base = 0x%08x\n", SmramHobDescriptorBlock->Descriptor[SmramRanges-1].PhysicalStart));
  S3MemoryRangeData = (RESERVED_ACPI_S3_RANGE*)(UINTN)
    (SmramHobDescriptorBlock->Descriptor[SmramRanges-1].PhysicalStart + RESERVED_ACPI_S3_RANGE_OFFSET);

  S3MemoryBase  = (UINTN) (S3MemoryRangeData->AcpiReservedMemoryBase);
  DEBUG ((EFI_D_INFO, "S3MemoryBase = 0x%08x\n", S3MemoryBase));
  S3MemorySize  = (UINTN) (S3MemoryRangeData->AcpiReservedMemorySize);
  DEBUG ((EFI_D_INFO, "S3MemorySize = 0x%08x\n", S3MemorySize));

  Status        = PeiServicesInstallPeiMemory (S3MemoryBase, S3MemorySize);
  ASSERT_EFI_ERROR (Status);

  //
  // Retrieve the system memory length and build memory hob for the system
  // memory above 1MB. So Memory Callback can set cache for the system memory
  // correctly on S3 boot path, just like it does on Normal boot path.
  //
  ASSERT ((S3MemoryRangeData->SystemMemoryLength - 0x100000) > 0);
  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
            ),
            0x100000,
            S3MemoryRangeData->SystemMemoryLength - 0x100000
            );

  for (Index = 0; Index < NumRanges; Index++) {
    if ((MemoryMap[Index].Type == DualChannelDdrMainMemory) &&
        (MemoryMap[Index].PhysicalAddress + MemoryMap[Index].RangeLength < 0x100000)) {
      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
        ),
        MemoryMap[Index].PhysicalAddress,
        MemoryMap[Index].RangeLength
        );
      DEBUG ((EFI_D_INFO, "Build resource HOB for Legacy Region on S3 patch :"));
      DEBUG ((EFI_D_INFO, " Memory Base:0x%lX Length:0x%lX\n", MemoryMap[Index].PhysicalAddress, MemoryMap[Index].RangeLength));
    }
  }

  return EFI_SUCCESS;
}

/**

  This function returns the memory ranges to be enabled, along with information
  describing how the range should be used.

  @param  PeiServices   PEI Services Table.
  @param  TimingData    Detected DDR timing parameters for installed memory.
  @param  RowConfArray  Pointer to an array of EFI_DUAL_CHANNEL_DDR_ROW_CONFIG structures. The number
                        of items in the array must match MaxRows returned by the McGetRowInfo() function.
  @param  MemoryMap     Buffer to record details of the memory ranges tobe enabled.
  @param  NumRanges     On input, this contains the maximum number of memory ranges that can be described
                        in the MemoryMap buffer.

  @return MemoryMap     The buffer will be filled in
          NumRanges     will contain the actual number of memory ranges that are to be anabled.
          EFI_SUCCESS   The function completed successfully.

**/
EFI_STATUS
GetMemoryMap (
  IN     EFI_PEI_SERVICES                                    **PeiServices,
  IN     UINT32                                              TotalMemorySize,
  IN OUT PEI_DUAL_CHANNEL_DDR_MEMORY_MAP_RANGE               *MemoryMap,
  IN OUT UINT8                                               *NumRanges
  )
{
  EFI_PHYSICAL_ADDRESS              MemorySize;
  EFI_PHYSICAL_ADDRESS              RowLength;
  EFI_STATUS                        Status;
  PEI_MEMORY_RANGE_PCI_MEMORY       PciMemoryMask;
  PEI_MEMORY_RANGE_OPTION_ROM       OptionRomMask;
  PEI_MEMORY_RANGE_SMRAM            SmramMask;
  PEI_MEMORY_RANGE_SMRAM            TsegMask;
  UINT32                            BlockNum;
  UINT8                             ExtendedMemoryIndex;
  UINT32                            Register;

  if ((*NumRanges) < MAX_RANGES) {
    return EFI_BUFFER_TOO_SMALL;
  }

  *NumRanges = 0;

  //
  // Find out which memory ranges to reserve on this platform
  //
  Status = ChooseRanges (
             &OptionRomMask,
             &SmramMask,
             &PciMemoryMask
             );
  ASSERT_EFI_ERROR (Status);

  //
  // Generate Memory ranges for the memory map.
  //
  MemorySize = 0;

  RowLength = TotalMemorySize;

  //
  // Add memory below 640KB to the memory map. Make sure memory between
  // 640KB and 1MB are reserved, even if not used for SMRAM
  //
  MemoryMap[*NumRanges].PhysicalAddress = MemorySize;
  MemoryMap[*NumRanges].CpuAddress      = MemorySize;
  MemoryMap[*NumRanges].RangeLength     = 0xA0000;
  MemoryMap[*NumRanges].Type            = DualChannelDdrMainMemory;
  (*NumRanges)++;

  //
  // Just mark this range reserved
  //
  MemoryMap[*NumRanges].PhysicalAddress = 0xA0000;
  MemoryMap[*NumRanges].CpuAddress      = 0xA0000;
  MemoryMap[*NumRanges].RangeLength     = 0x60000;
  MemoryMap[*NumRanges].Type            = DualChannelDdrReservedMemory;
  (*NumRanges)++;

  RowLength -= (0x100000 - MemorySize);
  MemorySize = 0x100000;

  //
  // Add remaining memory to the memory map
  //
  MemoryMap[*NumRanges].PhysicalAddress = MemorySize;
  MemoryMap[*NumRanges].CpuAddress      = MemorySize;
  MemoryMap[*NumRanges].RangeLength     = RowLength;
  MemoryMap[*NumRanges].Type            = DualChannelDdrMainMemory;
  (*NumRanges)++;
  MemorySize += RowLength;

  ExtendedMemoryIndex = (UINT8) (*NumRanges - 1);

  // See if we need to trim TSEG out of the highest memory range
  //
  if (SmramMask & PEI_MR_SMRAM_TSEG_MASK) {//pcd
    //
    // Create the new range for TSEG and remove that range from the previous SdrDdrMainMemory range
    //
    TsegMask  = (SmramMask & PEI_MR_SMRAM_SIZE_MASK);

    BlockNum  = 1;
    while (TsegMask) {
      TsegMask >>= 1;
      BlockNum <<= 1;
    }

    BlockNum >>= 1;

    if (BlockNum) {

      MemoryMap[*NumRanges].RangeLength           = (BlockNum * 128 * 1024);
      Register = (UINT32)((MemorySize - 1) & SMM_END_MASK);
      MemorySize                                 -= MemoryMap[*NumRanges].RangeLength;
      MemoryMap[*NumRanges].PhysicalAddress       = MemorySize;
      MemoryMap[*NumRanges].CpuAddress            = MemorySize;
      MemoryMap[ExtendedMemoryIndex].RangeLength -= MemoryMap[*NumRanges].RangeLength;

      //
      // Update QuarkNcSoc HSMMCTL register
      //
      Register |= (UINT32)(((RShiftU64(MemorySize, 16)) & SMM_START_MASK) + (SMM_WRITE_OPEN | SMM_READ_OPEN | SMM_CODE_RD_OPEN));
      QncHsmmcWrite (Register);
    }

    //
    // Chipset only supports cacheable SMRAM
    //
    MemoryMap[*NumRanges].Type = DualChannelDdrSmramCacheable;

    (*NumRanges)++;
  }

  //
  // trim 64K memory from highest memory range for Rmu Main binary shadow
  //
  MemoryMap[*NumRanges].RangeLength           = 0x10000;
  MemorySize                                 -= MemoryMap[*NumRanges].RangeLength;
  MemoryMap[*NumRanges].PhysicalAddress       = MemorySize;
  MemoryMap[*NumRanges].CpuAddress            = MemorySize;
  MemoryMap[ExtendedMemoryIndex].RangeLength -= MemoryMap[*NumRanges].RangeLength;
  MemoryMap[*NumRanges].Type = DualChannelDdrReservedMemory;
  (*NumRanges)++;

  return EFI_SUCCESS;
}

/**

Routine Description:

  Fill in bit masks to specify reserved memory ranges on the Lakeport platform

Arguments:

Returns:

  OptionRomMask - Bit mask specifying memory regions reserved for Legacy option
                  ROM use (if any)

  SmramMask - Bit mask specifying memory regions reserved for SMM use (if any)

**/
EFI_STATUS
ChooseRanges (
  IN OUT   PEI_MEMORY_RANGE_OPTION_ROM           *OptionRomMask,
  IN OUT   PEI_MEMORY_RANGE_SMRAM                *SmramMask,
  IN OUT   PEI_MEMORY_RANGE_PCI_MEMORY           *PciMemoryMask
  )
{

  //
  // Choose regions to reserve for Option ROM use
  //
  *OptionRomMask = PEI_MR_OPTION_ROM_NONE;

  //
  // Choose regions to reserve for SMM use (AB/H SEG and TSEG). Size is in 128K blocks
  //
  *SmramMask = PEI_MR_SMRAM_CACHEABLE_MASK | PEI_MR_SMRAM_TSEG_MASK | ((PcdGet32(PcdTSegSize)) >> 17);

  *PciMemoryMask = 0;

  return EFI_SUCCESS;
}

EFI_STATUS
GetPlatformMemorySize (
  IN       EFI_PEI_SERVICES                       **PeiServices,
  IN       EFI_BOOT_MODE                          BootMode,
  IN OUT   UINT64                                 *MemorySize
  )
{
  EFI_STATUS                            Status;
  EFI_PEI_READ_ONLY_VARIABLE2_PPI       *Variable;
  UINTN                                 DataSize;
  EFI_MEMORY_TYPE_INFORMATION           MemoryData [EfiMaxMemoryType + 1];
  UINTN                                 Index;

  DataSize = sizeof (MemoryData);

  if (BootMode == BOOT_IN_RECOVERY_MODE) {

    //
    // // Treat recovery as if variable not found (eg 1st boot).
    //
    Status = EFI_NOT_FOUND;

  } else {
    Status = PeiServicesLocatePpi (
               &gEfiPeiReadOnlyVariable2PpiGuid,
               0,
               NULL,
               (VOID **)&Variable
               );

    ASSERT_EFI_ERROR (Status);

    DataSize = sizeof (MemoryData);
    Status = Variable->GetVariable (
                         Variable,
                         EFI_MEMORY_TYPE_INFORMATION_VARIABLE_NAME,
                         &gEfiMemoryTypeInformationGuid,
                         NULL,
                         &DataSize,
                         &MemoryData
                         );
  }

  //
  // Accumulate maximum amount of memory needed
  //
  if (EFI_ERROR (Status)) {
    //
    // Start with minimum memory
    //
    *MemorySize = PEI_MIN_MEMORY_SIZE;

    for (Index = 0; Index < sizeof(mDefaultQncMemoryTypeInformation) / sizeof (EFI_MEMORY_TYPE_INFORMATION); Index++) {
        *MemorySize += mDefaultQncMemoryTypeInformation[Index].NumberOfPages * EFI_PAGE_SIZE;
    }

    //
    // Build the GUID'd HOB for DXE
    //
    BuildGuidDataHob (
                 &gEfiMemoryTypeInformationGuid,
                 mDefaultQncMemoryTypeInformation,
                 sizeof(mDefaultQncMemoryTypeInformation)
                 );
  } else {
    //
    // Start with at least PEI_MIN_MEMORY_SIZE pages of memory for the DXE Core and the DXE Stack
    //

    *MemorySize = PEI_MIN_MEMORY_SIZE;
    for (Index = 0; Index < DataSize / sizeof (EFI_MEMORY_TYPE_INFORMATION); Index++) {
      DEBUG ((EFI_D_INFO, "Index %d, Page: %d\n", Index, MemoryData[Index].NumberOfPages));
      *MemorySize += MemoryData[Index].NumberOfPages * EFI_PAGE_SIZE;
    }

    //
    // Build the GUID'd HOB for DXE
    //
    BuildGuidDataHob (
                 &gEfiMemoryTypeInformationGuid,
                 MemoryData,
                 DataSize
                 );

  }

  return EFI_SUCCESS;
}


EFI_STATUS
BaseMemoryTest (
  IN  EFI_PEI_SERVICES                   **PeiServices,
  IN  EFI_PHYSICAL_ADDRESS               BeginAddress,
  IN  UINT64                             MemoryLength,
  IN  PEI_MEMORY_TEST_OP                 Operation,
  OUT EFI_PHYSICAL_ADDRESS               *ErrorAddress
  )
{
  UINT32                TestPattern;
  EFI_PHYSICAL_ADDRESS  TempAddress;
  UINT32                SpanSize;

  TestPattern = 0x5A5A5A5A;
  SpanSize    = 0;

  //
  // Make sure we don't try and test anything above the max physical address range
  //
  ASSERT (BeginAddress + MemoryLength < MAX_ADDRESS);

  switch (Operation) {
  case Extensive:
    SpanSize = 0x4;
    break;

  case Sparse:
  case Quick:
    SpanSize = 0x40000;
    break;

  case Ignore:
    goto Done;
    break;
  }
  //
  // Write the test pattern into memory range
  //
  TempAddress = BeginAddress;
  while (TempAddress < BeginAddress + MemoryLength) {
    (*(UINT32 *) (UINTN) TempAddress) = TestPattern;
    TempAddress += SpanSize;
  }
  //
  // Read pattern from memory and compare it
  //
  TempAddress = BeginAddress;
  while (TempAddress < BeginAddress + MemoryLength) {
    if ((*(UINT32 *) (UINTN) TempAddress) != TestPattern) {
      *ErrorAddress = TempAddress;
      DEBUG ((EFI_D_ERROR, "Memory test failed at 0x%x.\n", TempAddress));
      return EFI_DEVICE_ERROR;
    }

    TempAddress += SpanSize;
  }

Done:
  return EFI_SUCCESS;
}

/**

  This function sets up the platform specific IMR protection for the various
  memory regions.

  @param  PeiMemoryBaseAddress  Base address of memory allocated for PEI.
  @param  PeiMemoryLength       Length in bytes of the PEI memory (includes ACPI memory).

  @return EFI_SUCCESS           The function completed successfully.
          EFI_ACCESS_DENIED     Access to IMRs failed.

**/
EFI_STATUS
SetPlatformImrPolicy (
  IN      EFI_PHYSICAL_ADDRESS    PeiMemoryBaseAddress,
  IN      UINT64                  PeiMemoryLength
  )
{
  UINT8         Index;
  UINT32        Register;
  UINT16        DeviceId;

  //
  // Check what Soc we are running on (read Host bridge DeviceId)
  //
  DeviceId = QNCMmPci16(0, MC_BUS, MC_DEV, MC_FUN, PCI_DEVICE_ID_OFFSET);

  //
  // If any IMR register is locked then we cannot proceed
  //
  for (Index = (QUARK_NC_MEMORY_MANAGER_IMR0+QUARK_NC_MEMORY_MANAGER_IMRXL); Index <=(QUARK_NC_MEMORY_MANAGER_IMR7+QUARK_NC_MEMORY_MANAGER_IMRXL); Index=Index+4)
  {
    Register = QNCPortRead (QUARK_NC_MEMORY_MANAGER_SB_PORT_ID, Index);
    if (Register & IMR_LOCK) {
      return EFI_ACCESS_DENIED;
    }
  }

  //
  // Add IMR2 protection for shadowed RMU binary.
  //
  QncImrWrite (
            QUARK_NC_MEMORY_MANAGER_IMR2,
            (UINT32)(((RShiftU64((PeiMemoryBaseAddress+PeiMemoryLength), 8)) & IMRH_MASK) | IMR_EN),
            (UINT32)((RShiftU64((PeiMemoryBaseAddress+PeiMemoryLength+PcdGet32(PcdFlashQNCMicrocodeSize)-1), 8)) & IMRH_MASK),
            (UINT32)(CPU_SNOOP + RMU + CPU0_NON_SMM),
            (UINT32)(CPU_SNOOP + RMU + CPU0_NON_SMM)
        );

  //
  // Add IMR3 protection for the default SMRAM.
  //
  QncImrWrite (
            QUARK_NC_MEMORY_MANAGER_IMR3,
            (UINT32)(((RShiftU64((SMM_DEFAULT_SMBASE), 8)) & IMRL_MASK) | IMR_EN),
            (UINT32)((RShiftU64((SMM_DEFAULT_SMBASE+SMM_DEFAULT_SMBASE_SIZE_BYTES-1), 8)) & IMRH_MASK),
            (UINT32)(CPU_SNOOP + CPU0_NON_SMM),
            (UINT32)(CPU_SNOOP + CPU0_NON_SMM)
        );

  //
  // Enable IMR4 protection of eSRAM.
  //
  QncImrWrite (
            QUARK_NC_MEMORY_MANAGER_IMR4,
            (UINT32)(((RShiftU64((UINTN)PcdGet32 (PcdEsramStage1Base), 8)) & IMRL_MASK) | IMR_EN),
            (UINT32)((RShiftU64(((UINTN)PcdGet32 (PcdEsramStage1Base) + (UINTN)PcdGet32 (PcdESramMemorySize) - 1), 8)) & IMRH_MASK),
            (UINT32)(CPU_SNOOP + CPU0_NON_SMM),
            (UINT32)(CPU_SNOOP + CPU0_NON_SMM)
        );

  //
  // Enable Interrupt on IMR/SMM Violation
  //
  QNCPortWrite (QUARK_NC_MEMORY_MANAGER_SB_PORT_ID, QUARK_NC_MEMORY_MANAGER_BIMRVCTL, (UINT32)(EnableIMRInt));
  if (DeviceId == QUARK2_MC_DEVICE_ID) {
    QNCPortWrite (QUARK_NC_MEMORY_MANAGER_SB_PORT_ID, QUARK_NC_MEMORY_MANAGER_BSMMVCTL, (UINT32)(EnableSMMInt));
  }

  //
  // Disable IMR7 memory protection (eSRAM + DDR3 memory) since our policies
  // are now setup.
  //
  QncImrWrite (
            QUARK_NC_MEMORY_MANAGER_IMR7,
            (UINT32)(IMRL_RESET & ~IMR_EN),
            (UINT32)IMRH_RESET,
            (UINT32)IMRX_ALL_ACCESS,
            (UINT32)IMRX_ALL_ACCESS
        );

  return EFI_SUCCESS;
}

/** Return info derived from Installing Memory by MemoryInit.

  @param[out]      RmuMainBaseAddressPtr   Return RmuMainBaseAddress to this location.
  @param[out]      SmramDescriptorPtr  Return start of Smram descriptor list to this location.
  @param[out]      NumSmramRegionsPtr  Return numbers of Smram regions to this location.

  @return Address of RMU shadow region at the top of available memory.
  @return List of Smram descriptors for each Smram region.
  @return Numbers of Smram regions.
**/
VOID
EFIAPI
InfoPostInstallMemory (
  OUT     UINT32                    *RmuMainBaseAddressPtr OPTIONAL,
  OUT     EFI_SMRAM_DESCRIPTOR      **SmramDescriptorPtr OPTIONAL,
  OUT     UINTN                     *NumSmramRegionsPtr OPTIONAL
  )
{
  EFI_STATUS                            Status;
  EFI_PEI_HOB_POINTERS                  Hob;
  UINT64                                CalcLength;
  EFI_SMRAM_HOB_DESCRIPTOR_BLOCK        *SmramHobDescriptorBlock;

  if ((RmuMainBaseAddressPtr == NULL) && (SmramDescriptorPtr == NULL) && (NumSmramRegionsPtr == NULL)) {
    return;
  }

  SmramHobDescriptorBlock = NULL;
  if (SmramDescriptorPtr != NULL) {
    *SmramDescriptorPtr = NULL;
  }
  if (NumSmramRegionsPtr != NULL) {
    *NumSmramRegionsPtr = 0;
  }

  //
  // Calculate RMU shadow region base address.
  // Set to 1 MB. Since 1MB cacheability will always be set
  // until override by CSM.
  //
  CalcLength = 0x100000;

  Status = PeiServicesGetHobList ((VOID **) &Hob.Raw);
  ASSERT_EFI_ERROR (Status);
  while (!END_OF_HOB_LIST (Hob)) {
    if (Hob.Header->HobType == EFI_HOB_TYPE_RESOURCE_DESCRIPTOR) {
      if (Hob.ResourceDescriptor->ResourceType == EFI_RESOURCE_SYSTEM_MEMORY) {
        //
        // Skip the memory region below 1MB
        //
        if (Hob.ResourceDescriptor->PhysicalStart >= 0x100000) {
          CalcLength += (UINT64) (Hob.ResourceDescriptor->ResourceLength);
        }
      }
    } else if (Hob.Header->HobType == EFI_HOB_TYPE_GUID_EXTENSION) {
      if (CompareGuid (&(Hob.Guid->Name), &gEfiSmmPeiSmramMemoryReserveGuid)) {
        SmramHobDescriptorBlock = (VOID*) (Hob.Raw + sizeof (EFI_HOB_GUID_TYPE));
        if (SmramDescriptorPtr != NULL) {
          *SmramDescriptorPtr = SmramHobDescriptorBlock->Descriptor;
        }
        if (NumSmramRegionsPtr != NULL) {
          *NumSmramRegionsPtr = SmramHobDescriptorBlock->NumberOfSmmReservedRegions;
        }
      }
    }
    Hob.Raw = GET_NEXT_HOB (Hob);
  }

  if (RmuMainBaseAddressPtr != NULL) {
    *RmuMainBaseAddressPtr = (UINT32) CalcLength;
  }
}