text: /** @file
  
  Copyright (c) 2017, Intel Corporation. All rights reserved.<BR>
  This program and the accompanying materials are licensed and made available under
  the terms and conditions of the BSD License that 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 <PiPei.h>
  #include <Library/PeiServicesTablePointerLib.h>
  #include <Library/BaseMemoryLib.h>
  #include <Library/DebugLib.h>
  #include <Library/HobVariableLib.h>
  #include <Library/HobLib.h>
  #include <Ppi/MemoryDiscovered.h>
  #include "Variable.h"
  #include "Fce.h"
  
  extern EFI_PEI_NOTIFY_DESCRIPTOR mMemoryNotifyList;
  
  /**
    This function finds the matched default data and create GUID hob for it. 
    
    @param StoreId  Specifies the type of defaults to retrieve.
    @param SkuId    Specifies the platform board of defaults to retrieve.
  
    @retval EFI_SUCCESS           The matched default data is found.
    @retval EFI_NOT_FOUND         The matched default data is not found.
    @retval EFI_OUT_OF_RESOURCES  No enough resource to create HOB.
  
  **/
  EFI_STATUS
  EFIAPI
  CreateDefaultVariableHob (
    IN UINT16  StoreId,
    IN UINT16  SkuId
    )
  {
    UINTN                      FvInstance;
    EFI_FIRMWARE_VOLUME_HEADER *FvHeader;
    EFI_FFS_FILE_HEADER        *FfsHeader;
    UINT32                     FileSize;
    EFI_COMMON_SECTION_HEADER  *Section;
    UINT32                     SectionLength;
    EFI_STATUS                 Status;
    BOOLEAN                    DefaultSettingIsFound;
    DEFAULT_DATA               *DefaultData;
    DEFAULT_INFO               *DefaultInfo;
    VARIABLE_STORE_HEADER      *VarStoreHeader;
    VARIABLE_STORE_HEADER      *VarStoreHeaderHob;
    UINT8                      *VarHobPtr;
    UINT8                      *VarPtr;
    UINT32                     VarDataOffset;
    UINT32                     VarHobDataOffset;
    EFI_BOOT_MODE              BootMode;
    BOOLEAN                    IsFirstSection;
    DATA_DELTA                 *DataDelta;
    UINTN                      DataDeltaSize;
    UINTN                      Index;
    CONST EFI_PEI_SERVICES     **PeiServices;
  
    //
    // Get PeiService pointer
    //
    PeiServices = GetPeiServicesTablePointer ();
  
    //
    // Find the FFS file that stores all default data.
    //
    DefaultSettingIsFound = FALSE;
    FvInstance            = 0;
    FfsHeader             = NULL;
    while (((*PeiServices)->FfsFindNextVolume (PeiServices, FvInstance, (VOID **) &FvHeader) == EFI_SUCCESS) &&
           (!DefaultSettingIsFound)) {
      FfsHeader = NULL;
      while ((*PeiServices)->FfsFindNextFile (PeiServices, EFI_FV_FILETYPE_FREEFORM, FvHeader, (VOID **) &FfsHeader) == EFI_SUCCESS) {
        if (CompareGuid ((EFI_GUID *) FfsHeader, &gDefaultDataOptSizeFileGuid)) {
          DefaultSettingIsFound = TRUE;
          break;
        }
      }
      FvInstance ++;
    }
  
    //
    // FFS file is not found.
    //
    if (!DefaultSettingIsFound) {
      return EFI_NOT_FOUND;
    }
  
    //
    // Find the matched default data for the input default ID and plat ID.
    //
    DefaultSettingIsFound = FALSE;
    VarStoreHeaderHob     = NULL;
    VarHobPtr      = NULL;
    DataDelta      = NULL;
    DataDeltaSize  = 0;
    IsFirstSection = TRUE;
    VarStoreHeader = NULL;
    Section  = (EFI_COMMON_SECTION_HEADER *)(FfsHeader + 1);
    FileSize = *(UINT32 *)(FfsHeader->Size) & 0x00FFFFFF;
    while (((UINTN) Section < (UINTN) FfsHeader + FileSize) && !DefaultSettingIsFound) {
      DefaultData = (DEFAULT_DATA *) (Section + 1);
      DefaultInfo   = &(DefaultData->DefaultInfo[0]);
      SectionLength = *(UINT32 *)Section->Size & 0x00FFFFFF;
  
      if (IsFirstSection) {
        //
        // Create HOB to store default data so that Variable driver can use it.
        // Allocate more data for header alignment.
        //
        VarStoreHeader = (VARIABLE_STORE_HEADER *) ((UINT8 *) DefaultData + DefaultData->HeaderSize);
        VarStoreHeaderHob = (VARIABLE_STORE_HEADER *) BuildGuidHob (&VarStoreHeader->Signature, VarStoreHeader->Size + HEADER_ALIGNMENT - 1);
        if (VarStoreHeaderHob == NULL) {
          //
          // No enough hob resource.
          //
          return EFI_OUT_OF_RESOURCES;
        }
  
        //
        // Copy variable storage header.
        //
        CopyMem (VarStoreHeaderHob, VarStoreHeader, sizeof (VARIABLE_STORE_HEADER));
        //
        // Copy variable data.
        //
        VarPtr           = (UINT8 *) HEADER_ALIGN ((UINTN) (VarStoreHeader + 1));
        VarDataOffset    = (UINT32) ((UINTN) VarPtr - (UINTN) VarStoreHeader);
        VarHobPtr        = (UINT8 *) HEADER_ALIGN ((UINTN) (VarStoreHeaderHob + 1));
        VarHobDataOffset = (UINT32) ((UINTN) VarHobPtr - (UINTN) VarStoreHeaderHob);
        CopyMem (VarHobPtr, VarPtr, VarStoreHeader->Size - VarDataOffset);
        //
        // Update variable size.
        //
        VarStoreHeaderHob->Size = VarStoreHeader->Size - VarDataOffset + VarHobDataOffset;
  
        //
        // Update Delta Data
        //
        VarHobPtr = (UINT8 *) VarStoreHeaderHob - VarDataOffset + VarHobDataOffset;
      } else {
        //
        // Apply delta setting
        //
        DataDelta     = (DATA_DELTA *) ((UINT8 *) DefaultData + DefaultData->HeaderSize);
        DataDeltaSize = SectionLength - sizeof (EFI_COMMON_SECTION_HEADER) - DefaultData->HeaderSize;
        for (Index = 0; Index < DataDeltaSize / sizeof (DATA_DELTA); Index ++) {
          *((UINT8 *) VarHobPtr + DataDelta[Index].Offset) = DataDelta[Index].Value;
        }
      }
  
      //
      // Find the matched DefaultId and BoardId
      //
      while ((UINTN) DefaultInfo < (UINTN) DefaultData + DefaultData->HeaderSize) {
        if (DefaultInfo->DefaultId == StoreId && DefaultInfo->BoardId == SkuId) {
          DefaultSettingIsFound = TRUE;
          break;
        }
        DefaultInfo ++;
      }
      //
      // Size is 24 bits wide so mask upper 8 bits. 
      // SectionLength is adjusted it is 4 byte aligned.
      // Go to the next section
      //
      SectionLength = (SectionLength + 3) & (~3);
      ASSERT (SectionLength != 0);
      Section = (EFI_COMMON_SECTION_HEADER *)((UINT8 *)Section + SectionLength);
      IsFirstSection = FALSE;
    }
    //
    // Matched default data is not found.
    //
    if (!DefaultSettingIsFound) {
      //
      // Change created HOB type to be unused.
      //
      if (VarStoreHeaderHob != NULL) {
        ((EFI_HOB_GUID_TYPE *)((UINT8 *) VarStoreHeaderHob - sizeof (EFI_HOB_GUID_TYPE)))->Header.HobType = EFI_HOB_TYPE_UNUSED;
      }
      return EFI_NOT_FOUND;
    }
  
    //
    // On recovery boot mode, emulation variable driver will be used.
    // But, Emulation variable only knows normal variable data format. 
    // So, if the default variable data format is authenticated, it needs to be converted to normal data.
    //
    Status = (*PeiServices)->GetBootMode (PeiServices, &BootMode);
    if (BootMode == BOOT_IN_RECOVERY_MODE && 
        CompareGuid (&VarStoreHeader->Signature, &gEfiAuthenticatedVariableGuid)) {
      Status = (**PeiServices).NotifyPpi (PeiServices, &mMemoryNotifyList);
      ASSERT_EFI_ERROR (Status);
    }
  
    return EFI_SUCCESS;
  }
  