/** @file

  Copyright (c) 2008 - 2009, Apple Inc. All rights reserved.<BR>

  SPDX-License-Identifier: BSD-2-Clause-Patent

**/

#include <PiPei.h>
#include <Library/BaseMemoryLib.h>
#include <Library/DebugLib.h>
#include <Library/ExtractGuidedSectionLib.h>
#include <Library/PcdLib.h>
#include <Library/PrePiLib.h>

#define PRE_PI_EXTRACT_GUIDED_SECTION_DATA_GUID  { 0x385A982C, 0x2F49, 0x4043, { 0xA5, 0x1E, 0x49, 0x01, 0x02, 0x5C, 0x8B, 0x6B }}

typedef struct {
  UINT32                                     NumberOfExtractHandler;
  GUID                                       *ExtractHandlerGuidTable;
  EXTRACT_GUIDED_SECTION_DECODE_HANDLER      *ExtractDecodeHandlerTable;
  EXTRACT_GUIDED_SECTION_GET_INFO_HANDLER    *ExtractGetInfoHandlerTable;
} PRE_PI_EXTRACT_GUIDED_SECTION_DATA;

PRE_PI_EXTRACT_GUIDED_SECTION_DATA *
GetSavedData (
  VOID
  )
{
  EFI_HOB_GUID_TYPE  *GuidHob;
  GUID               SavedDataGuid = PRE_PI_EXTRACT_GUIDED_SECTION_DATA_GUID;

  GuidHob = GetFirstGuidHob (&SavedDataGuid);
  GuidHob++;

  return (PRE_PI_EXTRACT_GUIDED_SECTION_DATA *)GuidHob;
}

RETURN_STATUS
EFIAPI
ExtractGuidedSectionRegisterHandlers (
  IN CONST  GUID                                     *SectionGuid,
  IN        EXTRACT_GUIDED_SECTION_GET_INFO_HANDLER  GetInfoHandler,
  IN        EXTRACT_GUIDED_SECTION_DECODE_HANDLER    DecodeHandler
  )
{
  PRE_PI_EXTRACT_GUIDED_SECTION_DATA  *SavedData;
  UINT32                              Index;

  //
  // Check input parameter.
  //
  if (SectionGuid == NULL) {
    return RETURN_INVALID_PARAMETER;
  }

  SavedData = GetSavedData ();

  //
  // Search the match registered GetInfo handler for the input guided section.
  //
  for (Index = 0; Index < SavedData->NumberOfExtractHandler; Index++) {
    if (CompareGuid (&SavedData->ExtractHandlerGuidTable[Index], SectionGuid)) {
      break;
    }
  }

  //
  // If the guided handler has been registered before, only update its handler.
  //
  if (Index < SavedData->NumberOfExtractHandler) {
    SavedData->ExtractDecodeHandlerTable[Index]  = DecodeHandler;
    SavedData->ExtractGetInfoHandlerTable[Index] = GetInfoHandler;
    return RETURN_SUCCESS;
  }

  //
  // Check the global table is enough to contain new Handler.
  //
  if (SavedData->NumberOfExtractHandler >= PcdGet32 (PcdMaximumGuidedExtractHandler)) {
    return RETURN_OUT_OF_RESOURCES;
  }

  //
  // Register new Handler and guid value.
  //
  CopyGuid (&SavedData->ExtractHandlerGuidTable[SavedData->NumberOfExtractHandler], SectionGuid);
  SavedData->ExtractDecodeHandlerTable[SavedData->NumberOfExtractHandler]    = DecodeHandler;
  SavedData->ExtractGetInfoHandlerTable[SavedData->NumberOfExtractHandler++] = GetInfoHandler;

  return RETURN_SUCCESS;
}

UINTN
EFIAPI
ExtractGuidedSectionGetGuidList (
  IN OUT  GUID  **ExtractHandlerGuidTable
  )
{
  PRE_PI_EXTRACT_GUIDED_SECTION_DATA  *SavedData;

  ASSERT (ExtractHandlerGuidTable != NULL);

  SavedData = GetSavedData ();

  *ExtractHandlerGuidTable = SavedData->ExtractHandlerGuidTable;
  return SavedData->NumberOfExtractHandler;
}

RETURN_STATUS
EFIAPI
ExtractGuidedSectionGetInfo (
  IN  CONST VOID    *InputSection,
  OUT       UINT32  *OutputBufferSize,
  OUT       UINT32  *ScratchBufferSize,
  OUT       UINT16  *SectionAttribute
  )
{
  PRE_PI_EXTRACT_GUIDED_SECTION_DATA  *SavedData;
  UINT32                              Index;
  EFI_GUID                            *SectionDefinitionGuid;

  if (InputSection == NULL) {
    return RETURN_INVALID_PARAMETER;
  }

  ASSERT (OutputBufferSize != NULL);
  ASSERT (ScratchBufferSize != NULL);
  ASSERT (SectionAttribute != NULL);

  SavedData = GetSavedData ();

  if (IS_SECTION2 (InputSection)) {
    SectionDefinitionGuid = &(((EFI_GUID_DEFINED_SECTION2 *)InputSection)->SectionDefinitionGuid);
  } else {
    SectionDefinitionGuid = &(((EFI_GUID_DEFINED_SECTION *)InputSection)->SectionDefinitionGuid);
  }

  //
  // Search the match registered GetInfo handler for the input guided section.
  //
  for (Index = 0; Index < SavedData->NumberOfExtractHandler; Index++) {
    if (CompareGuid (&SavedData->ExtractHandlerGuidTable[Index], SectionDefinitionGuid)) {
      break;
    }
  }

  //
  // Not found, the input guided section is not supported.
  //
  if (Index == SavedData->NumberOfExtractHandler) {
    return RETURN_INVALID_PARAMETER;
  }

  //
  // Call the match handler to getinfo for the input section data.
  //
  return SavedData->ExtractGetInfoHandlerTable[Index](
                                                      InputSection,
                                                      OutputBufferSize,
                                                      ScratchBufferSize,
                                                      SectionAttribute
                                                      );
}

RETURN_STATUS
EFIAPI
ExtractGuidedSectionDecode (
  IN  CONST VOID    *InputSection,
  OUT       VOID    **OutputBuffer,
  OUT       VOID    *ScratchBuffer         OPTIONAL,
  OUT       UINT32  *AuthenticationStatus
  )
{
  PRE_PI_EXTRACT_GUIDED_SECTION_DATA  *SavedData;
  UINT32                              Index;
  EFI_GUID                            *SectionDefinitionGuid;

  if (InputSection == NULL) {
    return RETURN_INVALID_PARAMETER;
  }

  ASSERT (OutputBuffer != NULL);
  ASSERT (AuthenticationStatus != NULL);

  SavedData = GetSavedData ();

  if (IS_SECTION2 (InputSection)) {
    SectionDefinitionGuid = &(((EFI_GUID_DEFINED_SECTION2 *)InputSection)->SectionDefinitionGuid);
  } else {
    SectionDefinitionGuid = &(((EFI_GUID_DEFINED_SECTION *)InputSection)->SectionDefinitionGuid);
  }

  //
  // Search the match registered GetInfo handler for the input guided section.
  //
  for (Index = 0; Index < SavedData->NumberOfExtractHandler; Index++) {
    if (CompareGuid (&SavedData->ExtractHandlerGuidTable[Index], SectionDefinitionGuid)) {
      break;
    }
  }

  //
  // Not found, the input guided section is not supported.
  //
  if (Index == SavedData->NumberOfExtractHandler) {
    return RETURN_INVALID_PARAMETER;
  }

  //
  // Call the match handler to getinfo for the input section data.
  //
  return SavedData->ExtractDecodeHandlerTable[Index](
                                                     InputSection,
                                                     OutputBuffer,
                                                     ScratchBuffer,
                                                     AuthenticationStatus
                                                     );
}

RETURN_STATUS
EFIAPI
ExtractGuidedSectionLibConstructor (
  VOID
  )
{
  PRE_PI_EXTRACT_GUIDED_SECTION_DATA  SavedData;
  GUID                                HobGuid = PRE_PI_EXTRACT_GUIDED_SECTION_DATA_GUID;

  //
  // Allocate global pool space to store the registered handler and its guid value.
  //
  SavedData.ExtractHandlerGuidTable = (GUID *)AllocatePool (PcdGet32 (PcdMaximumGuidedExtractHandler) * sizeof (GUID));
  if (SavedData.ExtractHandlerGuidTable == NULL) {
    return RETURN_OUT_OF_RESOURCES;
  }

  SavedData.ExtractDecodeHandlerTable = (EXTRACT_GUIDED_SECTION_DECODE_HANDLER *)AllocatePool (PcdGet32 (PcdMaximumGuidedExtractHandler) * sizeof (EXTRACT_GUIDED_SECTION_DECODE_HANDLER));
  if (SavedData.ExtractDecodeHandlerTable == NULL) {
    return RETURN_OUT_OF_RESOURCES;
  }

  SavedData.ExtractGetInfoHandlerTable = (EXTRACT_GUIDED_SECTION_GET_INFO_HANDLER *)AllocatePool (PcdGet32 (PcdMaximumGuidedExtractHandler) * sizeof (EXTRACT_GUIDED_SECTION_GET_INFO_HANDLER));
  if (SavedData.ExtractGetInfoHandlerTable == NULL) {
    return RETURN_OUT_OF_RESOURCES;
  }

  //
  // the initialized number is Zero.
  //
  SavedData.NumberOfExtractHandler = 0;

  BuildGuidDataHob (&HobGuid, &SavedData, sizeof (SavedData));

  return RETURN_SUCCESS;
}
