/** @file
The tool dumps the contents of a firmware volume

Copyright (c) 1999 - 2016, 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
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 <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
#include <assert.h>
#ifdef __GNUC__
#include <unistd.h>
#else
#include <direct.h>
#endif

#include <FvLib.h>
#include <Common/UefiBaseTypes.h>
#include <Common/UefiCapsule.h>
#include <Common/PiFirmwareFile.h>
#include <Common/PiFirmwareVolume.h>
#include <Guid/PiFirmwareFileSystem.h>
#include <IndustryStandard/PeImage.h>
#include <Protocol/GuidedSectionExtraction.h>

#include "Compress.h"
#include "Decompress.h"
#include "VolInfo.h"
#include "CommonLib.h"
#include "EfiUtilityMsgs.h"
#include "FirmwareVolumeBufferLib.h"
#include "OsPath.h"
#include "ParseGuidedSectionTools.h"
#include "StringFuncs.h"
#include "ParseInf.h"
#include "PeCoffLib.h"

//
// Utility global variables
//

EFI_GUID  gEfiCrc32GuidedSectionExtractionProtocolGuid = EFI_CRC32_GUIDED_SECTION_EXTRACTION_PROTOCOL_GUID;

#define UTILITY_MAJOR_VERSION      1
#define UTILITY_MINOR_VERSION      0

#define UTILITY_NAME         "VolInfo"

#define EFI_SECTION_ERROR EFIERR (100)

#define MAX_BASENAME_LEN  60  // not good to hardcode, but let's be reasonable

//
// Structure to keep a list of guid-to-basenames
//
typedef struct _GUID_TO_BASENAME {
  struct _GUID_TO_BASENAME  *Next;
  INT8                      Guid[PRINTED_GUID_BUFFER_SIZE];
  INT8                      BaseName[MAX_BASENAME_LEN];
} GUID_TO_BASENAME;

static GUID_TO_BASENAME *mGuidBaseNameList = NULL;

//
// Store GUIDed Section guid->tool mapping
//
EFI_HANDLE mParsedGuidedSectionTools = NULL;

CHAR8* mUtilityFilename = NULL;

BOOLEAN EnableHash = FALSE;
CHAR8 *OpenSslPath = NULL;

EFI_STATUS
ParseGuidBaseNameFile (
  CHAR8    *FileName
  );

EFI_STATUS
FreeGuidBaseNameList (
  VOID
  );

EFI_STATUS
PrintGuidName (
  IN UINT8    *GuidStr
  );

EFI_STATUS
ParseSection (
  IN UINT8  *SectionBuffer,
  IN UINT32 BufferLength
  );

EFI_STATUS
DumpDepexSection (
  IN UINT8    *Ptr,
  IN UINT32   SectionLength
  );

STATIC
EFI_STATUS
ReadHeader (
  IN FILE       *InputFile,
  OUT UINT32    *FvSize,
  OUT BOOLEAN   *ErasePolarity
  );

STATIC
EFI_STATUS
PrintFileInfo (
  EFI_FIRMWARE_VOLUME_HEADER  *FvImage,
  EFI_FFS_FILE_HEADER         *FileHeader,
  BOOLEAN                     ErasePolarity
  );

static
EFI_STATUS
PrintFvInfo (
  IN VOID                         *Fv,
  IN BOOLEAN                      IsChildFv
  );

static
VOID
LoadGuidedSectionToolsTxt (
  IN CHAR8* FirmwareVolumeFilename
  );

EFI_STATUS
CombinePath (
  IN  CHAR8* DefaultPath,
  IN  CHAR8* AppendPath,
  OUT CHAR8* NewPath
);

void
Usage (
  VOID
  );

UINT32
UnicodeStrLen (
  IN CHAR16 *String
  )
  /*++

  Routine Description:

  Returns the length of a null-terminated unicode string.

  Arguments:

    String - The pointer to a null-terminated unicode string.

  Returns:

    N/A

  --*/
{
  UINT32  Length;

  for (Length = 0; *String != L'\0'; String++, Length++) {
    ;
  }
  return Length;
}

VOID
Unicode2AsciiString (
  IN  CHAR16 *Source,
  OUT CHAR8  *Destination
  )
  /*++

  Routine Description:

  Convert a null-terminated unicode string to a null-terminated ascii string.

  Arguments:

    Source      - The pointer to the null-terminated input unicode string.
    Destination - The pointer to the null-terminated output ascii string.

  Returns:

    N/A

  --*/
{
  while (*Source != '\0') {
    *(Destination++) = (CHAR8) *(Source++);
  }
  //
  // End the ascii with a NULL.
  //
  *Destination = '\0';
}

int
main (
  int       argc,
  char      *argv[]
  )
/*++

Routine Description:

  GC_TODO: Add function description

Arguments:

  argc  - GC_TODO: add argument description
  ]     - GC_TODO: add argument description

Returns:

  GC_TODO: add return values

--*/
{
  FILE                        *InputFile;
  int                         BytesRead;
  EFI_FIRMWARE_VOLUME_HEADER  *FvImage;
  UINT32                      FvSize;
  EFI_STATUS                  Status;
  int                         Offset;
  BOOLEAN                     ErasePolarity;
  UINT64                      LogLevel;
  CHAR8                       *OpenSslEnv;
  CHAR8                       *OpenSslCommand;

  SetUtilityName (UTILITY_NAME);
  //
  // Print utility header
  //
  printf ("%s Version %d.%d Build %s\n",
    UTILITY_NAME,
    UTILITY_MAJOR_VERSION,
    UTILITY_MINOR_VERSION,
    __BUILD_VERSION
    );

  if (argc == 1) {
    Usage ();
    return -1;
  }

  argc--;
  argv++;
  LogLevel = 0;
  Offset = 0;

  //
  // Look for help options
  //
  if ((strcmp(argv[0], "-h") == 0) || (strcmp(argv[0], "--help") == 0) ||
      (strcmp(argv[0], "-?") == 0) || (strcmp(argv[0], "/?") == 0)) {
    Usage();
    return  STATUS_SUCCESS;
  }
  //
  // Version has already be printed, so just return success
  //
  if (strcmp(argv[0], "--version") == 0) {
    return  STATUS_SUCCESS;
  }

  //
  // If they specified -x xref guid/basename cross-reference files, process it.
  // This will print the basename beside each file guid. To use it, specify
  // -x xref_filename to processdsc, then use xref_filename as a parameter
  // here.
  //
  while (argc > 0) {
    if ((strcmp(argv[0], "-x") == 0) || (strcmp(argv[0], "--xref") == 0)) {
      ParseGuidBaseNameFile (argv[1]);
      printf("ParseGuidBaseNameFile: %s\n", argv[1]);
      argc -= 2;
      argv += 2;
      continue;
    }
    if (strcmp(argv[0], "--offset") == 0) {
      //
      // Hex or decimal?
      //
      if ((argv[1][0] == '0') && (tolower ((int)argv[1][1]) == 'x')) {
        if (sscanf (argv[1], "%x", &Offset) != 1) {
          Error (NULL, 0, 1003, "Invalid option value", "Offset = %s", argv[1]);
          return GetUtilityStatus ();
        }
      } else {
        if (sscanf (argv[1], "%d", &Offset) != 1) {
          Error (NULL, 0, 1003, "Invalid option value", "Offset = %s", argv[1]);
          return GetUtilityStatus ();
        }
        //
        // See if they said something like "64K"
        //
        if (tolower ((int)argv[1][strlen (argv[1]) - 1]) == 'k') {
          Offset *= 1024;
        }
      }

      argc -= 2;
      argv += 2;
      continue;
    }
    if ((stricmp (argv[0], "--hash") == 0)) {
      if (EnableHash == TRUE) {
        //
        // --hash already given in the option, ignore this one
        //
        argc --;
        argv ++;
        continue;
      }
      EnableHash = TRUE;
      OpenSslCommand = "openssl";
      OpenSslEnv = getenv("OPENSSL_PATH");
      if (OpenSslEnv == NULL) {
        OpenSslPath = OpenSslCommand;
      } else {
        OpenSslPath = malloc(strlen(OpenSslEnv)+strlen(OpenSslCommand)+1);
        if (OpenSslPath == NULL) {
          Error (NULL, 0, 4001, "Resource", "memory cannot be allocated!");
          return GetUtilityStatus ();
        }
        CombinePath(OpenSslEnv, OpenSslCommand, OpenSslPath);
      }
      if (OpenSslPath == NULL){
        Error (NULL, 0, 3000, "Open SSL command not available.  Please verify PATH or set OPENSSL_PATH.", NULL);
        return GetUtilityStatus ();
      }
      argc --;
      argv ++;
      continue;
    }

    if ((stricmp (argv[0], "-v") == 0) || (stricmp (argv[0], "--verbose") == 0)) {
      SetPrintLevel (VERBOSE_LOG_LEVEL);
      argc --;
      argv ++;
      continue;
    }

    if ((stricmp (argv[0], "-q") == 0) || (stricmp (argv[0], "--quiet") == 0)) {
      SetPrintLevel (KEY_LOG_LEVEL);
      argc --;
      argv ++;
      continue;
    }

    if ((stricmp (argv[0], "-d") == 0) || (stricmp (argv[0], "--debug") == 0)) {
      Status = AsciiStringToUint64 (argv[1], FALSE, &LogLevel);
      if (EFI_ERROR (Status)) {
        Error (NULL, 0, 1003, "Invalid option value", "%s = %s", argv[0], argv[1]);
        return -1;
      }
      if (LogLevel > 9) {
        Error (NULL, 0, 1003, "Invalid option value", "Debug Level range is 0-9, current input level is %d", (int) LogLevel);
        return -1;
      }
      SetPrintLevel (LogLevel);
      DebugMsg (NULL, 0, 9, "Debug Mode Set", "Debug Output Mode Level %s is set!", argv[1]);
      argc -= 2;
      argv += 2;
      continue;
    }

    mUtilityFilename = argv[0];
    argc --;
    argv ++;
  }

  //
  // Open the file containing the FV
  //
  if (mUtilityFilename == NULL) {
    Error (NULL, 0, 1001, "Missing option", "Input files are not specified");
    return GetUtilityStatus ();
  }
  InputFile = fopen (LongFilePath (mUtilityFilename), "rb");
  if (InputFile == NULL) {
    Error (NULL, 0, 0001, "Error opening the input file", mUtilityFilename);
    return GetUtilityStatus ();
  }
  //
  // Skip over pad bytes if specified. This is used if they prepend 0xff
  // data to the FV image binary.
  //
  if (Offset != 0) {
    fseek (InputFile, Offset, SEEK_SET);
  }
  //
  // Determine size of FV
  //
  Status = ReadHeader (InputFile, &FvSize, &ErasePolarity);
  if (EFI_ERROR (Status)) {
    Error (NULL, 0, 0003, "error parsing FV image", "%s Header is invalid", mUtilityFilename);
    fclose (InputFile);
    return GetUtilityStatus ();
  }
  //
  // Allocate a buffer for the FV image
  //
  FvImage = malloc (FvSize);
  if (FvImage == NULL) {
    Error (NULL, 0, 4001, "Resource: Memory can't be allocated", NULL);
    fclose (InputFile);
    return GetUtilityStatus ();
  }
  //
  // Seek to the start of the image, then read the entire FV to the buffer
  //
  fseek (InputFile, Offset, SEEK_SET);
  BytesRead = fread (FvImage, 1, FvSize, InputFile);
  fclose (InputFile);
  if ((unsigned int) BytesRead != FvSize) {
    Error (NULL, 0, 0004, "error reading FvImage from", mUtilityFilename);
    free (FvImage);
    return GetUtilityStatus ();
  }

  LoadGuidedSectionToolsTxt (mUtilityFilename);

  PrintFvInfo (FvImage, FALSE);

  //
  // Clean up
  //
  free (FvImage);
  FreeGuidBaseNameList ();
  return GetUtilityStatus ();
}


static
EFI_STATUS
PrintFvInfo (
  IN VOID                         *Fv,
  IN BOOLEAN                      IsChildFv
  )
/*++

Routine Description:

  GC_TODO: Add function description

Arguments:

  Fv            - Firmware Volume to print information about
  IsChildFv     - Flag specifies whether the input FV is a child FV.

Returns:

  EFI_STATUS

--*/
{
  EFI_STATUS                  Status;
  UINTN                       NumberOfFiles;
  BOOLEAN                     ErasePolarity;
  UINTN                       FvSize;
  EFI_FFS_FILE_HEADER         *CurrentFile;
  UINTN                       Key;

  Status = FvBufGetSize (Fv, &FvSize);

  NumberOfFiles = 0;
  ErasePolarity =
    (((EFI_FIRMWARE_VOLUME_HEADER*)Fv)->Attributes & EFI_FVB2_ERASE_POLARITY) ?
      TRUE : FALSE;

  //
  // Get the first file
  //
  Key = 0;
  Status = FvBufFindNextFile (Fv, &Key, (VOID **) &CurrentFile);
  if (EFI_ERROR (Status)) {
    Error (NULL, 0, 0003, "error parsing FV image", "cannot find the first file in the FV image");
    return GetUtilityStatus ();
  }
  //
  // Display information about files found
  //
  while (CurrentFile != NULL) {
    //
    // Increment the number of files counter
    //
    NumberOfFiles++;

    //
    // Display info about this file
    //
    Status = PrintFileInfo (Fv, CurrentFile, ErasePolarity);
    if (EFI_ERROR (Status)) {
      Error (NULL, 0, 0003, "error parsing FV image", "failed to parse a file in the FV");
      return GetUtilityStatus ();
    }
    //
    // Get the next file
    //
    Status = FvBufFindNextFile (Fv, &Key, (VOID **) &CurrentFile);
    if (Status == EFI_NOT_FOUND) {
      CurrentFile = NULL;
    } else if (EFI_ERROR (Status)) {
      Error (NULL, 0, 0003, "error parsing FV image", "cannot find the next file in the FV image");
      return GetUtilityStatus ();
    }
  }

  if (IsChildFv) {
    printf ("There are a total of %d files in the child FV\n", (int) NumberOfFiles);
  } else {
    printf ("There are a total of %d files in this FV\n", (int) NumberOfFiles);
  }

  return EFI_SUCCESS;
}

UINT32
GetOccupiedSize (
  IN UINT32  ActualSize,
  IN UINT32  Alignment
  )
/*++

Routine Description:

  This function returns the next larger size that meets the alignment 
  requirement specified.

Arguments:

  ActualSize      The size.
  Alignment       The desired alignment.
    
Returns:
 
  EFI_SUCCESS             Function completed successfully.
  EFI_ABORTED             The function encountered an error.

--*/
{
  UINT32  OccupiedSize;

  OccupiedSize = ActualSize;
  while ((OccupiedSize & (Alignment - 1)) != 0) {
    OccupiedSize++;
  }

  return OccupiedSize;
}

static
CHAR8 *
SectionNameToStr (
  IN EFI_SECTION_TYPE   Type
  )
/*++

Routine Description:

  Converts EFI Section names to Strings

Arguments:

  Type  - The EFI Section type

Returns:

  CHAR8* - Pointer to the String containing the section name.

--*/
{
  CHAR8 *SectionStr;
  CHAR8 *SectionTypeStringTable[] = {
    //
    // 0X00
    //
    "EFI_SECTION_ALL",
    //
    // 0x01
    //
    "EFI_SECTION_COMPRESSION",
    //
    // 0x02
    //
    "EFI_SECTION_GUID_DEFINED",    
    //
    // 0x03
    //
    "Unknown section type - Reserved 0x03",
    //
    // 0x04
    //
    "Unknown section type - Reserved 0x04",
    //
    // 0x05
    //
    "Unknown section type - Reserved 0x05",
    //
    // 0x06
    //
    "Unknown section type - Reserved 0x06",
    //
    // 0x07
    //
    "Unknown section type - Reserved 0x07",
    //
    // 0x08
    //
    "Unknown section type - Reserved 0x08",
    //
    // 0x09
    //
    "Unknown section type - Reserved 0x09",
    //
    // 0x0A
    //
    "Unknown section type - Reserved 0x0A",
    //
    // 0x0B
    //
    "Unknown section type - Reserved 0x0B",
    //
    // 0x0C
    //
    "Unknown section type - Reserved 0x0C",
    //
    // 0x0D
    //
    "Unknown section type - Reserved 0x0D",
    //
    // 0x0E
    //
    "Unknown section type - Reserved 0x0E",
    //
    // 0x0F
    //
    "Unknown section type - Reserved 0x0E",
    //
    // 0x10
    //
    "EFI_SECTION_PE32",
    //
    // 0x11
    //
    "EFI_SECTION_PIC",
    //
    // 0x12
    //
    "EFI_SECTION_TE",    
    //
    // 0x13
    //
    "EFI_SECTION_DXE_DEPEX", 
    //
    // 0x14
    //
    "EFI_SECTION_VERSION",
    //
    // 0x15
    //
    "EFI_SECTION_USER_INTERFACE",
    //
    // 0x16
    //
    "EFI_SECTION_COMPATIBILITY16",
    //
    // 0x17
    //
    "EFI_SECTION_FIRMWARE_VOLUME_IMAGE ",
    //
    // 0x18
    //
    "EFI_SECTION_FREEFORM_SUBTYPE_GUID ",
    //
    // 0x19
    //
    "EFI_SECTION_RAW",
    //
    // 0x1A
    //
    "Unknown section type - 0x1A",
    //
    // 0x1B
    //
    "EFI_SECTION_PEI_DEPEX",
    //
    // 0x1C
    //
    "EFI_SECTION_SMM_DEPEX",
    //
    // 0x1C+
    //
    "Unknown section type - Reserved - beyond last defined section"
  };

  if (Type > EFI_SECTION_LAST_SECTION_TYPE) {
    Type = EFI_SECTION_LAST_SECTION_TYPE + 1;
  }

  SectionStr = malloc (100);
  if (SectionStr == NULL) {
    printf ("Error: Out of memory resources.\n");
    return SectionStr;
  }
  strcpy (SectionStr, SectionTypeStringTable[Type]);
  return SectionStr;
}

STATIC
EFI_STATUS
ReadHeader (
  IN FILE       *InputFile,
  OUT UINT32    *FvSize,
  OUT BOOLEAN   *ErasePolarity
  )
/*++

Routine Description:

  This function determines the size of the FV and the erase polarity.  The 
  erase polarity is the FALSE value for file state.

Arguments:

  InputFile       The file that contains the FV image.
  FvSize          The size of the FV.
  ErasePolarity   The FV erase polarity.
    
Returns:
 
  EFI_SUCCESS             Function completed successfully.
  EFI_INVALID_PARAMETER   A required parameter was NULL or is out of range.
  EFI_ABORTED             The function encountered an error.

--*/
{
  EFI_FIRMWARE_VOLUME_HEADER  VolumeHeader;
  EFI_FV_BLOCK_MAP_ENTRY      BlockMap;
  UINTN                       Signature[2];
  UINTN                       BytesRead;
  UINT32                      Size;

  BytesRead = 0;
  Size      = 0;
  //
  // Check input parameters
  //
  if (InputFile == NULL || FvSize == NULL || ErasePolarity == NULL) {
    Error (__FILE__, __LINE__, 0, "application error", "invalid parameter to function");
    return EFI_INVALID_PARAMETER;
  }
  //
  // Read the header
  //
  fread (&VolumeHeader, sizeof (EFI_FIRMWARE_VOLUME_HEADER) - sizeof (EFI_FV_BLOCK_MAP_ENTRY), 1, InputFile);
  BytesRead     = sizeof (EFI_FIRMWARE_VOLUME_HEADER) - sizeof (EFI_FV_BLOCK_MAP_ENTRY);
  Signature[0]  = VolumeHeader.Signature;
  Signature[1]  = 0;

  //
  // Print FV header information
  //
  printf ("Signature:        %s (%X)\n", (char *) Signature, (unsigned) VolumeHeader.Signature);
  printf ("Attributes:       %X\n", (unsigned) VolumeHeader.Attributes);

  if (VolumeHeader.Attributes & EFI_FVB2_READ_DISABLED_CAP) {
    printf ("       EFI_FVB2_READ_DISABLED_CAP\n");
  }

  if (VolumeHeader.Attributes & EFI_FVB2_READ_ENABLED_CAP) {
    printf ("       EFI_FVB2_READ_ENABLED_CAP\n");
  }

  if (VolumeHeader.Attributes & EFI_FVB2_READ_STATUS) {
    printf ("       EFI_FVB2_READ_STATUS\n");
  }

  if (VolumeHeader.Attributes & EFI_FVB2_WRITE_DISABLED_CAP) {
    printf ("       EFI_FVB2_WRITE_DISABLED_CAP\n");
  }

  if (VolumeHeader.Attributes & EFI_FVB2_WRITE_ENABLED_CAP) {
    printf ("       EFI_FVB2_WRITE_ENABLED_CAP\n");
  }

  if (VolumeHeader.Attributes & EFI_FVB2_WRITE_STATUS) {
    printf ("       EFI_FVB2_WRITE_STATUS\n");
  }

  if (VolumeHeader.Attributes & EFI_FVB2_LOCK_CAP) {
    printf ("       EFI_FVB2_LOCK_CAP\n");
  }

  if (VolumeHeader.Attributes & EFI_FVB2_LOCK_STATUS) {
    printf ("       EFI_FVB2_LOCK_STATUS\n");
  }

  if (VolumeHeader.Attributes & EFI_FVB2_STICKY_WRITE) {
    printf ("       EFI_FVB2_STICKY_WRITE\n");
  }

  if (VolumeHeader.Attributes & EFI_FVB2_MEMORY_MAPPED) {
    printf ("       EFI_FVB2_MEMORY_MAPPED\n");
  }

  if (VolumeHeader.Attributes & EFI_FVB2_ERASE_POLARITY) {
    printf ("       EFI_FVB2_ERASE_POLARITY\n");
    *ErasePolarity = TRUE;
  }

#if (PI_SPECIFICATION_VERSION < 0x00010000)
  if (VolumeHeader.Attributes & EFI_FVB2_ALIGNMENT) {
    printf ("       EFI_FVB2_ALIGNMENT\n");
  }

  if (VolumeHeader.Attributes & EFI_FVB2_ALIGNMENT_2) {
    printf ("       EFI_FVB2_ALIGNMENT_2\n");
  }

  if (VolumeHeader.Attributes & EFI_FVB2_ALIGNMENT_4) {
    printf ("       EFI_FVB2_ALIGNMENT_4\n");
  }

  if (VolumeHeader.Attributes & EFI_FVB2_ALIGNMENT_8) {
    printf ("       EFI_FVB2_ALIGNMENT_8\n");
  }

  if (VolumeHeader.Attributes & EFI_FVB2_ALIGNMENT_16) {
    printf ("       EFI_FVB2_ALIGNMENT_16\n");
  }

  if (VolumeHeader.Attributes & EFI_FVB2_ALIGNMENT_32) {
    printf ("       EFI_FVB2_ALIGNMENT_32\n");
  }

  if (VolumeHeader.Attributes & EFI_FVB2_ALIGNMENT_64) {
    printf ("        EFI_FVB2_ALIGNMENT_64\n");
  }

  if (VolumeHeader.Attributes & EFI_FVB2_ALIGNMENT_128) {
    printf ("        EFI_FVB2_ALIGNMENT_128\n");
  }

  if (VolumeHeader.Attributes & EFI_FVB2_ALIGNMENT_256) {
    printf ("        EFI_FVB2_ALIGNMENT_256\n");
  }

  if (VolumeHeader.Attributes & EFI_FVB2_ALIGNMENT_512) {
    printf ("        EFI_FVB2_ALIGNMENT_512\n");
  }

  if (VolumeHeader.Attributes & EFI_FVB2_ALIGNMENT_1K) {
    printf ("        EFI_FVB2_ALIGNMENT_1K\n");
  }

  if (VolumeHeader.Attributes & EFI_FVB2_ALIGNMENT_2K) {
    printf ("        EFI_FVB2_ALIGNMENT_2K\n");
  }

  if (VolumeHeader.Attributes & EFI_FVB2_ALIGNMENT_4K) {
    printf ("        EFI_FVB2_ALIGNMENT_4K\n");
  }

  if (VolumeHeader.Attributes & EFI_FVB2_ALIGNMENT_8K) {
    printf ("        EFI_FVB2_ALIGNMENT_8K\n");
  }

  if (VolumeHeader.Attributes & EFI_FVB2_ALIGNMENT_16K) {
    printf ("        EFI_FVB2_ALIGNMENT_16K\n");
  }

  if (VolumeHeader.Attributes & EFI_FVB2_ALIGNMENT_32K) {
    printf ("        EFI_FVB2_ALIGNMENT_32K\n");
  }

  if (VolumeHeader.Attributes & EFI_FVB2_ALIGNMENT_64K) {
    printf ("        EFI_FVB2_ALIGNMENT_64K\n");
  }
  
#else

  if (VolumeHeader.Attributes & EFI_FVB2_READ_LOCK_CAP) {
    printf ("       EFI_FVB2_READ_LOCK_CAP\n");
  }

  if (VolumeHeader.Attributes & EFI_FVB2_READ_LOCK_STATUS) {
    printf ("       EFI_FVB2_READ_LOCK_STATUS\n");
  }

  if (VolumeHeader.Attributes & EFI_FVB2_WRITE_LOCK_CAP) {
    printf ("       EFI_FVB2_WRITE_LOCK_CAP\n");
  }

  if (VolumeHeader.Attributes & EFI_FVB2_WRITE_LOCK_STATUS) {
    printf ("       EFI_FVB2_WRITE_LOCK_STATUS\n");
  }

  if (VolumeHeader.Attributes & EFI_FVB2_ALIGNMENT_1) {
    printf ("       EFI_FVB2_ALIGNMENT_1\n");
  }

  if (VolumeHeader.Attributes & EFI_FVB2_ALIGNMENT_2) {
    printf ("        EFI_FVB2_ALIGNMENT_2\n");
  }

  if (VolumeHeader.Attributes & EFI_FVB2_ALIGNMENT_4) {
    printf ("        EFI_FVB2_ALIGNMENT_4\n");
  }

  if (VolumeHeader.Attributes & EFI_FVB2_ALIGNMENT_8) {
    printf ("        EFI_FVB2_ALIGNMENT_8\n");
  }

  if (VolumeHeader.Attributes & EFI_FVB2_ALIGNMENT_16) {
    printf ("        EFI_FVB2_ALIGNMENT_16\n");
  }

  if (VolumeHeader.Attributes & EFI_FVB2_ALIGNMENT_32) {
    printf ("        EFI_FVB2_ALIGNMENT_32\n");
  }

  if (VolumeHeader.Attributes & EFI_FVB2_ALIGNMENT_64) {
    printf ("        EFI_FVB2_ALIGNMENT_64\n");
  }

  if (VolumeHeader.Attributes & EFI_FVB2_ALIGNMENT_128) {
    printf ("        EFI_FVB2_ALIGNMENT_128\n");
  }

  if (VolumeHeader.Attributes & EFI_FVB2_ALIGNMENT_256) {
    printf ("        EFI_FVB2_ALIGNMENT_256\n");
  }

  if (VolumeHeader.Attributes & EFI_FVB2_ALIGNMENT_512) {
    printf ("        EFI_FVB2_ALIGNMENT_512\n");
  }

  if (VolumeHeader.Attributes & EFI_FVB2_ALIGNMENT_1K) {
    printf ("        EFI_FVB2_ALIGNMENT_1K\n");
  }

  if (VolumeHeader.Attributes & EFI_FVB2_ALIGNMENT_2K) {
    printf ("        EFI_FVB2_ALIGNMENT_2K\n");
  }

  if (VolumeHeader.Attributes & EFI_FVB2_ALIGNMENT_4K) {
    printf ("        EFI_FVB2_ALIGNMENT_4K\n");
  }

  if (VolumeHeader.Attributes & EFI_FVB2_ALIGNMENT_8K) {
    printf ("        EFI_FVB2_ALIGNMENT_8K\n");
  }

  if (VolumeHeader.Attributes & EFI_FVB2_ALIGNMENT_16K) {
    printf ("        EFI_FVB2_ALIGNMENT_16K\n");
  }

  if (VolumeHeader.Attributes & EFI_FVB2_ALIGNMENT_32K) {
    printf ("        EFI_FVB2_ALIGNMENT_32K\n");
  }

  if (VolumeHeader.Attributes & EFI_FVB2_ALIGNMENT_64K) {
    printf ("        EFI_FVB2_ALIGNMENT_64K\n");
  }

  if (VolumeHeader.Attributes & EFI_FVB2_ALIGNMENT_128K) {
    printf ("        EFI_FVB2_ALIGNMENT_128K\n");
  }

  if (VolumeHeader.Attributes & EFI_FVB2_ALIGNMENT_256K) {
    printf ("        EFI_FVB2_ALIGNMENT_256K\n");
  }

  if (VolumeHeader.Attributes & EFI_FVB2_ALIGNMENT_512K) {
    printf ("        EFI_FVB2_ALIGNMENT_512K\n");
  }

  if (VolumeHeader.Attributes & EFI_FVB2_ALIGNMENT_1M) {
    printf ("        EFI_FVB2_ALIGNMENT_1M\n");
  }

  if (VolumeHeader.Attributes & EFI_FVB2_ALIGNMENT_2M) {
    printf ("        EFI_FVB2_ALIGNMENT_2M\n");
  }

  if (VolumeHeader.Attributes & EFI_FVB2_ALIGNMENT_4M) {
    printf ("        EFI_FVB2_ALIGNMENT_4M\n");
  }

  if (VolumeHeader.Attributes & EFI_FVB2_ALIGNMENT_8M) {
    printf ("        EFI_FVB2_ALIGNMENT_8M\n");
  }

  if (VolumeHeader.Attributes & EFI_FVB2_ALIGNMENT_16M) {
    printf ("        EFI_FVB2_ALIGNMENT_16M\n");
  }

  if (VolumeHeader.Attributes & EFI_FVB2_ALIGNMENT_32M) {
    printf ("        EFI_FVB2_ALIGNMENT_32M\n");
  }

  if (VolumeHeader.Attributes & EFI_FVB2_ALIGNMENT_64M) {
    printf ("        EFI_FVB2_ALIGNMENT_64M\n");
  }

  if (VolumeHeader.Attributes & EFI_FVB2_ALIGNMENT_128M) {
    printf ("        EFI_FVB2_ALIGNMENT_128M\n");
  }

  if (VolumeHeader.Attributes & EFI_FVB2_ALIGNMENT_64M) {
    printf ("        EFI_FVB2_ALIGNMENT_64M\n");
  }

  if (VolumeHeader.Attributes & EFI_FVB2_ALIGNMENT_128M) {
    printf ("        EFI_FVB2_ALIGNMENT_128M\n");
  }

  if (VolumeHeader.Attributes & EFI_FVB2_ALIGNMENT_256M) {
    printf ("        EFI_FVB2_ALIGNMENT_256M\n");
  }

  if (VolumeHeader.Attributes & EFI_FVB2_ALIGNMENT_512M) {
    printf ("        EFI_FVB2_ALIGNMENT_512M\n");
  }

  if (VolumeHeader.Attributes & EFI_FVB2_ALIGNMENT_1G) {
    printf ("        EFI_FVB2_ALIGNMENT_1G\n");
  }

  if (VolumeHeader.Attributes & EFI_FVB2_ALIGNMENT_2G) {
    printf ("        EFI_FVB2_ALIGNMENT_2G\n");
  }

#endif
  printf ("Header Length:         0x%08X\n", VolumeHeader.HeaderLength);
  printf ("File System ID:        ");
  PrintGuid (&VolumeHeader.FileSystemGuid);
  //
  // printf ("\n");
  //
  printf ("Revision:              0x%04X\n", VolumeHeader.Revision);

  do {
    fread (&BlockMap, sizeof (EFI_FV_BLOCK_MAP_ENTRY), 1, InputFile);
    BytesRead += sizeof (EFI_FV_BLOCK_MAP_ENTRY);

    if (BlockMap.NumBlocks != 0) {
      printf ("Number of Blocks:      0x%08X\n", (unsigned) BlockMap.NumBlocks);
      printf ("Block Length:          0x%08X\n", (unsigned) BlockMap.Length);
      Size += BlockMap.NumBlocks * BlockMap.Length;
    }

  } while (!(BlockMap.NumBlocks == 0 && BlockMap.Length == 0));

  if (BytesRead != VolumeHeader.HeaderLength) {
    printf ("ERROR: Header length not consistent with Block Maps!\n");
    return EFI_ABORTED;
  }

  if (VolumeHeader.FvLength != Size) {
    printf ("ERROR: Volume Size not consistant with Block Maps!\n");
    return EFI_ABORTED;
  }

  printf ("Total Volume Size:     0x%08X\n", (unsigned) Size);

  *FvSize = Size;

  //
  // rewind (InputFile);
  //
  return EFI_SUCCESS;
}

STATIC
EFI_STATUS
PrintFileInfo (
  EFI_FIRMWARE_VOLUME_HEADER  *FvImage,
  EFI_FFS_FILE_HEADER         *FileHeader,
  BOOLEAN                     ErasePolarity
  )
/*++

Routine Description:

  GC_TODO: Add function description

Arguments:

  FvImage       - GC_TODO: add argument description
  FileHeader    - GC_TODO: add argument description
  ErasePolarity - GC_TODO: add argument description

Returns:

  EFI_SUCCESS - GC_TODO: Add description for return value
  EFI_ABORTED - GC_TODO: Add description for return value

--*/
{
  UINT32              FileLength;
  UINT8               FileState;
  UINT8               Checksum;
  EFI_FFS_FILE_HEADER2 BlankHeader;
  EFI_STATUS          Status;
  UINT8               GuidBuffer[PRINTED_GUID_BUFFER_SIZE];
  UINT32              HeaderSize;
#if (PI_SPECIFICATION_VERSION < 0x00010000) 
  UINT16              *Tail;
#endif
  //
  // Check if we have free space
  //
  HeaderSize = FvBufGetFfsHeaderSize(FileHeader);
  if (ErasePolarity) {
    memset (&BlankHeader, -1, HeaderSize);
  } else {
    memset (&BlankHeader, 0, HeaderSize);
  }

  if (memcmp (&BlankHeader, FileHeader, HeaderSize) == 0) {
    return EFI_SUCCESS;
  }
  //
  // Print file information.
  //
  printf ("============================================================\n");

  printf ("File Name:        ");
  PrintGuidToBuffer (&FileHeader->Name, GuidBuffer, sizeof (GuidBuffer), TRUE);
  printf ("%s  ", GuidBuffer);
  PrintGuidName (GuidBuffer);
  printf ("\n");

  //
  //  PrintGuid (&FileHeader->Name);
  //  printf ("\n");
  //
  FileLength = FvBufGetFfsFileSize (FileHeader);
  printf ("File Offset:      0x%08X\n", (unsigned) ((UINTN) FileHeader - (UINTN) FvImage));
  printf ("File Length:      0x%08X\n", (unsigned) FileLength);
  printf ("File Attributes:  0x%02X\n", FileHeader->Attributes);
  printf ("File State:       0x%02X\n", FileHeader->State);

  //
  // Print file state
  //
  FileState = GetFileState (ErasePolarity, FileHeader);

  switch (FileState) {

  case EFI_FILE_HEADER_CONSTRUCTION:
    printf ("        EFI_FILE_HEADER_CONSTRUCTION\n");
    return EFI_SUCCESS;

  case EFI_FILE_HEADER_INVALID:
    printf ("        EFI_FILE_HEADER_INVALID\n");
    return EFI_SUCCESS;

  case EFI_FILE_HEADER_VALID:
    printf ("        EFI_FILE_HEADER_VALID\n");
    Checksum  = CalculateSum8 ((UINT8 *) FileHeader, HeaderSize);
    Checksum  = (UINT8) (Checksum - FileHeader->IntegrityCheck.Checksum.File);
    Checksum  = (UINT8) (Checksum - FileHeader->State);
    if (Checksum != 0) {
      printf ("ERROR: Header checksum invalid.\n");
      return EFI_ABORTED;
    }

    return EFI_SUCCESS;

  case EFI_FILE_DELETED:
    printf ("        EFI_FILE_DELETED\n");

  case EFI_FILE_MARKED_FOR_UPDATE:
    printf ("        EFI_FILE_MARKED_FOR_UPDATE\n");

  case EFI_FILE_DATA_VALID:
    printf ("        EFI_FILE_DATA_VALID\n");

    //
    // Calculate header checksum
    //
    Checksum  = CalculateSum8 ((UINT8 *) FileHeader, HeaderSize);
    Checksum  = (UINT8) (Checksum - FileHeader->IntegrityCheck.Checksum.File);
    Checksum  = (UINT8) (Checksum - FileHeader->State);
    if (Checksum != 0) {
      Error (NULL, 0, 0003, "error parsing FFS file", "FFS file with Guid %s has invalid header checksum", GuidBuffer);
      return EFI_ABORTED;
    }

    FileLength = FvBufGetFfsFileSize (FileHeader);

    if (FileHeader->Attributes & FFS_ATTRIB_CHECKSUM) {
      //
      // Calculate file checksum
      //
      Checksum  = CalculateSum8 ((UINT8 *)FileHeader + HeaderSize, FileLength - HeaderSize);
      Checksum  = Checksum + FileHeader->IntegrityCheck.Checksum.File;
      if (Checksum != 0) {
        Error (NULL, 0, 0003, "error parsing FFS file", "FFS file with Guid %s has invalid file checksum", GuidBuffer);
        return EFI_ABORTED;
      }
    } else {
      if (FileHeader->IntegrityCheck.Checksum.File != FFS_FIXED_CHECKSUM) {
        Error (NULL, 0, 0003, "error parsing FFS file", "FFS file with Guid %s has invalid header checksum -- not set to fixed value of 0xAA", GuidBuffer);
        return EFI_ABORTED;
      }
    }
#if (PI_SPECIFICATION_VERSION < 0x00010000)    
    //
    // Verify tail if present
    //
    if (FileHeader->Attributes & FFS_ATTRIB_TAIL_PRESENT) {
      //
      // Verify tail is complement of integrity check field in the header.
      //
      Tail = (UINT16 *) ((UINTN) FileHeader + GetLength (FileHeader->Size) - sizeof (EFI_FFS_INTEGRITY_CHECK));
      if (FileHeader->IntegrityCheck.TailReference != (UINT16)~(*Tail)) {
        Error (NULL, 0, 0003, "error parsing FFS file", \
        "FFS file with Guid %s failed in the integrity check, tail is not the complement of the header field", GuidBuffer);
        return EFI_ABORTED;
      }
    }
 #endif   
    break;

  default:
    Error (NULL, 0, 0003, "error parsing FFS file", "FFS file with Guid %s has the invalid/unrecognized file state bits", GuidBuffer);
    return EFI_ABORTED;
  }

  printf ("File Type:        0x%02X  ", FileHeader->Type);

  switch (FileHeader->Type) {

  case EFI_FV_FILETYPE_RAW:
    printf ("EFI_FV_FILETYPE_RAW\n");
    break;

  case EFI_FV_FILETYPE_FREEFORM:
    printf ("EFI_FV_FILETYPE_FREEFORM\n");
    break;

  case EFI_FV_FILETYPE_SECURITY_CORE:
    printf ("EFI_FV_FILETYPE_SECURITY_CORE\n");
    break;

  case EFI_FV_FILETYPE_PEI_CORE:
    printf ("EFI_FV_FILETYPE_PEI_CORE\n");
    break;

  case EFI_FV_FILETYPE_DXE_CORE:
    printf ("EFI_FV_FILETYPE_DXE_CORE\n");
    break;

  case EFI_FV_FILETYPE_PEIM:
    printf ("EFI_FV_FILETYPE_PEIM\n");
    break;

  case EFI_FV_FILETYPE_DRIVER:
    printf ("EFI_FV_FILETYPE_DRIVER\n");
    break;

  case EFI_FV_FILETYPE_COMBINED_PEIM_DRIVER:
    printf ("EFI_FV_FILETYPE_COMBINED_PEIM_DRIVER\n");
    break;

  case EFI_FV_FILETYPE_APPLICATION:
    printf ("EFI_FV_FILETYPE_APPLICATION\n");
    break;

  case EFI_FV_FILETYPE_SMM:
    printf ("EFI_FV_FILETYPE_SMM\n");
    break;

  case EFI_FV_FILETYPE_FIRMWARE_VOLUME_IMAGE:
    printf ("EFI_FV_FILETYPE_FIRMWARE_VOLUME_IMAGE\n");
    break;

  case EFI_FV_FILETYPE_COMBINED_SMM_DXE:
    printf ("EFI_FV_FILETYPE_COMBINED_SMM_DXE\n");
    break;

  case EFI_FV_FILETYPE_SMM_CORE:
    printf ("EFI_FV_FILETYPE_SMM_CORE\n");
    break;

  case EFI_FV_FILETYPE_FFS_PAD:
    printf ("EFI_FV_FILETYPE_FFS_PAD\n");
    break;

  default:
    printf ("\nERROR: Unrecognized file type %X.\n", FileHeader->Type);
    return EFI_ABORTED;
    break;
  }

  switch (FileHeader->Type) {

  case EFI_FV_FILETYPE_ALL:
  case EFI_FV_FILETYPE_RAW:
  case EFI_FV_FILETYPE_FFS_PAD:
    break;

  default:
    //
    // All other files have sections
    //
    Status = ParseSection (
              (UINT8 *) ((UINTN) FileHeader + HeaderSize),
              FvBufGetFfsFileSize (FileHeader) - HeaderSize
              );
    if (EFI_ERROR (Status)) {
      //
      // printf ("ERROR: Parsing the FFS file.\n");
      //
      return EFI_ABORTED;
    }
    break;
  }

  return EFI_SUCCESS;
}

EFI_STATUS
RebaseImageRead (
  IN     VOID    *FileHandle,
  IN     UINTN   FileOffset,
  IN OUT UINT32  *ReadSize,
  OUT    VOID    *Buffer
  )
/*++

Routine Description:

  Support routine for the PE/COFF Loader that reads a buffer from a PE/COFF file

Arguments:

  FileHandle - The handle to the PE/COFF file

  FileOffset - The offset, in bytes, into the file to read

  ReadSize   - The number of bytes to read from the file starting at FileOffset

  Buffer     - A pointer to the buffer to read the data into.

Returns:

  EFI_SUCCESS - ReadSize bytes of data were read into Buffer from the PE/COFF file starting at FileOffset

--*/
{
  CHAR8   *Destination8;
  CHAR8   *Source8;
  UINT32  Length;

  Destination8  = Buffer;
  Source8       = (CHAR8 *) ((UINTN) FileHandle + FileOffset);
  Length        = *ReadSize;
  while (Length--) {
    *(Destination8++) = *(Source8++);
  }

  return EFI_SUCCESS;
}

EFI_STATUS
SetAddressToSectionHeader (
  IN     CHAR8   *FileName,
  IN OUT UINT8   *FileBuffer,
  IN     UINT64  NewPe32BaseAddress
  )
/*++

Routine Description:

  Set new base address into the section header of PeImage

Arguments:

  FileName           - Name of file
  FileBuffer         - Pointer to PeImage.
  NewPe32BaseAddress - New Base Address for PE image.

Returns:

  EFI_SUCCESS          Set new base address into this image successfully.

--*/
{
  EFI_STATUS                            Status;
  PE_COFF_LOADER_IMAGE_CONTEXT          ImageContext;
  UINTN                                 Index;
  EFI_IMAGE_OPTIONAL_HEADER_UNION       *ImgHdr;
  EFI_IMAGE_SECTION_HEADER              *SectionHeader;

  //
  // Initialize context
  //
  memset (&ImageContext, 0, sizeof (ImageContext));
  ImageContext.Handle     = (VOID *) FileBuffer;
  ImageContext.ImageRead  = (PE_COFF_LOADER_READ_FILE) RebaseImageRead;
  Status                  = PeCoffLoaderGetImageInfo (&ImageContext);
  if (EFI_ERROR (Status)) {
    Error (NULL, 0, 3000, "Invalid", "The input PeImage %s is not valid", FileName);
    return Status;
  }

  if (ImageContext.RelocationsStripped) {
    Error (NULL, 0, 3000, "Invalid", "The input PeImage %s has no relocation to be fixed up", FileName);
    return Status;
  }

  //
  // Get PeHeader pointer
  //
  ImgHdr = (EFI_IMAGE_OPTIONAL_HEADER_UNION *)(FileBuffer + ImageContext.PeCoffHeaderOffset);

  //
  // Get section header list
  //
  SectionHeader = (EFI_IMAGE_SECTION_HEADER *) (
    (UINTN) ImgHdr +
    sizeof (UINT32) +
    sizeof (EFI_IMAGE_FILE_HEADER) +
    ImgHdr->Pe32.FileHeader.SizeOfOptionalHeader
    );

  //
  // Set base address into the first section header that doesn't point to code section.
  //
  for (Index = 0; Index < ImgHdr->Pe32.FileHeader.NumberOfSections; Index ++, SectionHeader ++) {
    if ((SectionHeader->Characteristics & EFI_IMAGE_SCN_CNT_CODE) == 0) {
      *(UINT64 *) &SectionHeader->PointerToRelocations = NewPe32BaseAddress;
      break;
    }
  }

  //
  // BaseAddress is set to section header.
  //
  return EFI_SUCCESS;
}

EFI_STATUS
RebaseImage (
  IN     CHAR8   *FileName,
  IN OUT UINT8   *FileBuffer,
  IN     UINT64  NewPe32BaseAddress
  )
/*++

Routine Description:

  Set new base address into PeImage, and fix up PeImage based on new address.

Arguments:

  FileName           - Name of file
  FileBuffer         - Pointer to PeImage.
  NewPe32BaseAddress - New Base Address for PE image.

Returns:

  EFI_INVALID_PARAMETER   - BaseAddress is not valid.
  EFI_SUCCESS             - Update PeImage is correctly.

--*/
{
  EFI_STATUS                            Status;
  PE_COFF_LOADER_IMAGE_CONTEXT          ImageContext;
  UINTN                                 Index;
  EFI_IMAGE_OPTIONAL_HEADER_UNION       *ImgHdr;
  UINT8                                 *MemoryImagePointer;
  EFI_IMAGE_SECTION_HEADER              *SectionHeader;

  //
  // Initialize context
  //
  memset (&ImageContext, 0, sizeof (ImageContext));
  ImageContext.Handle     = (VOID *) FileBuffer;
  ImageContext.ImageRead  = (PE_COFF_LOADER_READ_FILE) RebaseImageRead;
  Status                  = PeCoffLoaderGetImageInfo (&ImageContext);
  if (EFI_ERROR (Status)) {
    Error (NULL, 0, 3000, "Invalid", "The input PeImage %s is not valid", FileName);
    return Status;
  }

  if (ImageContext.RelocationsStripped) {
    Error (NULL, 0, 3000, "Invalid", "The input PeImage %s has no relocation to be fixed up", FileName);
    return Status;
  }

  //
  // Get PeHeader pointer
  //
  ImgHdr = (EFI_IMAGE_OPTIONAL_HEADER_UNION *)(FileBuffer + ImageContext.PeCoffHeaderOffset);

  //
  // Load and Relocate Image Data
  //
  MemoryImagePointer = (UINT8 *) malloc ((UINTN) ImageContext.ImageSize + ImageContext.SectionAlignment);
  if (MemoryImagePointer == NULL) {
    Error (NULL, 0, 4001, "Resource", "memory cannot be allocated on rebase of %s", FileName);
    return EFI_OUT_OF_RESOURCES;
  }
  memset ((VOID *) MemoryImagePointer, 0, (UINTN) ImageContext.ImageSize + ImageContext.SectionAlignment);
  ImageContext.ImageAddress = ((UINTN) MemoryImagePointer + ImageContext.SectionAlignment - 1) & (~((INT64)ImageContext.SectionAlignment - 1));

  Status =  PeCoffLoaderLoadImage (&ImageContext);
  if (EFI_ERROR (Status)) {
    Error (NULL, 0, 3000, "Invalid", "LocateImage() call failed on rebase of %s", FileName);
    free ((VOID *) MemoryImagePointer);
    return Status;
  }

  ImageContext.DestinationAddress = NewPe32BaseAddress;
  Status                          = PeCoffLoaderRelocateImage (&ImageContext);
  if (EFI_ERROR (Status)) {
    Error (NULL, 0, 3000, "Invalid", "RelocateImage() call failed on rebase of %s", FileName);
    free ((VOID *) MemoryImagePointer);
    return Status;
  }

  //
  // Copy Relocated data to raw image file.
  //
  SectionHeader = (EFI_IMAGE_SECTION_HEADER *) (
    (UINTN) ImgHdr +
    sizeof (UINT32) +
    sizeof (EFI_IMAGE_FILE_HEADER) +
    ImgHdr->Pe32.FileHeader.SizeOfOptionalHeader
    );

  for (Index = 0; Index < ImgHdr->Pe32.FileHeader.NumberOfSections; Index ++, SectionHeader ++) {
    CopyMem (
      FileBuffer + SectionHeader->PointerToRawData,
      (VOID*) (UINTN) (ImageContext.ImageAddress + SectionHeader->VirtualAddress),
      SectionHeader->SizeOfRawData
      );
  }

  free ((VOID *) MemoryImagePointer);

  //
  // Update Image Base Address
  //
  if ((ImgHdr->Pe32.OptionalHeader.Magic == EFI_IMAGE_NT_OPTIONAL_HDR32_MAGIC) && (ImgHdr->Pe32.FileHeader.Machine != IMAGE_FILE_MACHINE_IA64)) {
    ImgHdr->Pe32.OptionalHeader.ImageBase = (UINT32) NewPe32BaseAddress;
  } else if (ImgHdr->Pe32Plus.OptionalHeader.Magic == EFI_IMAGE_NT_OPTIONAL_HDR64_MAGIC) {
    ImgHdr->Pe32Plus.OptionalHeader.ImageBase = NewPe32BaseAddress;
  } else {
    Error (NULL, 0, 3000, "Invalid", "unknown PE magic signature %X in PE32 image %s",
      ImgHdr->Pe32.OptionalHeader.Magic,
      FileName
      );
    return EFI_ABORTED;
  }

  //
  // Set new base address into section header
  //
  Status = SetAddressToSectionHeader (FileName, FileBuffer, NewPe32BaseAddress);

  return Status;
}

EFI_STATUS
CombinePath (
  IN  CHAR8* DefaultPath,
  IN  CHAR8* AppendPath,
  OUT CHAR8* NewPath
)
{
  UINT32 DefaultPathLen;
  DefaultPathLen = strlen(DefaultPath);
  strcpy(NewPath, DefaultPath);
  UINT64 Index = 0;
  for (; Index < DefaultPathLen; Index ++) {
    if (NewPath[Index] == '\\' || NewPath[Index] == '/') {
      if (NewPath[Index + 1] != '\0') {
        NewPath[Index] = '/';
      }
    }
  }
  if (NewPath[Index -1] != '/') {
    NewPath[Index] = '/';
    NewPath[Index + 1] = '\0';
  }
  strcat(NewPath, AppendPath);
  return EFI_SUCCESS;
}

EFI_STATUS
ParseSection (
  IN UINT8  *SectionBuffer,
  IN UINT32 BufferLength
  )
/*++

Routine Description:

  Parses EFI Sections

Arguments:

  SectionBuffer - Buffer containing the section to parse.
  BufferLength  - Length of SectionBuffer

Returns:

  EFI_SECTION_ERROR - Problem with section parsing.
                      (a) compression errors
                      (b) unrecognized section 
  EFI_UNSUPPORTED - Do not know how to parse the section.
  EFI_SUCCESS - Section successfully parsed.
  EFI_OUT_OF_RESOURCES - Memory allocation failed.

--*/
{
  EFI_SECTION_TYPE    Type;
  UINT8               *Ptr;
  UINT32              SectionLength;
  UINT32              SectionHeaderLen;
  CHAR8               *SectionName;
  EFI_STATUS          Status;
  UINT32              ParsedLength;
  UINT8               *CompressedBuffer;
  UINT32              CompressedLength;
  UINT8               *UncompressedBuffer;
  UINT32              UncompressedLength;
  UINT8               *ToolOutputBuffer;
  UINT32              ToolOutputLength;
  UINT8               CompressionType;
  UINT32              DstSize;
  UINT32              ScratchSize;
  UINT8               *ScratchBuffer;
  DECOMPRESS_FUNCTION DecompressFunction;
  GETINFO_FUNCTION    GetInfoFunction;
  // CHAR16              *name;
  CHAR8               *ExtractionTool;
  CHAR8               *ToolInputFile;
  CHAR8               *ToolOutputFile;
  CHAR8               *SystemCommand;
  EFI_GUID            *EfiGuid;
  UINT16              DataOffset;
  UINT16              Attributes;
  UINT32              RealHdrLen;
  CHAR8               *ToolInputFileName;
  CHAR8               *ToolOutputFileName;
  CHAR8               *UIFileName;

  ParsedLength = 0;
  ToolInputFileName = NULL;
  ToolOutputFileName = NULL;

  while (ParsedLength < BufferLength) {
    Ptr           = SectionBuffer + ParsedLength;

    SectionLength = GetLength (((EFI_COMMON_SECTION_HEADER *) Ptr)->Size);
    Type          = ((EFI_COMMON_SECTION_HEADER *) Ptr)->Type;

    //
    // This is sort of an odd check, but is necessary because FFS files are
    // padded to a QWORD boundary, meaning there is potentially a whole section
    // header worth of 0xFF bytes.
    //
    if (SectionLength == 0xffffff && Type == 0xff) {
      ParsedLength += 4;
      continue;
    }

    //
    // Get real section file size
    //
    SectionLength = GetSectionFileLength ((EFI_COMMON_SECTION_HEADER *) Ptr);
    SectionHeaderLen = GetSectionHeaderLength((EFI_COMMON_SECTION_HEADER *)Ptr);

    SectionName = SectionNameToStr (Type);
    if (SectionName != NULL) {
      printf ("------------------------------------------------------------\n");
      printf ("  Type:  %s\n  Size:  0x%08X\n", SectionName, (unsigned) SectionLength);
      free (SectionName);
    }

    switch (Type) {
    case EFI_SECTION_RAW:
    case EFI_SECTION_PIC:
    case EFI_SECTION_TE:
      // default is no more information
      break;

    case EFI_SECTION_PE32:
      if (EnableHash) {
        ToolInputFileName  = "edk2Temp_InputEfi.tmp";
        ToolOutputFileName = "edk2Temp_OutputHash.tmp";
        RebaseImage(ToolInputFileName, (UINT8*)Ptr + SectionHeaderLen, 0);
        PutFileImage (
          ToolInputFileName,
          (CHAR8*)Ptr + SectionHeaderLen,
          SectionLength - SectionHeaderLen
          );

        SystemCommand = malloc (
          strlen (OPENSSL_COMMAND_FORMAT_STRING) +
          strlen (OpenSslPath) +
          strlen (ToolInputFileName) +
          strlen (ToolOutputFileName) +
          1
          );
        if (SystemCommand == NULL) {
          Error (NULL, 0, 4001, "Resource", "memory cannot be allocated!");
          return EFI_OUT_OF_RESOURCES;
        }
        sprintf (
          SystemCommand,
          OPENSSL_COMMAND_FORMAT_STRING,
          OpenSslPath,
          ToolOutputFileName,
          ToolInputFileName
          );

        if (system (SystemCommand) != EFI_SUCCESS) {
          Error (NULL, 0, 3000, "Open SSL command not available.  Please verify PATH or set OPENSSL_PATH.", NULL);
        }
        else {
          FILE *fp;
          CHAR8 *StrLine;
          CHAR8 *NewStr;
          UINT32 nFileLen;
          if((fp = fopen(ToolOutputFileName,"r")) == NULL) {
            Error (NULL, 0, 0004, "Hash the PE32 image failed.", NULL);
          }
          else {
            fseek(fp,0,SEEK_SET);
            fseek(fp,0,SEEK_END);
            nFileLen = ftell(fp);
            fseek(fp,0,SEEK_SET);
            StrLine = malloc(nFileLen);
            if (StrLine == NULL) {
              fclose(fp);
              free (SystemCommand);
              Error (NULL, 0, 4001, "Resource", "memory cannot be allocated!");
              return EFI_OUT_OF_RESOURCES;
            }
            fgets(StrLine, nFileLen, fp);
            NewStr = strrchr (StrLine, '=');
            printf ("  SHA1: %s\n", NewStr + 1);
            free (StrLine);
            fclose(fp);
          }
        }
        remove(ToolInputFileName);
        remove(ToolOutputFileName);
        free (SystemCommand);
      }
      break;

    case EFI_SECTION_USER_INTERFACE:
      UIFileName = (CHAR8 *) malloc (UnicodeStrLen (((EFI_USER_INTERFACE_SECTION *) Ptr)->FileNameString) + 1);
      if (UIFileName == NULL) {
        Error (NULL, 0, 4001, "Resource", "memory cannot be allocated!");
        return EFI_OUT_OF_RESOURCES;
      }
      Unicode2AsciiString (((EFI_USER_INTERFACE_SECTION *) Ptr)->FileNameString, UIFileName);
      printf ("  String: %s\n", UIFileName);
      free (UIFileName);
      break;

    case EFI_SECTION_FIRMWARE_VOLUME_IMAGE:
      Status = PrintFvInfo (Ptr + SectionHeaderLen, TRUE);
      if (EFI_ERROR (Status)) {
        Error (NULL, 0, 0003, "printing of FV section contents failed", NULL);
        return EFI_SECTION_ERROR;
      }
      break;

    case EFI_SECTION_COMPATIBILITY16:
    case EFI_SECTION_FREEFORM_SUBTYPE_GUID:
      //
      // Section does not contain any further header information.
      //
      break;

    case EFI_SECTION_PEI_DEPEX:
    case EFI_SECTION_DXE_DEPEX:
    case EFI_SECTION_SMM_DEPEX:
      DumpDepexSection (Ptr, SectionLength);
      break;

    case EFI_SECTION_VERSION:
      printf ("  Build Number:  0x%02X\n", *(UINT16 *)(Ptr + SectionHeaderLen));
      printf ("  Version Strg:  %s\n", (char*) (Ptr + SectionHeaderLen + sizeof (UINT16)));
      break;

    case EFI_SECTION_COMPRESSION:
      UncompressedBuffer  = NULL;
      if (SectionHeaderLen == sizeof (EFI_COMMON_SECTION_HEADER)) {
        RealHdrLen = sizeof(EFI_COMPRESSION_SECTION);
        UncompressedLength  = ((EFI_COMPRESSION_SECTION *)Ptr)->UncompressedLength;
        CompressionType     = ((EFI_COMPRESSION_SECTION *)Ptr)->CompressionType;
      } else {
        RealHdrLen = sizeof(EFI_COMPRESSION_SECTION2);
        UncompressedLength  = ((EFI_COMPRESSION_SECTION2 *)Ptr)->UncompressedLength;
        CompressionType     = ((EFI_COMPRESSION_SECTION2 *)Ptr)->CompressionType;
      }
      CompressedLength    = SectionLength - RealHdrLen;
      printf ("  Uncompressed Length:  0x%08X\n", (unsigned) UncompressedLength);

      if (CompressionType == EFI_NOT_COMPRESSED) {
        printf ("  Compression Type:  EFI_NOT_COMPRESSED\n");
        if (CompressedLength != UncompressedLength) {
          Error (
            NULL,
            0,
            0,
            "file is not compressed, but the compressed length does not match the uncompressed length",
            NULL
            );
          return EFI_SECTION_ERROR;
        }

        UncompressedBuffer = Ptr + RealHdrLen;
      } else if (CompressionType == EFI_STANDARD_COMPRESSION) {
        GetInfoFunction     = EfiGetInfo;
        DecompressFunction  = EfiDecompress;
        printf ("  Compression Type:  EFI_STANDARD_COMPRESSION\n");

        CompressedBuffer  = Ptr + RealHdrLen;

        Status            = GetInfoFunction (CompressedBuffer, CompressedLength, &DstSize, &ScratchSize);
        if (EFI_ERROR (Status)) {
          Error (NULL, 0, 0003, "error getting compression info from compression section", NULL);
          return EFI_SECTION_ERROR;
        }

        if (DstSize != UncompressedLength) {
          Error (NULL, 0, 0003, "compression error in the compression section", NULL);
          return EFI_SECTION_ERROR;
        }

        ScratchBuffer       = malloc (ScratchSize);
        if (ScratchBuffer == NULL) {
          Error (NULL, 0, 4001, "Resource", "memory cannot be allocated!");
          return EFI_OUT_OF_RESOURCES;
        }
        UncompressedBuffer  = malloc (UncompressedLength);
        if (UncompressedBuffer == NULL) {
          free (ScratchBuffer);
          Error (NULL, 0, 4001, "Resource", "memory cannot be allocated!");
          return EFI_OUT_OF_RESOURCES;
        }
        Status = DecompressFunction (
                  CompressedBuffer,
                  CompressedLength,
                  UncompressedBuffer,
                  UncompressedLength,
                  ScratchBuffer,
                  ScratchSize
                  );
        free (ScratchBuffer);
        if (EFI_ERROR (Status)) {
          Error (NULL, 0, 0003, "decompress failed", NULL);
          free (UncompressedBuffer);
          return EFI_SECTION_ERROR;
        }
      } else {
        Error (NULL, 0, 0003, "unrecognized compression type", "type 0x%X", CompressionType);
        return EFI_SECTION_ERROR;
      }

      Status = ParseSection (UncompressedBuffer, UncompressedLength);

      if (CompressionType == EFI_STANDARD_COMPRESSION) {
        //
        // We need to deallocate Buffer
        //
        free (UncompressedBuffer);
      }

      if (EFI_ERROR (Status)) {
        Error (NULL, 0, 0003, "failed to parse section", NULL);
        return EFI_SECTION_ERROR;
      }
      break;

    case EFI_SECTION_GUID_DEFINED:
      if (SectionHeaderLen == sizeof(EFI_COMMON_SECTION_HEADER)) {
        EfiGuid = &((EFI_GUID_DEFINED_SECTION *) Ptr)->SectionDefinitionGuid;
        DataOffset = ((EFI_GUID_DEFINED_SECTION *) Ptr)->DataOffset;
        Attributes = ((EFI_GUID_DEFINED_SECTION *) Ptr)->Attributes;
      } else {
        EfiGuid = &((EFI_GUID_DEFINED_SECTION2 *) Ptr)->SectionDefinitionGuid;
        DataOffset = ((EFI_GUID_DEFINED_SECTION2 *) Ptr)->DataOffset;
        Attributes = ((EFI_GUID_DEFINED_SECTION2 *) Ptr)->Attributes;
      }
      printf ("  SectionDefinitionGuid:  ");
      PrintGuid (EfiGuid);
      printf ("\n");
      printf ("  DataOffset:             0x%04X\n", (unsigned) DataOffset);
      printf ("  Attributes:             0x%04X\n", (unsigned) Attributes);

      ExtractionTool =
        LookupGuidedSectionToolPath (
          mParsedGuidedSectionTools,
          EfiGuid
          );

      if (ExtractionTool != NULL) {
       #ifndef __GNUC__
        ToolInputFile = CloneString (tmpnam (NULL));
        ToolOutputFile = CloneString (tmpnam (NULL));
       #else
        char tmp1[] = "/tmp/fileXXXXXX";
        char tmp2[] = "/tmp/fileXXXXXX";
        int fd1;
        int fd2;
        fd1 = mkstemp(tmp1);
        fd2 = mkstemp(tmp2);
        ToolInputFile = CloneString(tmp1);
        ToolOutputFile = CloneString(tmp2);
        close(fd1);
        close(fd2);
       #endif

        if ((ToolInputFile == NULL) || (ToolOutputFile == NULL)) {
          if (ToolInputFile != NULL) {
            free (ToolInputFile);
          }
          if (ToolOutputFile != NULL) {
            free (ToolOutputFile);
          }
          free (ExtractionTool);

          Error (NULL, 0, 4001, "Resource", "memory cannot be allocated!");
          return EFI_OUT_OF_RESOURCES;
        }

        //
        // Construction 'system' command string
        //
        SystemCommand = malloc (
          strlen (EXTRACT_COMMAND_FORMAT_STRING) +
          strlen (ExtractionTool) +
          strlen (ToolInputFile) +
          strlen (ToolOutputFile) +
          1
          );
        if (SystemCommand == NULL) {
          free (ToolInputFile);
          free (ToolOutputFile);
          free (ExtractionTool);

          Error (NULL, 0, 4001, "Resource", "memory cannot be allocated!");
          return EFI_OUT_OF_RESOURCES;
        }
        sprintf (
          SystemCommand,
          EXTRACT_COMMAND_FORMAT_STRING,
          ExtractionTool,
          ToolOutputFile,
          ToolInputFile
          );
        free (ExtractionTool);

        Status =
          PutFileImage (
            ToolInputFile,
            (CHAR8*) SectionBuffer + DataOffset,
            BufferLength - DataOffset
            );

        system (SystemCommand);
        remove (ToolInputFile);
        free (ToolInputFile);

        Status =
          GetFileImage (
            ToolOutputFile,
            (CHAR8 **)&ToolOutputBuffer,
            &ToolOutputLength
            );
        remove (ToolOutputFile);
        free (ToolOutputFile);
        free (SystemCommand);
        if (EFI_ERROR (Status)) {
          Error (NULL, 0, 0004, "unable to read decoded GUIDED section", NULL);
          return EFI_SECTION_ERROR;
        }

        Status = ParseSection (
                  ToolOutputBuffer,
                  ToolOutputLength
                  );
        if (EFI_ERROR (Status)) {
          Error (NULL, 0, 0003, "parse of decoded GUIDED section failed", NULL);
          return EFI_SECTION_ERROR;
        }

      //
      // Check for CRC32 sections which we can handle internally if needed.
      //
      } else if (!CompareGuid (
                   EfiGuid,
                   &gEfiCrc32GuidedSectionExtractionProtocolGuid
                   )
          ) {
        //
        // CRC32 guided section
        //
        Status = ParseSection (
                  SectionBuffer + DataOffset,
                  BufferLength - DataOffset
                  );
        if (EFI_ERROR (Status)) {
          Error (NULL, 0, 0003, "parse of CRC32 GUIDED section failed", NULL);
          return EFI_SECTION_ERROR;
        }
      } else {
        //
        // We don't know how to parse it now.
        //
        Error (NULL, 0, 0003, "Error parsing section", \
        "EFI_SECTION_GUID_DEFINED cannot be parsed at this time. Tool to decode this section should have been defined in GuidedSectionTools.txt (built in the FV directory).");
        return EFI_UNSUPPORTED;
      }
      break;

    default:
      //
      // Unknown section, return error
      //
      Error (NULL, 0, 0003, "unrecognized section type found", "section type = 0x%X", Type);
      return EFI_SECTION_ERROR;
    }

    ParsedLength += SectionLength;
    //
    // We make then next section begin on a 4-byte boundary
    //
    ParsedLength = GetOccupiedSize (ParsedLength, 4);
  }

  if (ParsedLength < BufferLength) {
    Error (NULL, 0, 0003, "sections do not completely fill the sectioned buffer being parsed", NULL);
    return EFI_SECTION_ERROR;
  }

  return EFI_SUCCESS;
}

EFI_STATUS
DumpDepexSection (
  IN UINT8    *Ptr,
  IN UINT32   SectionLength
  )
/*++

Routine Description:

  GC_TODO: Add function description

Arguments:

  Ptr           - GC_TODO: add argument description
  SectionLength - GC_TODO: add argument description

Returns:

  EFI_SUCCESS - GC_TODO: Add description for return value

--*/
{
  UINT8 GuidBuffer[PRINTED_GUID_BUFFER_SIZE];

  //
  // Need at least a section header + data
  //
  if (SectionLength <= sizeof (EFI_COMMON_SECTION_HEADER)) {
    return EFI_SUCCESS;
  }

  Ptr += GetSectionHeaderLength((EFI_COMMON_SECTION_HEADER *)Ptr);
  SectionLength -= GetSectionHeaderLength((EFI_COMMON_SECTION_HEADER *)Ptr);
  while (SectionLength > 0) {
    printf ("        ");
    switch (*Ptr) {
    case EFI_DEP_BEFORE:
      printf ("BEFORE\n");
      Ptr++;
      SectionLength--;
      break;

    case EFI_DEP_AFTER:
      printf ("AFTER\n");
      Ptr++;
      SectionLength--;
      break;

    case EFI_DEP_PUSH:
      printf ("PUSH\n        ");
      PrintGuidToBuffer ((EFI_GUID *) (Ptr + 1), GuidBuffer, sizeof (GuidBuffer), TRUE);
      printf ("%s  ", GuidBuffer);
      PrintGuidName (GuidBuffer);
      printf ("\n");
      //
      // PrintGuid ((EFI_GUID *)(Ptr + 1));
      //
      Ptr += 17;
      SectionLength -= 17;
      break;

    case EFI_DEP_AND:
      printf ("AND\n");
      Ptr++;
      SectionLength--;
      break;

    case EFI_DEP_OR:
      printf ("OR\n");
      Ptr++;
      SectionLength--;
      break;

    case EFI_DEP_NOT:
      printf ("NOT\n");
      Ptr++;
      SectionLength--;
      break;

    case EFI_DEP_TRUE:
      printf ("TRUE\n");
      Ptr++;
      SectionLength--;
      break;

    case EFI_DEP_FALSE:
      printf ("FALSE\n");
      Ptr++;
      SectionLength--;
      break;

    case EFI_DEP_END:
      printf ("END DEPEX\n");
      Ptr++;
      SectionLength--;
      break;

    case EFI_DEP_SOR:
      printf ("SOR\n");
      Ptr++;
      SectionLength--;
      break;

    default:
      printf ("Unrecognized byte in depex: 0x%X\n", *Ptr);
      return EFI_SUCCESS;
    }
  }

  return EFI_SUCCESS;
}

EFI_STATUS
PrintGuidName (
  IN UINT8    *GuidStr
  )
/*++

Routine Description:

  GC_TODO: Add function description

Arguments:

  GuidStr - GC_TODO: add argument description

Returns:

  EFI_SUCCESS - GC_TODO: Add description for return value
  EFI_INVALID_PARAMETER - GC_TODO: Add description for return value

--*/
{
  GUID_TO_BASENAME  *GPtr;
  //
  // If we have a list of guid-to-basenames, then go through the list to
  // look for a guid string match. If found, print the basename to stdout,
  // otherwise return a failure.
  //
  GPtr = mGuidBaseNameList;
  while (GPtr != NULL) {
    if (_stricmp ((CHAR8*) GuidStr, (CHAR8*) GPtr->Guid) == 0) {
      printf ("%s", GPtr->BaseName);
      return EFI_SUCCESS;
    }

    GPtr = GPtr->Next;
  }

  return EFI_INVALID_PARAMETER;
}

EFI_STATUS
ParseGuidBaseNameFile (
  CHAR8    *FileName
  )
/*++

Routine Description:

  GC_TODO: Add function description

Arguments:

  FileName  - GC_TODO: add argument description

Returns:

  EFI_DEVICE_ERROR - GC_TODO: Add description for return value
  EFI_OUT_OF_RESOURCES - GC_TODO: Add description for return value
  EFI_SUCCESS - GC_TODO: Add description for return value

--*/
{
  FILE              *Fptr;
  CHAR8             Line[MAX_LINE_LEN];
  CHAR8             *FormatString;
  INTN              FormatLength;
  GUID_TO_BASENAME  *GPtr;

  if ((Fptr = fopen (LongFilePath (FileName), "r")) == NULL) {
    printf ("ERROR: Failed to open input cross-reference file '%s'\n", FileName);
    return EFI_DEVICE_ERROR;
  }

  //
  // Generate the format string for fscanf
  //
  FormatLength = snprintf (
                   NULL,
                   0,
                   "%%%us %%%us",
                   (unsigned) sizeof (GPtr->Guid) - 1,
                   (unsigned) sizeof (GPtr->BaseName) - 1
                   ) + 1;

  FormatString = (CHAR8 *) malloc (FormatLength);
  if (FormatString == NULL) {
    fclose (Fptr);
    return EFI_OUT_OF_RESOURCES;
  }

  snprintf (
    FormatString,
    FormatLength,
    "%%%us %%%us",
    (unsigned) sizeof (GPtr->Guid) - 1,
    (unsigned) sizeof (GPtr->BaseName) - 1
    );

  while (fgets (Line, sizeof (Line), Fptr) != NULL) {
    //
    // Allocate space for another guid/basename element
    //
    GPtr = malloc (sizeof (GUID_TO_BASENAME));
    if (GPtr == NULL) {
      free (FormatString);
      fclose (Fptr);
      return EFI_OUT_OF_RESOURCES;
    }

    memset ((char *) GPtr, 0, sizeof (GUID_TO_BASENAME));
    if (sscanf (Line, FormatString, GPtr->Guid, GPtr->BaseName) == 2) {
      GPtr->Next        = mGuidBaseNameList;
      mGuidBaseNameList = GPtr;
    } else {
      //
      // Some sort of error. Just continue.
      //
      free (GPtr);
    }
  }

  free (FormatString);
  fclose (Fptr);
  return EFI_SUCCESS;
}

EFI_STATUS
FreeGuidBaseNameList (
  VOID
  )
/*++

Routine Description:

  GC_TODO: Add function description

Arguments:

  None

Returns:

  EFI_SUCCESS - GC_TODO: Add description for return value

--*/
{
  GUID_TO_BASENAME  *Next;

  while (mGuidBaseNameList != NULL) {
    Next = mGuidBaseNameList->Next;
    free (mGuidBaseNameList);
    mGuidBaseNameList = Next;
  }

  return EFI_SUCCESS;
}


static
VOID
LoadGuidedSectionToolsTxt (
  IN CHAR8* FirmwareVolumeFilename
  )
{
  CHAR8* PeerFilename;
  CHAR8* Places[] = {
    NULL,
    //NULL,
    };
  UINTN Index;

  Places[0] = FirmwareVolumeFilename;
  //Places[1] = mUtilityFilename;

  mParsedGuidedSectionTools = NULL;

  for (Index = 0; Index < (sizeof(Places)/sizeof(Places[0])); Index++) {
    PeerFilename = OsPathPeerFilePath (Places[Index], "GuidedSectionTools.txt");
    //printf("Loading %s...\n", PeerFilename);
    if (OsPathExists (PeerFilename)) {
      mParsedGuidedSectionTools = ParseGuidedSectionToolsFile (PeerFilename);
    }
    free (PeerFilename);
    if (mParsedGuidedSectionTools != NULL) {
      return;
    }
  }
}


void
Usage (
  VOID
  )
/*++

Routine Description:

  GC_TODO: Add function description

Arguments:

  None

Returns:

  GC_TODO: add return values

--*/
{
  //
  // Summary usage
  //
  fprintf (stdout, "Usage: %s [options] <input_file>\n\n", UTILITY_NAME);

  //
  // Copyright declaration
  // 
  fprintf (stdout, "Copyright (c) 2007 - 2016, Intel Corporation. All rights reserved.\n\n");
  fprintf (stdout, "  Display Tiano Firmware Volume FFS image information\n\n");

  //
  // Details Option
  //
  fprintf (stdout, "optional arguments:\n");
  fprintf (stdout, "  -h, --help\n\
            Show this help message and exit\n");
  fprintf (stdout, "  --version\n\
           Show program's version number and exit\n");
  fprintf (stdout, "  -d [DEBUG], --debug [DEBUG]\n\
            Output DEBUG statements, where DEBUG_LEVEL is 0 (min) - 9 (max)\n");
  fprintf (stdout, "  -v, --verbose\n\
            Print informational statements\n");
  fprintf (stdout, "  -q, --quiet\n\
            Returns the exit code, error messages will be displayed\n");
  fprintf (stdout, "  -s, --silent\n\
            Returns only the exit code; informational and error\n\
            messages are not displayed\n");
  fprintf (stdout, "  -x XREF_FILENAME, --xref XREF_FILENAME\n\
            Parse the basename to file-guid cross reference file(s)\n");
  fprintf (stdout, "  -f OFFSET, --offset OFFSET\n\
            The offset from the start of the input file to start \n\
            processing an FV\n");
  fprintf (stdout, "  --hash\n\
            Generate HASH value of the entire PE image\n");
  fprintf (stdout, "  --sfo\n\
            Reserved for future use\n");
}