text: /** @file
      Firmware volume helper interfaces.
  
    Copyright (c) 2015, Intel Corporation. All rights reserved.<BR>
    Copyright (c) 2016 - 2021, Arm Limited. All rights reserved.<BR>
    SPDX-License-Identifier: BSD-2-Clause-Patent
  
  **/
  
  #include "StandaloneMmCore.h"
  #include <Library/FvLib.h>
  #include <Library/ExtractGuidedSectionLib.h>
  
  //
  // List of file types supported by dispatcher
  //
  EFI_FV_FILETYPE  mMmFileTypes[] = {
    EFI_FV_FILETYPE_MM,
    0xE, // EFI_FV_FILETYPE_MM_STANDALONE,
         //
         // Note: DXE core will process the FV image file, so skip it in MM core
         // EFI_FV_FILETYPE_FIRMWARE_VOLUME_IMAGE
         //
  };
  
  EFI_STATUS
  MmAddToDriverList (
    IN EFI_FIRMWARE_VOLUME_HEADER  *FwVolHeader,
    IN VOID                        *Pe32Data,
    IN UINTN                       Pe32DataSize,
    IN VOID                        *Depex,
    IN UINTN                       DepexSize,
    IN EFI_GUID                    *DriverName
    );
  
  BOOLEAN
  FvHasBeenProcessed (
    IN EFI_FIRMWARE_VOLUME_HEADER  *FwVolHeader
    );
  
  VOID
  FvIsBeingProcessed (
    IN EFI_FIRMWARE_VOLUME_HEADER  *FwVolHeader
    );
  
  /**
    Given the pointer to the Firmware Volume Header find the
    MM driver and return its PE32 image.
  
    @param [in] FwVolHeader   Pointer to memory mapped FV
  
    @retval  EFI_SUCCESS            Success.
    @retval  EFI_INVALID_PARAMETER  Invalid parameter.
    @retval  EFI_NOT_FOUND          Could not find section data.
    @retval  EFI_OUT_OF_RESOURCES   Out of resources.
    @retval  EFI_VOLUME_CORRUPTED   Firmware volume is corrupted.
    @retval  EFI_UNSUPPORTED        Operation not supported.
  
  **/
  EFI_STATUS
  MmCoreFfsFindMmDriver (
    IN  EFI_FIRMWARE_VOLUME_HEADER  *FwVolHeader
    )
  {
    EFI_STATUS                  Status;
    EFI_STATUS                  DepexStatus;
    EFI_FFS_FILE_HEADER         *FileHeader;
    EFI_FV_FILETYPE             FileType;
    VOID                        *Pe32Data;
    UINTN                       Pe32DataSize;
    VOID                        *Depex;
    UINTN                       DepexSize;
    UINTN                       Index;
    EFI_COMMON_SECTION_HEADER   *Section;
    VOID                        *SectionData;
    UINTN                       SectionDataSize;
    UINT32                      DstBufferSize;
    VOID                        *ScratchBuffer;
    UINT32                      ScratchBufferSize;
    VOID                        *DstBuffer;
    UINT16                      SectionAttribute;
    UINT32                      AuthenticationStatus;
    EFI_FIRMWARE_VOLUME_HEADER  *InnerFvHeader;
  
    DEBUG ((DEBUG_INFO, "MmCoreFfsFindMmDriver - 0x%x\n", FwVolHeader));
  
    if (FvHasBeenProcessed (FwVolHeader)) {
      return EFI_SUCCESS;
    }
  
    FvIsBeingProcessed (FwVolHeader);
  
    //
    // First check for encapsulated compressed firmware volumes
    //
    FileHeader = NULL;
    do {
      Status = FfsFindNextFile (
                 EFI_FV_FILETYPE_FIRMWARE_VOLUME_IMAGE,
                 FwVolHeader,
                 &FileHeader
                 );
      if (EFI_ERROR (Status)) {
        break;
      }
  
      Status = FfsFindSectionData (
                 EFI_SECTION_GUID_DEFINED,
                 FileHeader,
                 &SectionData,
                 &SectionDataSize
                 );
      if (EFI_ERROR (Status)) {
        break;
      }
  
      Section = (EFI_COMMON_SECTION_HEADER *)(FileHeader + 1);
      Status  = ExtractGuidedSectionGetInfo (
                  Section,
                  &DstBufferSize,
                  &ScratchBufferSize,
                  &SectionAttribute
                  );
      if (EFI_ERROR (Status)) {
        break;
      }
  
      //
      // Allocate scratch buffer
      //
      ScratchBuffer = (VOID *)(UINTN)AllocatePages (EFI_SIZE_TO_PAGES (ScratchBufferSize));
      if (ScratchBuffer == NULL) {
        return EFI_OUT_OF_RESOURCES;
      }
  
      //
      // Allocate destination buffer, extra one page for adjustment
      //
      DstBuffer = (VOID *)(UINTN)AllocatePages (EFI_SIZE_TO_PAGES (DstBufferSize));
      if (DstBuffer == NULL) {
        return EFI_OUT_OF_RESOURCES;
      }
  
      //
      // Call decompress function
      //
      Status = ExtractGuidedSectionDecode (
                 Section,
                 &DstBuffer,
                 ScratchBuffer,
                 &AuthenticationStatus
                 );
      FreePages (ScratchBuffer, EFI_SIZE_TO_PAGES (ScratchBufferSize));
      if (EFI_ERROR (Status)) {
        goto FreeDstBuffer;
      }
  
      DEBUG ((
        DEBUG_INFO,
        "Processing compressed firmware volume (AuthenticationStatus == %x)\n",
        AuthenticationStatus
        ));
  
      Status = FindFfsSectionInSections (
                 DstBuffer,
                 DstBufferSize,
                 EFI_SECTION_FIRMWARE_VOLUME_IMAGE,
                 &Section
                 );
      if (EFI_ERROR (Status)) {
        goto FreeDstBuffer;
      }
  
      InnerFvHeader = (VOID *)(Section + 1);
      Status        = MmCoreFfsFindMmDriver (InnerFvHeader);
      if (EFI_ERROR (Status)) {
        goto FreeDstBuffer;
      }
    } while (TRUE);
  
    for (Index = 0; Index < sizeof (mMmFileTypes) / sizeof (mMmFileTypes[0]); Index++) {
      DEBUG ((DEBUG_INFO, "Check MmFileTypes - 0x%x\n", mMmFileTypes[Index]));
      FileType   = mMmFileTypes[Index];
      FileHeader = NULL;
      do {
        Status = FfsFindNextFile (FileType, FwVolHeader, &FileHeader);
        if (!EFI_ERROR (Status)) {
          Status = FfsFindSectionData (EFI_SECTION_PE32, FileHeader, &Pe32Data, &Pe32DataSize);
          DEBUG ((DEBUG_INFO, "Find PE data - 0x%x\n", Pe32Data));
          DepexStatus = FfsFindSectionData (EFI_SECTION_MM_DEPEX, FileHeader, &Depex, &DepexSize);
          if (!EFI_ERROR (DepexStatus)) {
            MmAddToDriverList (FwVolHeader, Pe32Data, Pe32DataSize, Depex, DepexSize, &FileHeader->Name);
          }
        }
      } while (!EFI_ERROR (Status));
    }
  
    return EFI_SUCCESS;
  
  FreeDstBuffer:
    FreePages (DstBuffer, EFI_SIZE_TO_PAGES (DstBufferSize));
  
    return Status;
  }
  