/** @file
  This module provide Hand-Off Block manupulation.

Copyright (c) 2006 - 2018, Intel Corporation. All rights reserved.<BR>
SPDX-License-Identifier: BSD-2-Clause-Patent

**/

#include "PeiMain.h"

/**

  Gets the pointer to the HOB List.

  @param PeiServices                   An indirect pointer to the EFI_PEI_SERVICES table published by the PEI Foundation.
  @param HobList                       Pointer to the HOB List.

  @retval EFI_SUCCESS                  Get the pointer of HOB List
  @retval EFI_NOT_AVAILABLE_YET        the HOB List is not yet published
  @retval EFI_INVALID_PARAMETER        HobList is NULL (in debug mode)

**/
EFI_STATUS
EFIAPI
PeiGetHobList (
  IN CONST EFI_PEI_SERVICES  **PeiServices,
  IN OUT VOID          **HobList
  )
{
  PEI_CORE_INSTANCE *PrivateData;

  //
  // Only check this parameter in debug mode
  //

  DEBUG_CODE_BEGIN ();
    if (HobList == NULL) {
      return EFI_INVALID_PARAMETER;
    }
  DEBUG_CODE_END ();

  PrivateData = PEI_CORE_INSTANCE_FROM_PS_THIS(PeiServices);

  *HobList    = PrivateData->HobList.Raw;

  return EFI_SUCCESS;
}


/**
  Add a new HOB to the HOB List.

  @param PeiServices      An indirect pointer to the EFI_PEI_SERVICES table published by the PEI Foundation.
  @param Type             Type of the new HOB.
  @param Length           Length of the new HOB to allocate.
  @param Hob              Pointer to the new HOB.

  @return  EFI_SUCCESS           Success to create hob.
  @retval  EFI_INVALID_PARAMETER if Hob is NULL
  @retval  EFI_NOT_AVAILABLE_YET if HobList is still not available.
  @retval  EFI_OUT_OF_RESOURCES  if there is no more memory to grow the Hoblist.

**/
EFI_STATUS
EFIAPI
PeiCreateHob (
  IN CONST EFI_PEI_SERVICES  **PeiServices,
  IN UINT16            Type,
  IN UINT16            Length,
  IN OUT VOID          **Hob
  )
{
  EFI_STATUS                           Status;
  EFI_HOB_HANDOFF_INFO_TABLE           *HandOffHob;
  EFI_HOB_GENERIC_HEADER               *HobEnd;
  EFI_PHYSICAL_ADDRESS                 FreeMemory;


  Status = PeiGetHobList (PeiServices, Hob);
  if (EFI_ERROR(Status)) {
    return Status;
  }

  HandOffHob = *Hob;

  //
  // Check Length to avoid data overflow.
  //
  if (0x10000 - Length <= 0x7) {
    return EFI_INVALID_PARAMETER;
  }
  Length     = (UINT16)((Length + 0x7) & (~0x7));

  FreeMemory = HandOffHob->EfiFreeMemoryTop -
               HandOffHob->EfiFreeMemoryBottom;

  if (FreeMemory < Length) {
    DEBUG ((EFI_D_ERROR, "PeiCreateHob fail: Length - 0x%08x\n", (UINTN)Length));
    DEBUG ((EFI_D_ERROR, "  FreeMemoryTop    - 0x%08x\n", (UINTN)HandOffHob->EfiFreeMemoryTop));
    DEBUG ((EFI_D_ERROR, "  FreeMemoryBottom - 0x%08x\n", (UINTN)HandOffHob->EfiFreeMemoryBottom));
    return EFI_OUT_OF_RESOURCES;
  }

  *Hob = (VOID*) (UINTN) HandOffHob->EfiEndOfHobList;
  ((EFI_HOB_GENERIC_HEADER*) *Hob)->HobType   = Type;
  ((EFI_HOB_GENERIC_HEADER*) *Hob)->HobLength = Length;
  ((EFI_HOB_GENERIC_HEADER*) *Hob)->Reserved  = 0;

  HobEnd = (EFI_HOB_GENERIC_HEADER*) ((UINTN) *Hob + Length);
  HandOffHob->EfiEndOfHobList = (EFI_PHYSICAL_ADDRESS) (UINTN) HobEnd;

  HobEnd->HobType   = EFI_HOB_TYPE_END_OF_HOB_LIST;
  HobEnd->HobLength = (UINT16) sizeof (EFI_HOB_GENERIC_HEADER);
  HobEnd->Reserved  = 0;
  HobEnd++;
  HandOffHob->EfiFreeMemoryBottom = (EFI_PHYSICAL_ADDRESS) (UINTN) HobEnd;

  return EFI_SUCCESS;
}

/**
  Install SEC HOB data to the HOB List.

  @param PeiServices    An indirect pointer to the EFI_PEI_SERVICES table published by the PEI Foundation.
  @param SecHobList     Pointer to SEC HOB List.

  @return EFI_SUCCESS           Success to install SEC HOB data.
  @retval EFI_OUT_OF_RESOURCES  If there is no more memory to grow the Hoblist.

**/
EFI_STATUS
PeiInstallSecHobData (
  IN CONST EFI_PEI_SERVICES     **PeiServices,
  IN EFI_HOB_GENERIC_HEADER     *SecHobList
  )
{
  EFI_STATUS                    Status;
  EFI_HOB_HANDOFF_INFO_TABLE    *HandOffHob;
  EFI_PEI_HOB_POINTERS          HobStart;
  EFI_PEI_HOB_POINTERS          Hob;
  UINTN                         SecHobListLength;
  EFI_PHYSICAL_ADDRESS          FreeMemory;
  EFI_HOB_GENERIC_HEADER        *HobEnd;

  HandOffHob = NULL;
  Status = PeiGetHobList (PeiServices, (VOID **) &HandOffHob);
  if (EFI_ERROR(Status)) {
    return Status;
  }
  ASSERT (HandOffHob != NULL);

  HobStart.Raw = (UINT8 *) SecHobList;
  //
  // The HobList must not contain a EFI_HOB_HANDOFF_INFO_TABLE HOB (PHIT) HOB.
  //
  ASSERT (HobStart.Header->HobType != EFI_HOB_TYPE_HANDOFF);
  //
  // Calculate the SEC HOB List length,
  // not including the terminated HOB(EFI_HOB_TYPE_END_OF_HOB_LIST).
  //
  for (Hob.Raw = HobStart.Raw; !END_OF_HOB_LIST (Hob); Hob.Raw = GET_NEXT_HOB (Hob));
  SecHobListLength = (UINTN) Hob.Raw - (UINTN) HobStart.Raw;
  //
  // The length must be 8-bytes aligned.
  //
  ASSERT ((SecHobListLength & 0x7) == 0);

  FreeMemory = HandOffHob->EfiFreeMemoryTop -
               HandOffHob->EfiFreeMemoryBottom;

  if (FreeMemory < SecHobListLength) {
    DEBUG ((DEBUG_ERROR, "PeiInstallSecHobData fail: SecHobListLength - 0x%08x\n", SecHobListLength));
    DEBUG ((DEBUG_ERROR, "  FreeMemoryTop    - 0x%08x\n", (UINTN)HandOffHob->EfiFreeMemoryTop));
    DEBUG ((DEBUG_ERROR, "  FreeMemoryBottom - 0x%08x\n", (UINTN)HandOffHob->EfiFreeMemoryBottom));
    return EFI_OUT_OF_RESOURCES;
  }

  Hob.Raw = (UINT8 *) (UINTN) HandOffHob->EfiEndOfHobList;
  CopyMem (Hob.Raw, HobStart.Raw, SecHobListLength);

  HobEnd = (EFI_HOB_GENERIC_HEADER *) ((UINTN) Hob.Raw + SecHobListLength);
  HandOffHob->EfiEndOfHobList = (EFI_PHYSICAL_ADDRESS) (UINTN) HobEnd;

  HobEnd->HobType   = EFI_HOB_TYPE_END_OF_HOB_LIST;
  HobEnd->HobLength = (UINT16) sizeof (EFI_HOB_GENERIC_HEADER);
  HobEnd->Reserved  = 0;
  HobEnd++;
  HandOffHob->EfiFreeMemoryBottom = (EFI_PHYSICAL_ADDRESS) (UINTN) HobEnd;

  return EFI_SUCCESS;
}

/**

  Builds a Handoff Information Table HOB

  @param BootMode        - Current Bootmode
  @param MemoryBegin     - Start Memory Address.
  @param MemoryLength    - Length of Memory.

  @return EFI_SUCCESS Always success to initialize HOB.

**/
EFI_STATUS
PeiCoreBuildHobHandoffInfoTable (
  IN EFI_BOOT_MODE         BootMode,
  IN EFI_PHYSICAL_ADDRESS  MemoryBegin,
  IN UINT64                MemoryLength
  )
{
  EFI_HOB_HANDOFF_INFO_TABLE   *Hob;
  EFI_HOB_GENERIC_HEADER       *HobEnd;

  Hob                      = (VOID *)(UINTN)MemoryBegin;
  HobEnd                   = (EFI_HOB_GENERIC_HEADER*) (Hob+1);
  Hob->Header.HobType      = EFI_HOB_TYPE_HANDOFF;
  Hob->Header.HobLength    = (UINT16) sizeof (EFI_HOB_HANDOFF_INFO_TABLE);
  Hob->Header.Reserved     = 0;

  HobEnd->HobType          = EFI_HOB_TYPE_END_OF_HOB_LIST;
  HobEnd->HobLength        = (UINT16) sizeof (EFI_HOB_GENERIC_HEADER);
  HobEnd->Reserved         = 0;

  Hob->Version             = EFI_HOB_HANDOFF_TABLE_VERSION;
  Hob->BootMode            = BootMode;

  Hob->EfiMemoryTop        = MemoryBegin + MemoryLength;
  Hob->EfiMemoryBottom     = MemoryBegin;
  Hob->EfiFreeMemoryTop    = MemoryBegin + MemoryLength;
  Hob->EfiFreeMemoryBottom = (EFI_PHYSICAL_ADDRESS) (UINTN) (HobEnd + 1);
  Hob->EfiEndOfHobList     = (EFI_PHYSICAL_ADDRESS) (UINTN) HobEnd;

  return EFI_SUCCESS;
}
