/**@file
This utility is part of build process for IA32/X64 FD.
It generates FIT table.

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

**/

#include "FitGen.h"

//
// FIT spec
//
#pragma pack (1)
typedef struct {
  UINT64     Address;
  UINT8      Size[3];
  UINT8      Rsvd;
  UINT16     Version;
  UINT8      Type:7;
  UINT8      C_V:1;
  UINT8      Checksum;
} FIRMWARE_INTERFACE_TABLE_ENTRY;

//
// Struct for policy
//
typedef struct {
  UINT16     IndexPort;
  UINT16     DataPort;
  UINT8      Width;
  UINT8      Bit;
  UINT16     Index;
  UINT8      Size[3];
  UINT8      Rsvd;
  UINT16     Version; // == 0
  UINT8      Type:7;
  UINT8      C_V:1;
  UINT8      Checksum;
} FIRMWARE_INTERFACE_TABLE_ENTRY_PORT;

#define FIT_ALIGNMENT          0x3F // 0xF is required previously, but if we need exclude FIT, we must set 64 bytes alignment.
#define BIOS_MODULE_ALIGNMENT  0x3F  // 64 bytes for AnC
#define MICROCODE_ALIGNMENT    0x7FF

#define ACM_PKCS_1_5_RSA_SIGNATURE_SHA256_SIZE          256
#define ACM_PKCS_1_5_RSA_SIGNATURE_SHA384_SIZE          384

#define ACM_HEADER_VERSION_3  (3 << 16)
#define ACM_MODULE_TYPE_CHIPSET_ACM                     2
#define ACM_MODULE_SUBTYPE_CAPABLE_OF_EXECUTE_AT_RESET  0x1
#define ACM_MODULE_SUBTYPE_ANC_MODULE                   0x2
#define ACM_MODULE_FLAG_PREPRODUCTION                   0x4000
#define ACM_MODULE_FLAG_DEBUG_SIGN                      0x8000


typedef struct {
  UINT16     ModuleType;
  UINT16     ModuleSubType;
  UINT32     HeaderLen;
  UINT32     HeaderVersion;
  UINT16     ChipsetID;
  UINT16     Flags;
  UINT32     ModuleVendor;
  UINT32     Date;
  UINT32     Size;
  UINT16     TxtSvn;
  UINT16     SeSvn;
  UINT32     CodeControl;
  UINT32     ErrorEntryPoint;
  UINT32     GDTLimit;
  UINT32     GDTBasePtr;
  UINT32     SegSel;
  UINT32     EntryPoint;
  UINT8      Rsvd2[64];
  UINT32     KeySize; // 64
  UINT32     ScratchSize; // 2 * KeySize + 15
//UINT8      RSAPubKey[64 * 4]; // KeySize * 4
//UINT32     RSAPubExp;
//UINT8      RSASig[256];
  // End of AC module header
//UINT8      Scratch[(64 * 2 + 15) * 4]; // ScratchSize * 4
  // User Area
//UINT8      UserArea[1];
} ACM_FORMAT;

#define CHIPSET_ACM_INFORMATION_TABLE_VERSION_3  0x03
#define CHIPSET_ACM_INFORMATION_TABLE_VERSION_4  0x04

#define CHIPSET_ACM_INFORMATION_TABLE_VERSION    CHIPSET_ACM_INFORMATION_TABLE_VERSION_3

#define CHIPSET_ACM_INFORMATION_TABLE_GUID_V03 \
  { 0x7FC03AAA, 0x18DB46A7, 0x8F69AC2E, 0x5A7F418D }

#define CHIPSET_ACM_TYPE_BIOS   0
#define CHIPSET_ACM_TYPE_SINIT  1

typedef struct {
  UINT32    Guid0;
  UINT32    Guid1;
  UINT32    Guid2;
  UINT32    Guid3;
} ACM_GUID;

typedef struct {
  ACM_GUID   Guid;
  UINT8      ChipsetACMType;
  UINT8      Version;
  UINT16     Length;
  UINT32     ChipsetIDList;
  UINT32     OsSinitTableVer;
  UINT32     MinMleHeaderVer;
//#if (CHIPSET_ACM_INFORMATION_TABLE_VERSION >= CHIPSET_ACM_INFORMATION_TABLE_VERSION_3)
  UINT32     Capabilities;
  UINT8      AcmVersion;
  UINT8      AcmRevision[3];
//#if (CHIPSET_ACM_INFORMATION_TABLE_VERSION >= CHIPSET_ACM_INFORMATION_TABLE_VERSION_4)
  UINT32     ProcessorIDList;
//#endif
//#endif
} CHIPSET_ACM_INFORMATION_TABLE;

#define ACM_CHIPSET_ID_REVISION_ID_MAKE  0x1

typedef struct {
  UINT32     Flags;
  UINT16     VendorID;
  UINT16     DeviceID;
  UINT16     RevisionID;
  UINT8      Reserved[6];
} ACM_CHIPSET_ID;

typedef struct {
  UINT32           Count;
  ACM_CHIPSET_ID   ChipsetID[1];
} CHIPSET_ID_LIST;

typedef struct {
  UINT32     FMS;
  UINT32     FMSMask;
  UINT64     PlatformID;
  UINT64     PlatformMask;
} ACM_PROCESSOR_ID;

typedef struct {
  UINT32           Count;
  ACM_PROCESSOR_ID ProcessorID[1];
} PROCESSOR_ID_LIST;

#pragma pack ()


ACM_GUID mChipsetAcmInformationTableGuid03 = CHIPSET_ACM_INFORMATION_TABLE_GUID_V03;


//
// BIOS INFO data structure
// This is self contained data structure for BIOS info
//
#pragma pack (1)
#define BIOS_INFO_SIGNATURE  SIGNATURE_64 ('$', 'B', 'I', 'O', 'S', 'I', 'F', '$')
typedef struct {
  UINT64            Signature;
  UINT32            EntryCount;
  UINT32            Reserved;
//BIOS_INFO_STRUCT  Struct[EntryCount];
} BIOS_INFO_HEADER;

//
// BIOS_INFO_STRUCT attributes
// bits[0:3] means general attributes
// bits[4:7] means type specific attributes
//
#define BIOS_INFO_STRUCT_ATTRIBUTE_GENERAL_EXCLUDE_FROM_FIT  0x01
#define BIOS_INFO_STRUCT_ATTRIBUTE_MICROCODE_WHOLE_REGION    0x10
#define BIOS_INFO_STRUCT_ATTRIBUTE_BIOS_POST_IBB             0x10

typedef struct {
  //
  // FitTable entry type
  //
  UINT8    Type;
  //
  // BIOS_INFO_STRUCT attributes
  //
  UINT8    Attributes;
  //
  // FitTable entry version
  //
  UINT16   Version;
  //
  // FitTable entry real size
  //
  UINT32   Size;
  //
  // FitTable entry address
  //
  UINT64   Address;
} BIOS_INFO_STRUCT;

#pragma pack ()

#define MAX_BIOS_MODULE_ENTRY  0x20
#define MAX_MICROCODE_ENTRY    0x20
#define MAX_OPTIONAL_ENTRY     0x20
#define MAX_PORT_ENTRY         0x20

#define DEFAULT_FIT_TABLE_POINTER_OFFSET  0x40
#define DEFAULT_FIT_ENTRY_VERSION         0x0100

#define MEMORY_TO_FLASH(FileBuffer, FvBuffer, FvSize)  \
                 (UINTN)(0x100000000 - ((UINTN)(FvBuffer) + (UINTN)(FvSize) - (UINTN)(FileBuffer)))
#define FLASH_TO_MEMORY(Address, FvBuffer, FvSize)  \
                 (VOID *)(UINTN)((UINTN)(FvBuffer) + (UINTN)(FvSize) - (0x100000000 - (UINTN)(Address)))

#define FIT_TABLE_TYPE_HEADER                 0
#define FIT_TABLE_TYPE_MICROCODE              1
#define FIT_TABLE_TYPE_STARTUP_ACM            2
#define FIT_TABLE_TYPE_BIOS_MODULE            7
#define FIT_TABLE_TYPE_TPM_POLICY             8
#define FIT_TABLE_TYPE_BIOS_POLICY            9
#define FIT_TABLE_TYPE_TXT_POLICY             10
#define FIT_TABLE_TYPE_KEY_MANIFEST           11
#define FIT_TABLE_TYPE_BOOT_POLICY_MANIFEST   12
#define FIT_TABLE_TYPE_BIOS_DATA_AREA         13
#define FIT_TABLE_TYPE_CSE_SECURE_BOOT        16

typedef struct {
  UINT32  Type;
  UINT32  Address;
  UINT32  Size;
  UINT32  Version; // Used by OptionalModule and PortModule only
} FIT_TABLE_CONTEXT_ENTRY;

typedef struct {
  BOOLEAN                    Clear;
  UINT32                     FitTablePointerOffset;
  UINT32                     FitTablePointerOffset2;
  UINT32                     FitEntryNumber;
  UINT32                     BiosModuleNumber;
  UINT32                     MicrocodeNumber;
  UINT32                     OptionalModuleNumber;
  UINT32                     PortModuleNumber;
  UINT32                     GlobalVersion;
  UINT32                     FitHeaderVersion;
  FIT_TABLE_CONTEXT_ENTRY    StartupAcm;
  UINT32                     StartupAcmVersion;
  FIT_TABLE_CONTEXT_ENTRY    BiosModule[MAX_BIOS_MODULE_ENTRY];
  UINT32                     BiosModuleVersion;
  FIT_TABLE_CONTEXT_ENTRY    Microcode[MAX_MICROCODE_ENTRY];
  BOOLEAN                    MicrocodeAlignment;
  UINT32                     MicrocodeVersion;
  FIT_TABLE_CONTEXT_ENTRY    OptionalModule[MAX_OPTIONAL_ENTRY];
  FIT_TABLE_CONTEXT_ENTRY    PortModule[MAX_PORT_ENTRY];
} FIT_TABLE_CONTEXT;

FIT_TABLE_CONTEXT   gFitTableContext = {0};

unsigned int
xtoi (
  char  *str
  );

VOID
PrintUtilityInfo (
  VOID
  )
/*++

Routine Description:

  Displays the standard utility information to STDOUT

Arguments:

  None

Returns:

  None

--*/
{
  printf (
    "%s - Tiano IA32/X64 FIT table generation Utility."" Version %i.%i\n\n",
    UTILITY_NAME,
    UTILITY_MAJOR_VERSION,
    UTILITY_MINOR_VERSION
    );
}

VOID
PrintUsage (
  VOID
  )
/*++

Routine Description:

  Displays the utility usage syntax to STDOUT

Arguments:

  None

Returns:

  None

--*/
{
  printf ("Usage (generate): %s [-D] InputFvRecoveryFile OutputFvRecoveryFile\n"
          "\t[-V <FitEntryDefaultVersion>]\n"
          "\t[-F <FitTablePointerOffset>] [-F <FitTablePointerOffset>] [-V <FitHeaderVersion>]\n"
          "\t[-NA]\n"
          "\t[-CLEAR]\n"
          "\t[-I <BiosInfoGuid>]\n"
          "\t[-S <StartupAcmAddress StartupAcmSize>|<StartupAcmGuid>] [-V <StartupAcmVersion>]\n"
          "\t[-B <BiosModuleAddress BiosModuleSize>] [-B ...] [-V <BiosModuleVersion>]\n"
          "\t[-M <MicrocodeAddress MicrocodeSize>] [-M ...]|[-U <MicrocodeFv MicrocodeBase>|<MicrocodeRegionOffset MicrocodeRegionSize>|<MicrocodeGuid>] [-V <MicrocodeVersion>]\n"
          "\t[-O RecordType <RecordDataAddress RecordDataSize>|<RESERVE RecordDataSize>|<RecordDataGuid>|<RecordBinFile> [-V <RecordVersion>]] [-O ... [-V ...]]\n"
          "\t[-P RecordType <IndexPort DataPort Width Bit Index> [-V <RecordVersion>]] [-P ... [-V ...]]\n"
          , UTILITY_NAME);
  printf ("  Where:\n");
  printf ("\t-D                     - It is FD file instead of FV file. (The tool will search FV file)\n");
  printf ("\tInputFvRecoveryFile    - Name of the input FvRecovery.fv file.\n");
  printf ("\tOutputFvRecoveryFile   - Name of the output FvRecovery.fv file.\n");
  printf ("\tFitTablePointerOffset  - FIT table pointer offset. 0x%x as default. 0x18 for current soon to be obsoleted CPUs. User can set both.\n", DEFAULT_FIT_TABLE_POINTER_OFFSET);
  printf ("\tBiosInfoGuid           - Guid of BiosInfo Module. If this module exists, StartupAcm/Bios/Microcode can be optional.\n");
  printf ("\tStartupAcmAddress      - Address of StartupAcm.\n");
  printf ("\tStartupAcmSize         - Size of StartupAcm.\n");
  printf ("\tStartupAcmGuid         - Guid of StartupAcm Module, if StartupAcm is in a BiosModule, it will be excluded form that.\n");
  printf ("\tBiosModuleAddress      - Address of BiosModule. User should ensure there is no overlap.\n");
  printf ("\tBiosModuleSize         - Size of BiosModule.\n");
  printf ("\tMicrocodeAddress       - Address of Microcode.\n");
  printf ("\tMicrocodeSize          - Size of Microcode.\n");
  printf ("\tMicrocodeFv            - Name of Microcode.fv file.\n");
  printf ("\tMicrocodeBase          - The base address of Microcode.fv in final FD image.\n");
  printf ("\tMicrocodeRegionOffset  - Offset of Microcode region in input FD image.\n");
  printf ("\tMicrocodeRegionSize    - Size of Microcode region in input FD image.\n");
  printf ("\tMicrocodeGuid          - Guid of Microcode Module.\n");
  printf ("\t-NA                    - No 0x800 aligned Microcode requirement. No -NA means Microcode is 0x800 aligned.\n");
  printf ("\tRecordType             - FIT entry record type. User should ensure it is ordered.\n");
  printf ("\tRecordDataAddress      - FIT entry record data address.\n");
  printf ("\tRecordDataSize         - FIT entry record data size.\n");
  printf ("\tRecordDataGuid         - FIT entry record data GUID.\n");
  printf ("\tRecordBinFile          - FIT entry record data binary file.\n");
  printf ("\tFitEntryDefaultVersion - The default version for all FIT table entries. 0x%04x is used if this is not specified.\n", DEFAULT_FIT_ENTRY_VERSION);
  printf ("\tFitHeaderVersion       - The version for FIT header. (Override default version)\n");
  printf ("\tStartupAcmVersion      - The version for StartupAcm. (Override default version)\n");
  printf ("\tBiosModuleVersion      - The version for BiosModule. (Override default version)\n");
  printf ("\tMicrocodeVersion       - The version for Microcode. (Override default version)\n");
  printf ("\tRecordVersion          - The version for Record. (Override default version)\n");
  printf ("\tIndexPort              - The Index Port Number.\n");
  printf ("\tDataPort               - The Data Port Number.\n");
  printf ("\tWidth                  - The Width of the port.\n");
  printf ("\tBit                    - The Bit Number of the port.\n");
  printf ("\tIndex                  - The Index Number of the port.\n");
  printf ("\nUsage (view): %s [-view] InputFile -F <FitTablePointerOffset>\n", UTILITY_NAME);
  printf ("  Where:\n");
  printf ("\tInputFile              - Name of the input file.\n");
  printf ("\tFitTablePointerOffset  - FIT table pointer offset from end of file. 0x%x as default.\n", DEFAULT_FIT_TABLE_POINTER_OFFSET);
  printf ("\nTool return values:\n");
  printf ("\tSTATUS_SUCCESS=%d, STATUS_WARNING=%d, STATUS_ERROR=%d\n", STATUS_SUCCESS, STATUS_WARNING, STATUS_ERROR);
}

VOID *
SetMem (
  OUT     VOID                      *Buffer,
  IN      UINTN                     Length,
  IN      UINT8                     Value
  )
{
  //
  // Declare the local variables that actually move the data elements as
  // volatile to prevent the optimizer from replacing this function with
  // the intrinsic memset()
  //
  volatile UINT8                    *Pointer;

  Pointer = (UINT8*)Buffer;
  while (Length-- > 0) {
    *(Pointer++) = Value;
  }
  return Buffer;
}

STATUS
ReadInputFile (
  IN CHAR8    *FileName,
  OUT UINT8   **FileData,
  OUT UINT32  *FileSize,
  OUT UINT8   **FileBufferRaw OPTIONAL
  )
/*++

Routine Description:

  Read input file

Arguments:

  FileName      - The input file name
  FileData      - The input file data, the memory is aligned.
  FileSize      - The input file size
  FileBufferRaw - The memory to hold input file data. The caller must free the memory.

Returns:

  STATUS_SUCCESS - The file found and data read
  STATUS_ERROR   - The file data is not read
  STATUS_WARNING - The file is not found

--*/
{
  FILE                        *FpIn;
  UINT32                      TempResult;

  //
  // Open the Input FvRecovery.fv file
  //
  if ((FpIn = fopen (FileName, "rb")) == NULL) {
    //
    // Return WARNING, let caller make decision
    //
//    Error (NULL, 0, 0, "Unable to open file", FileName);
    return STATUS_WARNING;
  }
  //
  // Get the Input FvRecovery.fv file size
  //
  fseek (FpIn, 0, SEEK_END);
  *FileSize = ftell (FpIn);
  //
  // Read the contents of input file to memory buffer
  //
  if (FileBufferRaw != NULL) {
    *FileBufferRaw = (UINT8 *) malloc (*FileSize + 0x10000);
    if (NULL == *FileBufferRaw) {
      Error (NULL, 0, 0, "No sufficient memory to allocate!", NULL);
      fclose (FpIn);
      return STATUS_ERROR;
    }
    TempResult = 0x10000 - (UINT32) ((UINTN)*FileBufferRaw & 0x0FFFF);
    *FileData = (UINT8 *)((UINTN)*FileBufferRaw + TempResult);
  } else {
    *FileData = (UINT8 *) malloc (*FileSize);
     if (NULL == *FileData) {
      Error (NULL, 0, 0, "No sufficient memory to allocate!", NULL);
      fclose (FpIn);
      return STATUS_ERROR;
    }
  }
  fseek (FpIn, 0, SEEK_SET);
  TempResult = fread (*FileData, 1, *FileSize, FpIn);
  if (TempResult != *FileSize) {
    Error (NULL, 0, 0, "Read input file error!", NULL);
    if (FileBufferRaw != NULL) {
      free ((VOID *)*FileBufferRaw);
    } else {
      free ((VOID *)*FileData);
    }
    fclose (FpIn);
    return STATUS_ERROR;
  }

  //
  // Close the input FvRecovery.fv file
  //
  fclose (FpIn);

  return STATUS_SUCCESS;
}

UINT8 *
FindNextFvHeader (
  IN UINT8 *FileBuffer,
  IN UINTN  FileLength
  )
/*++

  Routine Description:
    Find next FvHeader in the FileBuffer

  Parameters:
    FileBuffer        -   The start FileBuffer which needs to be searched
    FileLength        -   The whole File Length.
  Return:
    FvHeader          -   The FvHeader is found successfully.
    NULL              -   The FvHeader is not found.

--*/
{
  UINT8                       *FileHeader;
  EFI_FIRMWARE_VOLUME_HEADER  *FvHeader;
  UINT16                      FileChecksum;

  FileHeader = FileBuffer;
  for (; (UINTN)FileBuffer < (UINTN)FileHeader + FileLength; FileBuffer += 8) {
    FvHeader = (EFI_FIRMWARE_VOLUME_HEADER *)FileBuffer;
    if (FvHeader->Signature == EFI_FVH_SIGNATURE) {
      //
      // potential candidate
      //

      //
      // Check checksum
      //
      if (FvHeader->FvLength > FileLength) {
        continue;
      }
      if (FvHeader->HeaderLength >= FileLength) {
        continue;
      }
      FileChecksum = CalculateChecksum16 ((UINT16 *)FileBuffer, FvHeader->HeaderLength / sizeof (UINT16));
      if (FileChecksum != 0) {
        continue;
      }

      //
      // Check revision and reserved field
      //
#if (PI_SPECIFICATION_VERSION < 0x00010000)
      if ((FvHeader->Revision == EFI_FVH_REVISION) &&
          (FvHeader->Reserved[0] == 0) &&
          (FvHeader->Reserved[1] == 0) &&
          (FvHeader->Reserved[2] == 0) ){
        return FileBuffer;
      }
#else
      if ((FvHeader->Revision == EFI_FVH_PI_REVISION) &&
          (FvHeader->Reserved[0] == 0) ){
        return FileBuffer;
      }
#endif
    }
  }

  return NULL;
}

UINT8  *
FindFileFromFvByGuid (
  IN UINT8     *FvBuffer,
  IN UINT32    FvSize,
  IN EFI_GUID  *Guid,
  OUT UINT32   *FileSize
  )
/*++

Routine Description:

  Find File with GUID in an FV

Arguments:

  FvBuffer       - FV binary buffer
  FvSize         - FV size
  Guid           - File GUID value to be searched
  FileSize       - Guid File size

Returns:

  FileLocation   - Guid File location.
  NULL           - Guid File is not found.

--*/
{
  EFI_FIRMWARE_VOLUME_HEADER  *FvHeader;
  EFI_FFS_FILE_HEADER         *FileHeader;
  UINT64                      FvLength;
  EFI_GUID                    *TempGuid;
  UINT8                       *FixPoint;
  UINT32                      Offset;
  UINT32                      FileLength;
  UINT32                      FileOccupiedSize;

  //
  // Find the FFS file
  //
  FvHeader = (EFI_FIRMWARE_VOLUME_HEADER *)FindNextFvHeader (FvBuffer, FvSize);
  if (NULL == FvHeader) {
    return NULL;
  }
  while (TRUE) {
    FvLength         = FvHeader->FvLength;

    //
    // Prepare to walk the FV image
    //
    InitializeFvLib (FvHeader, (UINT32)FvLength);

    FileHeader       = (EFI_FFS_FILE_HEADER *)((UINTN)FvHeader + FvHeader->HeaderLength);
    Offset           = (UINT32) (UINTN) FileHeader - (UINT32) (UINTN) FvHeader;

    while (Offset < FvLength) {
      TempGuid = (EFI_GUID *)&(FileHeader->Name);
      FileLength = (*(UINT32 *)(FileHeader->Size)) & 0x00FFFFFF;
      FileOccupiedSize = GETOCCUPIEDSIZE(FileLength, 8);
      if ((CompareGuid (TempGuid, Guid)) == 0) {
        //
        // Good! Find it.
        //
        FixPoint = ((UINT8 *)FileHeader + sizeof(EFI_FFS_FILE_HEADER));
        //
        // Find the position of file module, the offset
        // between the position and the end of FvRecovery.fv file
        // should not exceed 128kB to prevent reset vector from
        // outside legacy E and F segment
        //
        if ((UINTN)FvHeader + FvLength - (UINTN)FixPoint > 0x20000) {
  //        printf ("WARNING: The position of file module is not in E and F segment!\n");
  //        return NULL;
        }
        *FileSize = FileLength - sizeof(EFI_FFS_FILE_HEADER);
  #if (PI_SPECIFICATION_VERSION < 0x00010000)
        if (FileHeader->Attributes & FFS_ATTRIB_TAIL_PRESENT) {
          *FileSize -= sizeof(EFI_FFS_FILE_TAIL);
        }
  #endif
        return FixPoint;
      }
      FileHeader = (EFI_FFS_FILE_HEADER *)((UINTN)FileHeader + FileOccupiedSize);
      Offset = (UINT32) (UINTN) FileHeader - (UINT32) (UINTN) FvHeader;
    }

    //
    // Not found, check next FV
    //
    if ((UINTN)FvBuffer + FvSize > (UINTN)FvHeader + FvLength) {
      FvHeader = (EFI_FIRMWARE_VOLUME_HEADER *)FindNextFvHeader ((UINT8 *)FvHeader + (UINTN)FvLength, (UINTN)FvBuffer + FvSize - ((UINTN)FvHeader + (UINTN)FvLength));
      if (FvHeader == NULL) {
        break;
      }
    } else {
      break;
    }
  }

  //
  // Not found
  //
  return NULL;
}

BOOLEAN
IsGuidData (
  IN CHAR8     *StringData,
  OUT EFI_GUID *Guid
  )
/*++

Routine Description:

  Check whether a string is a GUID

Arguments:

  StringData  - the String
  Guid        - Guid to hold the value

Returns:

  TRUE  - StringData is a GUID, and Guid field is filled.
  FALSE - StringData is not a GUID

--*/
{
  if (strlen (StringData) != strlen ("00000000-0000-0000-0000-000000000000")) {
    return FALSE;
  }
  if ((StringData[8] != '-') ||
      (StringData[13] != '-') ||
      (StringData[18] != '-') ||
      (StringData[23] != '-') ) {
    return FALSE;
  }

  StringToGuid (StringData, Guid);

  return TRUE;
}

VOID
CheckOverlap (
  IN UINT32 Address,
  IN UINT32 Size
  )
{
  INTN  Index;

  for (Index = 0; Index < (INTN)gFitTableContext.BiosModuleNumber; Index ++) {
    if ((gFitTableContext.BiosModule[Index].Address <= Address) &&
        ((gFitTableContext.BiosModule[Index].Size - Size) >= (Address - gFitTableContext.BiosModule[Index].Address))) {
      UINT32  TempSize;
      INT32   SubIndex;

      //
      // Found overlap, split BiosModuleEntry
      // Currently only support StartupAcm in 1 BiosModule. It does not support StartupAcm across 2 BiosModule or more.
      //
      if (gFitTableContext.BiosModuleNumber >= MAX_BIOS_MODULE_ENTRY) {
        Error (NULL, 0, 0, "Too many Bios Module!", NULL);
        return ;
      }

      if (Address != gFitTableContext.BiosModule[Index].Address) {
        //
        // Skip the entry whose start address is same as StartupAcm
        //
        gFitTableContext.BiosModule[gFitTableContext.BiosModuleNumber].Type    = FIT_TABLE_TYPE_BIOS_MODULE;
        gFitTableContext.BiosModule[gFitTableContext.BiosModuleNumber].Address = gFitTableContext.BiosModule[Index].Address;
        gFitTableContext.BiosModule[gFitTableContext.BiosModuleNumber].Size    = Address - gFitTableContext.BiosModule[Index].Address;
        gFitTableContext.BiosModuleNumber ++;
        gFitTableContext.FitEntryNumber++;
      }

      TempSize = gFitTableContext.BiosModule[Index].Address + gFitTableContext.BiosModule[Index].Size;
      gFitTableContext.BiosModule[Index].Address = Address + Size;
      gFitTableContext.BiosModule[Index].Size    = TempSize - gFitTableContext.BiosModule[Index].Address;

      if (gFitTableContext.BiosModule[Index].Size == 0) {
        //
        // remove the entry if size is 0
        //
        for (SubIndex = Index; SubIndex < (INTN)gFitTableContext.BiosModuleNumber - 1; SubIndex ++) {
          gFitTableContext.BiosModule[SubIndex].Address = gFitTableContext.BiosModule[SubIndex + 1].Address;
          gFitTableContext.BiosModule[SubIndex].Size    = gFitTableContext.BiosModule[SubIndex + 1].Size;
        }
        gFitTableContext.BiosModuleNumber --;
        gFitTableContext.FitEntryNumber--;
      }
      break;
    }
  }
}

UINT32
GetFitEntryNumber (
  IN INTN   argc,
  IN CHAR8  **argv,
  IN UINT8  *FdBuffer,
  IN UINT32 FdSize
  )
/*++

Routine Description:

  Get FIT entry number and fill global FIT table context, from argument

Arguments:

  argc           - Number of command line parameters.
  argv           - Array of pointers to parameter strings.
  FdBuffer       - FD binary buffer
  FdSize         - FD size

Returns:

  FitEntryNumber - The FIT entry number
  0              - Argument parse fail

*/
{
  EFI_GUID  Guid;
  INTN      Index;
  UINT8     *FileBuffer;
  UINT32    FileSize;
  UINT32    Type;
  UINT8     *MicrocodeFileBuffer;
  UINT8     *MicrocodeFileBufferRaw;
  UINT32    MicrocodeFileSize;
  UINT32    MicrocodeBase;
  UINT32    MicrocodeSize;
  UINT8     *MicrocodeBuffer;
  UINT32    MicrocodeRegionOffset;
  UINT32    MicrocodeRegionSize;
  STATUS    Status;
  EFI_FIRMWARE_VOLUME_HEADER  *FvHeader;
  UINTN                       FitEntryNumber;
  BOOLEAN                     BiosInfoExist;
  BIOS_INFO_HEADER            *BiosInfo;
  BIOS_INFO_STRUCT            *BiosInfoStruct;
  UINTN                       BiosInfoIndex;

  //
  // Init index
  //
  Index = 3;
  if (((strcmp (argv[1], "-D") == 0) ||
       (strcmp (argv[1], "-d") == 0)) ) {
    Index ++;
  }

  //
  // Fill Global Version
  //
  if ((Index + 1 >= argc) ||
      ((strcmp (argv[Index], "-V") != 0) &&
       (strcmp (argv[Index], "-v") != 0)) ) {
    gFitTableContext.GlobalVersion = DEFAULT_FIT_ENTRY_VERSION;
  } else {
    gFitTableContext.GlobalVersion = xtoi (argv[Index + 1]);
    Index += 2;
  }

  //
  // 0. FIT Header
  //
  gFitTableContext.FitEntryNumber = 1;
  if ((Index + 1 >= argc) ||
      ((strcmp (argv[Index], "-F") != 0) &&
       (strcmp (argv[Index], "-f") != 0)) ) {
    //
    // Use default address
    //
    gFitTableContext.FitTablePointerOffset = DEFAULT_FIT_TABLE_POINTER_OFFSET;
  } else {
    //
    // Get offset from parameter
    //
    gFitTableContext.FitTablePointerOffset = xtoi (argv[Index + 1]);
    Index += 2;
  }

  //
  // 0.1 FIT Header 2
  //
  if ((Index + 1 >= argc) ||
      ((strcmp (argv[Index], "-F") != 0) &&
       (strcmp (argv[Index], "-f") != 0)) ) {
    //
    // Bypass
    //
    gFitTableContext.FitTablePointerOffset2 = 0;
  } else {
    //
    // Get offset from parameter
    //
    gFitTableContext.FitTablePointerOffset2 = xtoi (argv[Index + 1]);
    Index += 2;
  }

  //
  // 0.2 FIT Header version
  //
  if ((Index + 1 >= argc) ||
      ((strcmp (argv[Index], "-V") != 0) &&
       (strcmp (argv[Index], "-v") != 0)) ) {
    //
    // Bypass
    //
    gFitTableContext.FitHeaderVersion = gFitTableContext.GlobalVersion;
  } else {
    //
    // Get offset from parameter
    //
    gFitTableContext.FitHeaderVersion = xtoi (argv[Index + 1]);
    Index += 2;
  }

  //
  // 0.3 Microcode alignment
  //
  if ((Index >= argc) ||
      ((strcmp (argv[Index], "-NA") != 0) &&
       (strcmp (argv[Index], "-na") != 0)) ) {
    //
    // by pass
    //
    gFitTableContext.MicrocodeAlignment = TRUE;
  } else {
    //
    // no alignment
    //
    gFitTableContext.MicrocodeAlignment = FALSE;
    Index += 1;
  }

  //
  // 0.4 Clear FIT table related memory
  //
  if ((Index >= argc) ||
      ((strcmp (argv[Index], "-CLEAR") != 0) &&
       (strcmp (argv[Index], "-clear") != 0)) ) {
    //
    // by pass
    //
    gFitTableContext.Clear = FALSE;
  } else {
    //
    // Clear FIT table
    //
    gFitTableContext.Clear = TRUE;
    //
    // Do not parse any more
    //
    return 0;
  }

  //
  // 0.5 BiosInfo
  //
  if ((Index + 1 >= argc) ||
      ((strcmp (argv[Index], "-I") != 0) &&
       (strcmp (argv[Index], "-i") != 0)) ) {
    //
    // Bypass
    //
    BiosInfoExist = FALSE;
  } else {
    //
    // Get offset from parameter
    //
    BiosInfoExist = TRUE;
    if (IsGuidData (argv[Index + 1], &Guid)) {
      FileBuffer = FindFileFromFvByGuid (FdBuffer, FdSize, &Guid, &FileSize);
      if (FileBuffer == NULL) {
        Error (NULL, 0, 0, "-I Parameter incorrect, GUID not found!", "%s", argv[Index + 1]);
        // not found
        return 0;
      }
      BiosInfo = (BIOS_INFO_HEADER *)FileBuffer;
      for (BiosInfoIndex = 0; BiosInfoIndex < FileSize; BiosInfoIndex++) {
        if (((BIOS_INFO_HEADER *)(FileBuffer + BiosInfoIndex))->Signature == BIOS_INFO_SIGNATURE) {
          BiosInfo = (BIOS_INFO_HEADER *)(FileBuffer + BiosInfoIndex);
        }
      }
      if (BiosInfo->Signature != BIOS_INFO_SIGNATURE) {
        Error (NULL, 0, 0, "-I Parameter incorrect, Signature Error!", NULL);
        // not found
        return 0;
      }
      BiosInfoStruct = (BIOS_INFO_STRUCT *)(BiosInfo + 1);

      for (BiosInfoIndex = 0; BiosInfoIndex < BiosInfo->EntryCount; BiosInfoIndex++) {
        if ((BiosInfoStruct[BiosInfoIndex].Attributes & BIOS_INFO_STRUCT_ATTRIBUTE_GENERAL_EXCLUDE_FROM_FIT) != 0) {
          continue;
        }
        switch (BiosInfoStruct[BiosInfoIndex].Type) {
        case FIT_TABLE_TYPE_HEADER:
          Error (NULL, 0, 0, "-I Parameter incorrect, Header Type unsupported!", NULL);
          return 0;
        case FIT_TABLE_TYPE_STARTUP_ACM:
          if (gFitTableContext.StartupAcm.Type != 0) {
            Error (NULL, 0, 0, "-I Parameter incorrect, Duplicated StartupAcm!", NULL);
            return 0;
          }
          gFitTableContext.StartupAcm.Type    = FIT_TABLE_TYPE_STARTUP_ACM;
          gFitTableContext.StartupAcm.Address = (UINT32)BiosInfoStruct[BiosInfoIndex].Address;
          gFitTableContext.StartupAcm.Size    = (UINT32)BiosInfoStruct[BiosInfoIndex].Size;
          gFitTableContext.StartupAcmVersion  = BiosInfoStruct[BiosInfoIndex].Version;
          gFitTableContext.FitEntryNumber ++;
          break;
        case FIT_TABLE_TYPE_BIOS_MODULE:
          if ((BiosInfoStruct[BiosInfoIndex].Attributes & BIOS_INFO_STRUCT_ATTRIBUTE_BIOS_POST_IBB) != 0) {
            continue;
          }
          if (gFitTableContext.BiosModuleNumber >= MAX_BIOS_MODULE_ENTRY) {
            Error (NULL, 0, 0, "-I Parameter incorrect, Too many Bios Module!", NULL);
            return 0;
          }
          gFitTableContext.BiosModule[gFitTableContext.BiosModuleNumber].Type    = FIT_TABLE_TYPE_BIOS_MODULE;
          gFitTableContext.BiosModule[gFitTableContext.BiosModuleNumber].Address = (UINT32)BiosInfoStruct[BiosInfoIndex].Address;
          gFitTableContext.BiosModule[gFitTableContext.BiosModuleNumber].Size    = (UINT32)BiosInfoStruct[BiosInfoIndex].Size;
          gFitTableContext.BiosModuleVersion = BiosInfoStruct[BiosInfoIndex].Version;
          gFitTableContext.BiosModuleNumber ++;
          gFitTableContext.FitEntryNumber ++;
          break;
        case FIT_TABLE_TYPE_MICROCODE:
          if ((BiosInfoStruct[BiosInfoIndex].Attributes & BIOS_INFO_STRUCT_ATTRIBUTE_MICROCODE_WHOLE_REGION) == 0) {
            if (gFitTableContext.MicrocodeNumber >= MAX_MICROCODE_ENTRY) {
              Error (NULL, 0, 0, "-I Parameter incorrect, Too many Microcode!", NULL);
              return 0;
            }
            gFitTableContext.Microcode[gFitTableContext.MicrocodeNumber].Type    = FIT_TABLE_TYPE_MICROCODE;
            gFitTableContext.Microcode[gFitTableContext.MicrocodeNumber].Address = (UINT32)BiosInfoStruct[BiosInfoIndex].Address;
            gFitTableContext.Microcode[gFitTableContext.MicrocodeNumber].Size    = (UINT32)BiosInfoStruct[BiosInfoIndex].Size;
            gFitTableContext.MicrocodeVersion = BiosInfoStruct[BiosInfoIndex].Version;
            gFitTableContext.MicrocodeNumber++;
            gFitTableContext.FitEntryNumber++;
          } else {
            MicrocodeRegionOffset = (UINT32)BiosInfoStruct[BiosInfoIndex].Address;
            MicrocodeRegionSize   = (UINT32)BiosInfoStruct[BiosInfoIndex].Size;
            if (MicrocodeRegionOffset == 0) {
              Error (NULL, 0, 0, "-I Parameter incorrect, MicrocodeRegionOffset is 0", NULL);
              return 0;
            }
            if (MicrocodeRegionSize == 0) {
              Error (NULL, 0, 0, "-I Parameter incorrect, MicrocodeRegionSize is 0", NULL);
              return 0;
            }
            if (MicrocodeRegionSize > FdSize) {
              Error (NULL, 0, 0, "-I Parameter incorrect, MicrocodeRegionSize too large", NULL);
              return 0;
            }

            MicrocodeFileBuffer = FLASH_TO_MEMORY (MicrocodeRegionOffset, FdBuffer, FdSize);
            MicrocodeFileSize = MicrocodeRegionSize;
            MicrocodeBase = MicrocodeRegionOffset;

            FvHeader = (EFI_FIRMWARE_VOLUME_HEADER *)MicrocodeFileBuffer;
            if (FvHeader->Signature == EFI_FVH_SIGNATURE) {
              // Skip FV header + FFS header
              MicrocodeBuffer = MicrocodeFileBuffer + sizeof(EFI_FIRMWARE_VOLUME_HEADER) + sizeof(EFI_FV_BLOCK_MAP_ENTRY) + sizeof(EFI_FFS_FILE_HEADER);
            } else {
              MicrocodeBuffer = MicrocodeFileBuffer;
            }
            while ((UINT32)(MicrocodeBuffer - MicrocodeFileBuffer) < MicrocodeFileSize) {
              if (*(UINT32 *)(MicrocodeBuffer) != 0x1) { // HeaderVersion
                break;
              }
              if (*(UINT32 *)(MicrocodeBuffer + 20) != 0x1) { // LoaderVersion
                break;
              }
              if (*(UINT32 *)(MicrocodeBuffer + 28) == 0) { // DataSize
                MicrocodeSize = 2048;
              } else {
                //
                // MCU might be put at 2KB alignment, if so, we need to adjust the size as 2KB alignment.
                //
                if (gFitTableContext.MicrocodeAlignment) {
                  MicrocodeSize = (*(UINT32 *)(MicrocodeBuffer + 32) + MICROCODE_ALIGNMENT) & ~MICROCODE_ALIGNMENT;
                } else {
                  MicrocodeSize = (*(UINT32 *)(MicrocodeBuffer + 32));
                }
              }

              //
              // Add Microcode
              //
              if (gFitTableContext.MicrocodeNumber >= MAX_MICROCODE_ENTRY) {
                printf ("-I Parameter incorrect, Too many Microcode!\n");
                return 0;
              }
              gFitTableContext.Microcode[gFitTableContext.MicrocodeNumber].Type = FIT_TABLE_TYPE_MICROCODE;
              gFitTableContext.Microcode[gFitTableContext.MicrocodeNumber].Address = MicrocodeBase + ((UINT32) (UINTN) MicrocodeBuffer - (UINT32) (UINTN) MicrocodeFileBuffer);
              gFitTableContext.Microcode[gFitTableContext.MicrocodeNumber].Size = MicrocodeSize;
              gFitTableContext.MicrocodeNumber++;
              gFitTableContext.FitEntryNumber++;

              MicrocodeBuffer += MicrocodeSize;
            }
          }
          break;
        case FIT_TABLE_TYPE_TPM_POLICY:
        case FIT_TABLE_TYPE_BIOS_POLICY:
        case FIT_TABLE_TYPE_TXT_POLICY:
        case FIT_TABLE_TYPE_KEY_MANIFEST:
        case FIT_TABLE_TYPE_BOOT_POLICY_MANIFEST:
        case FIT_TABLE_TYPE_BIOS_DATA_AREA:
        case FIT_TABLE_TYPE_CSE_SECURE_BOOT:
        default :
          if (BiosInfoStruct[BiosInfoIndex].Version != 0) {
            if (gFitTableContext.OptionalModuleNumber >= MAX_OPTIONAL_ENTRY) {
              Error (NULL, 0, 0, "-I Parameter incorrect, Too many Optional Module!", NULL);
              return 0;
            }
            gFitTableContext.OptionalModule[gFitTableContext.OptionalModuleNumber].Type    = BiosInfoStruct[BiosInfoIndex].Type;
            gFitTableContext.OptionalModule[gFitTableContext.OptionalModuleNumber].Address = (UINT32)BiosInfoStruct[BiosInfoIndex].Address;
            gFitTableContext.OptionalModule[gFitTableContext.OptionalModuleNumber].Size    = (UINT32)BiosInfoStruct[BiosInfoIndex].Size;
            gFitTableContext.OptionalModule[gFitTableContext.OptionalModuleNumber].Version = BiosInfoStruct[BiosInfoIndex].Version;
            gFitTableContext.OptionalModuleNumber++;
            gFitTableContext.FitEntryNumber++;
          } else {
            if (gFitTableContext.PortModuleNumber >= MAX_PORT_ENTRY) {
              Error (NULL, 0, 0, "-I Parameter incorrect, Too many Port Module!", NULL);
              return 0;
            }
            gFitTableContext.PortModule[gFitTableContext.PortModuleNumber].Type    = BiosInfoStruct[BiosInfoIndex].Type;
            gFitTableContext.PortModule[gFitTableContext.PortModuleNumber].Address = (UINT32)BiosInfoStruct[BiosInfoIndex].Address;
            gFitTableContext.PortModule[gFitTableContext.PortModuleNumber].Size    = (UINT32)(BiosInfoStruct[BiosInfoIndex].Address >> 32);
            gFitTableContext.PortModule[gFitTableContext.PortModuleNumber].Version = BiosInfoStruct[BiosInfoIndex].Version;
            gFitTableContext.PortModuleNumber++;
            gFitTableContext.FitEntryNumber++;
          }
          break;
        }
      }

    } else {
      Error (NULL, 0, 0, "-I Parameter incorrect, expect GUID!", NULL);
      return 0;
    }
    Index += 2;
  }

  //
  // 1. StartupAcm
  //
  do {
    if ((Index + 1 >= argc) ||
        ((strcmp (argv[Index], "-S") != 0) &&
         (strcmp (argv[Index], "-s") != 0)) ) {
      if (BiosInfoExist && (gFitTableContext.StartupAcm.Type == FIT_TABLE_TYPE_STARTUP_ACM)) {
        break;
      }
//      Error (NULL, 0, 0, "-S Parameter incorrect, expect -S!", NULL);
//      return 0;
      printf ("-S not found. WARNING!\n");
      break;
    }
    if (IsGuidData (argv[Index + 1], &Guid)) {
      FileBuffer = FindFileFromFvByGuid (FdBuffer, FdSize, &Guid, &FileSize);
      if (FileBuffer == NULL) {
        Error (NULL, 0, 0, "-S Parameter incorrect, GUID not found!", "%s", argv[Index + 1]);
        // not found
        return 0;
      }
      FileBuffer = (UINT8 *)MEMORY_TO_FLASH (FileBuffer, FdBuffer, FdSize);
      Index += 2;
    } else {
      if (Index + 2 >= argc) {
        Error (NULL, 0, 0, "-S Parameter incorrect, expect Address Size!", NULL);
        return 0;
      }
      FileBuffer = (UINT8 *) (UINTN) xtoi (argv[Index + 1]);
      FileSize = xtoi (argv[Index + 2]);
      Index += 3;
    }
    if (gFitTableContext.StartupAcm.Type != 0) {
      Error (NULL, 0, 0, "-S Parameter incorrect, Duplicated StartupAcm!", NULL);
      return 0;
    }
    gFitTableContext.StartupAcm.Type = FIT_TABLE_TYPE_STARTUP_ACM;
    gFitTableContext.StartupAcm.Address = (UINT32) (UINTN) FileBuffer;
    gFitTableContext.StartupAcm.Size = FileSize;
    gFitTableContext.FitEntryNumber ++;

    //
    // 1.1 StartupAcm version
    //
    if ((Index + 1 >= argc) ||
        ((strcmp (argv[Index], "-V") != 0) &&
         (strcmp (argv[Index], "-v") != 0)) ) {
      //
      // Bypass
      //
      gFitTableContext.StartupAcmVersion = gFitTableContext.GlobalVersion;
    } else {
      //
      // Get offset from parameter
      //
      gFitTableContext.StartupAcmVersion = xtoi (argv[Index + 1]);
      Index += 2;
    }
  } while (FALSE);

  //
  // 2. BiosModule
  //
  do {
    if ((Index + 2 >= argc) ||
        ((strcmp (argv[Index], "-B") != 0) &&
         (strcmp (argv[Index], "-b") != 0)) ) {
      if (BiosInfoExist && (gFitTableContext.BiosModuleNumber != 0)) {
        break;
      }
//      Error (NULL, 0, 0, "-B Parameter incorrect, expect -B!", NULL);
//      return 0;
      printf ("-B not found. WARNING!\n");
      break;
    }

    FileBuffer = (UINT8 *) (UINTN) xtoi (argv[Index + 1]);
    FileSize = xtoi (argv[Index + 2]);
    gFitTableContext.BiosModule[gFitTableContext.BiosModuleNumber].Type = FIT_TABLE_TYPE_BIOS_MODULE;
    gFitTableContext.BiosModule[gFitTableContext.BiosModuleNumber].Address = (UINT32) (UINTN) FileBuffer;
    gFitTableContext.BiosModule[gFitTableContext.BiosModuleNumber].Size = FileSize;
    gFitTableContext.BiosModuleNumber ++;
    gFitTableContext.FitEntryNumber ++;

    while (TRUE) {
      Index += 3;
      if (Index + 2 >= argc) {
        break;
      }
      if ((strcmp (argv[Index], "-B") != 0) &&
          (strcmp (argv[Index], "-b") != 0) ) {
        break;
      }
      if (gFitTableContext.BiosModuleNumber >= MAX_BIOS_MODULE_ENTRY) {
        Error (NULL, 0, 0, "-B Parameter incorrect, Too many Bios Module!", NULL);
        return 0;
      }
      FileBuffer = (UINT8 *) (UINTN) xtoi (argv[Index + 1]);
      FileSize = xtoi (argv[Index + 2]);
      gFitTableContext.BiosModule[gFitTableContext.BiosModuleNumber].Type = FIT_TABLE_TYPE_BIOS_MODULE;
      gFitTableContext.BiosModule[gFitTableContext.BiosModuleNumber].Address = (UINT32) (UINTN) FileBuffer;
      gFitTableContext.BiosModule[gFitTableContext.BiosModuleNumber].Size = FileSize;
      gFitTableContext.BiosModuleNumber ++;
      gFitTableContext.FitEntryNumber++;
    }

    //
    // 2.1 BiosModule version
    //
    if ((Index + 1 >= argc) ||
        ((strcmp (argv[Index], "-V") != 0) &&
         (strcmp (argv[Index], "-v") != 0)) ) {
      //
      // Bypass
      //
      gFitTableContext.BiosModuleVersion = gFitTableContext.GlobalVersion;
    } else {
      //
      // Get offset from parameter
      //
      gFitTableContext.BiosModuleVersion = xtoi (argv[Index + 1]);
      Index += 2;
    }
  } while (FALSE);

  //
  // 3. Microcode
  //
  while (TRUE) {
    if (Index + 1 >= argc) {
      break;
    }
    if ((strcmp (argv[Index], "-M") != 0) &&
        (strcmp (argv[Index], "-m") != 0) ) {
      break;
    }
    if (IsGuidData (argv[Index + 2], &Guid)) {
      Error (NULL, 0, 0, "-M Parameter incorrect, GUID unsupported!", NULL);
      return 0;
    } else {
      if (Index + 2 >= argc) {
        break;
      }
      FileBuffer = (UINT8 *) (UINTN) xtoi (argv[Index + 1]);
      FileSize = xtoi (argv[Index + 2]);
      Index += 3;
    }
    if (gFitTableContext.MicrocodeNumber >= MAX_MICROCODE_ENTRY) {
      Error (NULL, 0, 0, "-M Parameter incorrect, Too many Microcode!", NULL);
      return 0;
    }
    gFitTableContext.Microcode[gFitTableContext.MicrocodeNumber].Type = FIT_TABLE_TYPE_MICROCODE;
    gFitTableContext.Microcode[gFitTableContext.MicrocodeNumber].Address = (UINT32) (UINTN) FileBuffer;
    gFitTableContext.Microcode[gFitTableContext.MicrocodeNumber].Size = FileSize;
    gFitTableContext.MicrocodeNumber++;
    gFitTableContext.FitEntryNumber++;
  }

  //
  // 3.1 MicrocodeFv
  //
  while (TRUE) {
    if (Index + 1 >= argc) {
      break;
    }
    if ((strcmp (argv[Index], "-U") != 0) &&
        (strcmp (argv[Index], "-u") != 0) ) {
      break;
    }
    //
    // Get Fv
    //
    if (IsGuidData (argv[Index + 1], &Guid)) {
      MicrocodeFileBuffer = FindFileFromFvByGuid (FdBuffer, FdSize, &Guid, &MicrocodeFileSize);
      if (MicrocodeFileBuffer == NULL) {
        Error (NULL, 0, 0, "-U Parameter incorrect, GUID not found!", "%s", argv[Index + 1]);
        // not found
        return 0;
      }
      Index += 2;

      MicrocodeBuffer = MicrocodeFileBuffer;
      MicrocodeFileBufferRaw = NULL;
      MicrocodeRegionOffset = MEMORY_TO_FLASH (MicrocodeFileBuffer, FdBuffer, FdSize);
      MicrocodeRegionSize   = 0;
      MicrocodeBase = MicrocodeRegionOffset;

    } else {
      if (Index + 2 >= argc) {
        break;
      }
      Status = ReadInputFile (argv[Index + 1], &MicrocodeFileBuffer, &MicrocodeFileSize, &MicrocodeFileBufferRaw);
      if (Status != STATUS_SUCCESS) {
        MicrocodeRegionOffset = xtoi (argv[Index + 1]);
        MicrocodeRegionSize   = xtoi (argv[Index + 2]);

        if (MicrocodeRegionOffset == 0) {
          Error (NULL, 0, 0, "-U Parameter incorrect, MicrocodeRegionOffset is 0, or unable to open file", "%s", argv[Index + 1]);
          return 0;
        }
        if (MicrocodeRegionSize == 0) {
          Error (NULL, 0, 0, "-U Parameter incorrect, MicrocodeRegionSize is 0", NULL);
          return 0;
        }
        if (MicrocodeRegionSize > FdSize) {
          Error (NULL, 0, 0, "-U Parameter incorrect, MicrocodeRegionSize too large", NULL);
          return 0;
        }

        Index += 3;

        MicrocodeFileBufferRaw = NULL;
        MicrocodeFileBuffer = FLASH_TO_MEMORY (MicrocodeRegionOffset, FdBuffer, FdSize);
        MicrocodeFileSize = MicrocodeRegionSize;
        MicrocodeBase = MicrocodeRegionOffset;

        FvHeader = (EFI_FIRMWARE_VOLUME_HEADER *)MicrocodeFileBuffer;
        if (FvHeader->Signature == EFI_FVH_SIGNATURE) {
          // Skip FV header + FFS header
          MicrocodeBuffer = MicrocodeFileBuffer + sizeof(EFI_FIRMWARE_VOLUME_HEADER) + sizeof(EFI_FV_BLOCK_MAP_ENTRY) + sizeof(EFI_FFS_FILE_HEADER);
        } else {
          MicrocodeBuffer = MicrocodeFileBuffer;
        }
      } else {
        MicrocodeBase = xtoi (argv[Index + 2]);
        Index += 3;
        MicrocodeRegionOffset = 0;
        MicrocodeRegionSize   = 0;

        FvHeader = (EFI_FIRMWARE_VOLUME_HEADER *)MicrocodeFileBuffer;
        if (FvHeader->Signature == EFI_FVH_SIGNATURE) {
          // Skip FV header + FFS header
          MicrocodeBuffer = MicrocodeFileBuffer + sizeof(EFI_FIRMWARE_VOLUME_HEADER) + sizeof(EFI_FV_BLOCK_MAP_ENTRY) + sizeof(EFI_FFS_FILE_HEADER);
        } else {
          MicrocodeBuffer = MicrocodeFileBuffer;
        }
      }
    }
    while ((UINT32)(MicrocodeBuffer - MicrocodeFileBuffer) < MicrocodeFileSize) {
      if (*(UINT32 *)(MicrocodeBuffer) != 0x1) { // HeaderVersion
        break;
      }
      if (*(UINT32 *)(MicrocodeBuffer + 20) != 0x1) { // LoaderVersion
        break;
      }
      if (*(UINT32 *)(MicrocodeBuffer + 28) == 0) { // DataSize
        MicrocodeSize = 2048;
      } else {
        //
        // MCU might be put at 2KB alignment, if so, we need to adjust the size as 2KB alignment.
        //
        if (gFitTableContext.MicrocodeAlignment) {
          MicrocodeSize = (*(UINT32 *)(MicrocodeBuffer + 32) + MICROCODE_ALIGNMENT) & ~MICROCODE_ALIGNMENT;
        } else {
          MicrocodeSize = (*(UINT32 *)(MicrocodeBuffer + 32));
        }
      }

      //
      // Add Microcode
      //
      if (gFitTableContext.MicrocodeNumber >= MAX_MICROCODE_ENTRY) {
        printf ("-U Parameter incorrect, Too many Microcode!\n");
        return 0;
      }
      gFitTableContext.Microcode[gFitTableContext.MicrocodeNumber].Type = FIT_TABLE_TYPE_MICROCODE;
      gFitTableContext.Microcode[gFitTableContext.MicrocodeNumber].Address = MicrocodeBase + ((UINT32) (UINTN) MicrocodeBuffer - (UINT32) (UINTN) MicrocodeFileBuffer);
      gFitTableContext.Microcode[gFitTableContext.MicrocodeNumber].Size = MicrocodeSize;
      gFitTableContext.MicrocodeNumber++;
      gFitTableContext.FitEntryNumber++;

      MicrocodeBuffer += MicrocodeSize;
    }

    if (MicrocodeFileBufferRaw != NULL) {
      free ((VOID *)MicrocodeFileBufferRaw);
      MicrocodeFileBufferRaw = NULL;
    }
  }

  //
  // 3.3 Microcode version
  //
  if ((Index + 1 >= argc) ||
      ((strcmp (argv[Index], "-V") != 0) &&
       (strcmp (argv[Index], "-v") != 0)) ) {
    //
    // Bypass
    //
    gFitTableContext.MicrocodeVersion = gFitTableContext.GlobalVersion;
  } else {
    //
    // Get offset from parameter
    //
    gFitTableContext.MicrocodeVersion = xtoi (argv[Index + 1]);
    Index += 2;
  }

  //
  // 4. Optional type
  //
  while (TRUE) {
    if (Index + 2 >= argc) {
      break;
    }
    if ((strcmp (argv[Index], "-O") != 0) &&
        (strcmp (argv[Index], "-o") != 0) ) {
      break;
    }
    Type = xtoi (argv[Index + 1]);
    //
    // 1st, try GUID
    //
    if (IsGuidData (argv[Index + 2], &Guid)) {
      FileBuffer = FindFileFromFvByGuid (FdBuffer, FdSize, &Guid, &FileSize);
      if (FileBuffer == NULL) {
        Error (NULL, 0, 0, "-O Parameter incorrect, GUID not found!", "%s", argv[Index + 2]);
        // not found
        return 0;
      }
      if (FileSize >= 0x80000000) {
        Error (NULL, 0, 0, "-O Parameter incorrect, FileSize too large!", NULL);
        return 0;
      }
      FileBuffer = (UINT8 *)MEMORY_TO_FLASH (FileBuffer, FdBuffer, FdSize);
      Index += 3;
    } else {
      //
      // 2nd, try file
      //
      Status = ReadInputFile (argv[Index + 2], &FileBuffer, &FileSize, NULL);
      if (Status == STATUS_SUCCESS) {
        if (FileSize >= 0x80000000) {
          Error (NULL, 0, 0, "-O Parameter incorrect, FileSize too large!", NULL);
          free (FileBuffer);
          return 0;
        }
        //
        // Set the most significant bit
        // It means the data in memory, not in flash yet.
        // Assume the file size should < 2G.
        //
        FileSize |= 0x80000000;
        Index += 3;
      } else {
        //
        // 3rd, try <RESERVE, Length>
        //
        if (Index + 3 >= argc) {
          break;
        }
        if ((strcmp (argv[Index + 2], "RESERVE") == 0) ||
            (strcmp (argv[Index + 2], "reserve") == 0)) {
          FileSize = xtoi (argv[Index + 3]);
          if (FileSize >= 0x80000000) {
            Error (NULL, 0, 0, "-O Parameter incorrect, FileSize too large!", NULL);
            return 0;
          }
          FileBuffer = malloc (FileSize);
          if (FileBuffer == NULL) {
            Error (NULL, 0, 0, "No sufficient memory to allocate!", NULL);
            return 0;
          }
          SetMem (FileBuffer, FileSize, 0xFF);
          //
          // Set the most significant bit
          // It means the data in memory, not in flash yet.
          // Assume the file size should < 2G.
          //
          FileSize |= 0x80000000;
          Index += 4;
        } else {
          //
          // 4th, try <Address, Length>
          //
          if (Index + 3 >= argc) {
            break;
          }
          FileBuffer = (UINT8 *) (UINTN) xtoi (argv[Index + 2]);
          FileSize = xtoi (argv[Index + 3]);
          if (FileSize >= 0x80000000) {
            Error (NULL, 0, 0, "-O Parameter incorrect, FileSize too large!", NULL);
            return 0;
          }
          Index += 4;
        }
      }
    }
    if (gFitTableContext.OptionalModuleNumber >= MAX_OPTIONAL_ENTRY) {
      Error (NULL, 0, 0, "-O Parameter incorrect, Too many Optional Module!", NULL);
      free (FileBuffer);
      return 0;
    }
    gFitTableContext.OptionalModule[gFitTableContext.OptionalModuleNumber].Type = Type;
    gFitTableContext.OptionalModule[gFitTableContext.OptionalModuleNumber].Address = (UINT32) (UINTN) FileBuffer;
    gFitTableContext.OptionalModule[gFitTableContext.OptionalModuleNumber].Size = FileSize;

    //
    // 4.1 Optional Module version
    //
    if ((Index + 1 >= argc) ||
        ((strcmp (argv[Index], "-V") != 0) &&
         (strcmp (argv[Index], "-v") != 0)) ) {
      //
      // Bypass
      //
      gFitTableContext.OptionalModule[gFitTableContext.OptionalModuleNumber].Version = gFitTableContext.GlobalVersion;
    } else {
      //
      // Get offset from parameter
      //
      gFitTableContext.OptionalModule[gFitTableContext.OptionalModuleNumber].Version = xtoi (argv[Index + 1]);
      Index += 2;
    }

    gFitTableContext.OptionalModuleNumber ++;
    gFitTableContext.FitEntryNumber++;
  }

  //
  // 5. Port type
  //
  while (TRUE) {
    if (Index + 6 >= argc) {
      break;
    }
    if ((strcmp (argv[Index], "-P") != 0) &&
        (strcmp (argv[Index], "-p") != 0) ) {
      break;
    }

    Type = xtoi (argv[Index + 1]);
    if (gFitTableContext.PortModuleNumber >= MAX_PORT_ENTRY) {
      printf ("-P Parameter incorrect, Too many Port Module!\n");
      return 0;
    }

    gFitTableContext.PortModule[gFitTableContext.PortModuleNumber].Type = Type;
    gFitTableContext.PortModule[gFitTableContext.PortModuleNumber].Address = (UINT16)xtoi (argv[Index + 2]) + ((UINT16)xtoi (argv[Index + 3]) << 16);
    gFitTableContext.PortModule[gFitTableContext.PortModuleNumber].Size = (UINT8)xtoi (argv[Index + 4]) + ((UINT8)xtoi (argv[Index + 5]) << 8) + ((UINT16)xtoi (argv[Index + 6]) << 16);
    Index += 7;

    //
    // 5.1 Port Module version
    //
    if ((Index + 1 >= argc) ||
        ((strcmp (argv[Index], "-V") != 0) &&
         (strcmp (argv[Index], "-v") != 0)) ) {
      //
      // Bypass
      //
      gFitTableContext.PortModule[gFitTableContext.PortModuleNumber].Version = 0;
    } else {
      //
      // Get offset from parameter
      //
      gFitTableContext.PortModule[gFitTableContext.PortModuleNumber].Version = xtoi (argv[Index + 1]);
      Index += 2;
    }

    gFitTableContext.PortModuleNumber++;
    gFitTableContext.FitEntryNumber++;
  }

  //
  // Final: Check StartupAcm in BiosModule.
  //
  CheckOverlap (gFitTableContext.StartupAcm.Address, gFitTableContext.StartupAcm.Size);
  FitEntryNumber = gFitTableContext.FitEntryNumber;
  for (Index = 0; Index < (INTN)gFitTableContext.OptionalModuleNumber; Index++) {
    if ((gFitTableContext.OptionalModule[Index].Type == FIT_TABLE_TYPE_BIOS_POLICY) ||
        (gFitTableContext.OptionalModule[Index].Type == FIT_TABLE_TYPE_KEY_MANIFEST) ||
        (gFitTableContext.OptionalModule[Index].Type == FIT_TABLE_TYPE_BOOT_POLICY_MANIFEST) ||
        (gFitTableContext.OptionalModule[Index].Type == FIT_TABLE_TYPE_BIOS_DATA_AREA) ||
        (gFitTableContext.OptionalModule[Index].Type == FIT_TABLE_TYPE_CSE_SECURE_BOOT)) {
      // NOTE: It might be virtual address now. Just put a place holder.
      FitEntryNumber ++;
    }
  }

  return FitEntryNumber;
}

VOID *
FindSpaceSkipApVector (
  IN UINT8     *FvBuffer,
  IN UINT8     *Address,
  IN UINTN     Size
  )
/*++

Routine Description:

  No enough space - it might happen that it is occupied by AP wake vector.
  Last chance - skip this and search again.

Arguments:

  FvBuffer       - FvRecovery binary buffer
  Address        - Address to be searched from
  Size           - Size need to be filled

Returns:

  FitTableOffset - The FIT table offset
  NULL           - No enough space for FIT table

*/
{
  UINT8        *ApVector;
  UINT8        *NewAddress;
  UINTN        Index;

  ApVector = (UINT8 *)((UINTN)Address & ~0xFFF);
  if ((UINTN)ApVector <= (UINTN)FvBuffer) {
    return NULL;
  }

  NewAddress = (UINT8 *)(ApVector - Size);
  for (Index = 0; Index < Size; Index ++) {
    if (NewAddress[Index] != 0xFF) {
      return NULL;
    }
  }
  return NewAddress;
}

VOID *
GetFreeSpaceFromFv (
  IN UINT8     *FvBuffer,
  IN UINT32    FvSize,
  IN UINT32    FitEntryNumber
  )
/*++

Routine Description:

  Get free space for FIT table from FvRecovery

Arguments:

  FvBuffer       - FvRecovery binary buffer
  FvSize         - FvRecovery size
  FitEntryNumber - The FIT entry number

Returns:

  FitTableOffset - The offset of FIT table in FvRecovery file
  NULL           - Free space not found

--*/
{
  UINT8       *FitTableOffset;
  INTN        Index;
  INTN        SubIndex;
  UINT8       *OptionalModuleAddress;
  EFI_GUID    VTFGuid = EFI_FFS_VOLUME_TOP_FILE_GUID;
  UINT32      AlignedSize;
  UINT32      FitTableSize;

  EFI_FIRMWARE_VOLUME_HEADER  *FvHeader;
  EFI_FFS_FILE_HEADER         *FileHeader;
  UINT64                      FvLength;
  UINT32                      Offset;
  UINT32                      FileLength;
  UINT32                      FileOccupiedSize;

  //
  // Check 4G - FitTablePointerOffset
  //
  if ((*(UINT64 *)(FvBuffer + FvSize - gFitTableContext.FitTablePointerOffset) != 0xFFFFFFFFFFFFFFFFull) &&
      (*(UINT64 *)(FvBuffer + FvSize - gFitTableContext.FitTablePointerOffset) != 0) &&
      (*(UINT64 *)(FvBuffer + FvSize - gFitTableContext.FitTablePointerOffset) != 0xEEEEEEEEEEEEEEEEull)) {
    Error (NULL, 0, 0, "4G - FitTablePointerOffset is occupied!", NULL);
    return NULL;
  }
  if (gFitTableContext.FitTablePointerOffset2 != 0) {
    if ((*(UINT64 *)(FvBuffer + FvSize - gFitTableContext.FitTablePointerOffset2) != 0xFFFFFFFFFFFFFFFFull) &&
        (*(UINT64 *)(FvBuffer + FvSize - gFitTableContext.FitTablePointerOffset2) != 0) &&
        (*(UINT64 *)(FvBuffer + FvSize - gFitTableContext.FitTablePointerOffset2) != 0xEEEEEEEEEEEEEEEEull)) {
      Error (NULL, 0, 0, "4G - FitTablePointerOffset is occupied!", NULL);
      return NULL;
    }
  }

  //
  // Get EFI_FFS_VOLUME_TOP_FILE_GUID location
  //
  FitTableOffset = NULL;

  FvHeader         = (EFI_FIRMWARE_VOLUME_HEADER *)FvBuffer;
  FvLength         = FvHeader->FvLength;
  FileHeader       = (EFI_FFS_FILE_HEADER *)(FvBuffer + FvHeader->HeaderLength);
  Offset           = (UINTN)FileHeader - (UINTN)FvBuffer;

  while (Offset < FvLength) {
    FileLength = (*(UINT32 *)(FileHeader->Size)) & 0x00FFFFFF;
    FileOccupiedSize = GETOCCUPIEDSIZE(FileLength, 8);
    if ((CompareGuid (&(FileHeader->Name), &VTFGuid)) == 0) {
      // find it
      FitTableOffset = (UINT8 *)FileHeader;
      break;
    }
    FileHeader = (EFI_FFS_FILE_HEADER *)((UINTN)FileHeader + FileOccupiedSize);
    Offset = (UINTN)FileHeader - (UINTN)FvBuffer;
  }

  if (FitTableOffset == NULL) {
    Error (NULL, 0, 0, "EFI_FFS_VOLUME_TOP_FILE_GUID not found!", NULL);
    return NULL;
  }

  FitTableSize = FitEntryNumber * sizeof(FIRMWARE_INTERFACE_TABLE_ENTRY);
  FitTableSize += FIT_ALIGNMENT;
  FitTableSize &= ~FIT_ALIGNMENT;

  FitTableOffset = (UINT8 *)((UINTN)FitTableOffset & ~FIT_ALIGNMENT);
  FitTableOffset = (UINT8 *)(FitTableOffset - FitTableSize);

  //
  // Check it
  //
  for (Index = 0; Index < (INTN)(FitTableSize); Index ++) {
    if (FitTableOffset[Index] != 0xFF) {
      //
      // No enough space - it might happen that it is occupied by AP wake vector.
      // Last chance - skip this and search again.
      //
      FitTableOffset = FindSpaceSkipApVector (FvBuffer, &FitTableOffset[Index], FitTableSize);
      if (FitTableOffset == NULL) {
        Error (NULL, 0, 0, "No enough space for FIT table!", NULL);
        return NULL;
      }
    }
  }

  //
  // Check space for Optional module
  //
  OptionalModuleAddress = FitTableOffset;
  for (Index = 0; Index < (INTN)gFitTableContext.OptionalModuleNumber; Index++) {
    AlignedSize = gFitTableContext.OptionalModule[Index].Size;
    if ((gFitTableContext.OptionalModule[Index].Size & 0x80000000) != 0) {

      //
      // Need copy binary to file.
      //
      gFitTableContext.OptionalModule[Index].Size &= ~0x80000000;

      AlignedSize = gFitTableContext.OptionalModule[Index].Size;
      if ((gFitTableContext.OptionalModule[Index].Type == FIT_TABLE_TYPE_BIOS_POLICY) ||
          (gFitTableContext.OptionalModule[Index].Type == FIT_TABLE_TYPE_KEY_MANIFEST) ||
          (gFitTableContext.OptionalModule[Index].Type == FIT_TABLE_TYPE_BOOT_POLICY_MANIFEST) ||
          (gFitTableContext.OptionalModule[Index].Type == FIT_TABLE_TYPE_BIOS_DATA_AREA) ||
          (gFitTableContext.OptionalModule[Index].Type == FIT_TABLE_TYPE_CSE_SECURE_BOOT)) {
        // Let it 64 byte align
        AlignedSize += BIOS_MODULE_ALIGNMENT;
        AlignedSize &= ~BIOS_MODULE_ALIGNMENT;
      }

      OptionalModuleAddress -= AlignedSize;

      if ((gFitTableContext.OptionalModule[Index].Type == FIT_TABLE_TYPE_BIOS_POLICY) ||
          (gFitTableContext.OptionalModule[Index].Type == FIT_TABLE_TYPE_KEY_MANIFEST) ||
          (gFitTableContext.OptionalModule[Index].Type == FIT_TABLE_TYPE_BOOT_POLICY_MANIFEST) ||
          (gFitTableContext.OptionalModule[Index].Type == FIT_TABLE_TYPE_BIOS_DATA_AREA) ||
          (gFitTableContext.OptionalModule[Index].Type == FIT_TABLE_TYPE_CSE_SECURE_BOOT)) {
        // Let it 64 byte align
        OptionalModuleAddress = (UINT8 *)((UINTN)OptionalModuleAddress & ~BIOS_MODULE_ALIGNMENT);
      }

      for (SubIndex = 0; SubIndex < (INTN)(AlignedSize); SubIndex ++) {
        if (OptionalModuleAddress[SubIndex] != 0xFF) {
          //
          // No enough space - it might happen that it is occupied by AP wake vector.
          // Last chance - skip this and search again.
          //
          OptionalModuleAddress = FindSpaceSkipApVector (FvBuffer, &OptionalModuleAddress[SubIndex], AlignedSize);
          if (OptionalModuleAddress == NULL) {
            Error (NULL, 0, 0, "No enough space for OptionalModule!", NULL);
            return NULL;
          }
        }
      }
      memcpy (OptionalModuleAddress, (VOID *) (UINTN) gFitTableContext.OptionalModule[Index].Address, gFitTableContext.OptionalModule[Index].Size);
      free ((VOID *) (UINTN) gFitTableContext.OptionalModule[Index].Address);
      gFitTableContext.OptionalModule[Index].Address = MEMORY_TO_FLASH (OptionalModuleAddress, FvBuffer, FvSize);
    }
    //
    // Final: Check BiosPolicyData in BiosModule.
    //
    if ((gFitTableContext.OptionalModule[Index].Type == FIT_TABLE_TYPE_BIOS_POLICY) ||
        (gFitTableContext.OptionalModule[Index].Type == FIT_TABLE_TYPE_KEY_MANIFEST) ||
        (gFitTableContext.OptionalModule[Index].Type == FIT_TABLE_TYPE_BOOT_POLICY_MANIFEST) ||
        (gFitTableContext.OptionalModule[Index].Type == FIT_TABLE_TYPE_BIOS_DATA_AREA) ||
        (gFitTableContext.OptionalModule[Index].Type == FIT_TABLE_TYPE_CSE_SECURE_BOOT)) {
      CheckOverlap (gFitTableContext.OptionalModule[Index].Address, AlignedSize);
    }
  }

  return FitTableOffset;
}

VOID
PrintFitData (
  VOID
  )
/*++

Routine Description:

  Output FIT table information

Arguments:

  None

Returns:

  None

--*/
{
  UINT32                          Index;

  printf ("FIT Table Pointer Offset: 0x%x\n", gFitTableContext.FitTablePointerOffset);
  if (gFitTableContext.FitTablePointerOffset2 != 0) {
    printf ("FIT Table Pointer Offset: 0x%x\n", gFitTableContext.FitTablePointerOffset2);
  }
  printf ("Total FIT Entry number: 0x%x\n", gFitTableContext.FitEntryNumber);
  printf ("FitHeader version: 0x%04x\n", gFitTableContext.FitHeaderVersion);
  if (gFitTableContext.StartupAcm.Address != 0) {
    printf ("StartupAcm - (0x%08x, 0x%08x, 0x%04x)\n", gFitTableContext.StartupAcm.Address, gFitTableContext.StartupAcm.Size, gFitTableContext.StartupAcmVersion);
  }
  for (Index = 0; Index < gFitTableContext.BiosModuleNumber; Index++) {
    printf ("BiosModule[%d] - (0x%08x, 0x%08x, 0x%04x)\n", Index, gFitTableContext.BiosModule[Index].Address, gFitTableContext.BiosModule[Index].Size, gFitTableContext.BiosModuleVersion);
  }
  for (Index = 0; Index < gFitTableContext.MicrocodeNumber; Index++) {
    printf ("Microcode[%d] - (0x%08x, 0x%08x, 0x%04x)\n", Index, gFitTableContext.Microcode[Index].Address, gFitTableContext.Microcode[Index].Size, gFitTableContext.MicrocodeVersion);
  }
  for (Index = 0; Index < gFitTableContext.OptionalModuleNumber; Index++) {
    printf ("OptionalModule[%d] - (0x%08x, 0x%08x, 0x%02x, 0x%04x)\n", Index, gFitTableContext.OptionalModule[Index].Address, gFitTableContext.OptionalModule[Index].Size, gFitTableContext.OptionalModule[Index].Type, gFitTableContext.OptionalModule[Index].Version);
  }
  for (Index = 0; Index < gFitTableContext.PortModuleNumber; Index++) {
    printf ("PortModule[%d] - (0x%04x, 0x%04x, 0x%02x, 0x%02x, 0x%04x, 0x%02x, 0x%04x)\n", Index,
      (UINT16)gFitTableContext.PortModule[Index].Address, (UINT16)(gFitTableContext.PortModule[Index].Address >> 16),
      (UINT8)gFitTableContext.PortModule[Index].Size, (UINT8)(gFitTableContext.PortModule[Index].Size >> 8), (UINT16)(gFitTableContext.PortModule[Index].Size >> 16),
      gFitTableContext.PortModule[Index].Type, gFitTableContext.PortModule[Index].Version);
  }

  printf ("\n");
  return ;
}

CHAR8 *mFitTypeStr[] = {
  "           ",
  "MICROCODE  ",
  "STARTUP_ACM",
  "           ",
  "           ",
  "           ",
  "           ",
  "BIOS_MODULE",
  "TPM_POLICY ",
  "BIOS_POLICY",
  "TXT_POLICY ",
  "KEYMANIFEST",
  "BP_MANIFEST",
  "BIOS_DATA_A",
  " ",
  " ",
  "CSE_SECUREB"
};

CHAR8  mFitSignature[] = "'_FIT_   ' ";
CHAR8  mFitSignatureInHeader[] = "'        ' ";
CHAR8 *
FitTypeToStr (
  IN FIRMWARE_INTERFACE_TABLE_ENTRY  *FitEntry
  )
/*++

Routine Description:

  Convert FitEntry type to a string

Arguments:

  FitEntry - Fit entry

Returns:

  String

--*/
{
  if (FitEntry->Type == FIT_TABLE_TYPE_HEADER) {
    CopyMem (&mFitSignatureInHeader[1], &FitEntry->Address, sizeof(FitEntry->Address));
    return mFitSignatureInHeader;
  }
  if (FitEntry->Type < sizeof (mFitTypeStr)/sizeof(mFitTypeStr[0])) {
    return mFitTypeStr[FitEntry->Type];
  } else {
    return "           ";
  }
}

VOID
PrintFitTable (
  IN UINT8                       *FvBuffer,
  IN UINT32                      FvSize
  )
/*++

Routine Description:

  Print Fit table in flash image

Arguments:

  FvBuffer       - FvRecovery binary buffer
  FvSize         - FvRecovery size

Returns:

  None

--*/
{
  FIRMWARE_INTERFACE_TABLE_ENTRY  *FitEntry;
  UINT32                          EntryNum;
  UINT32                          Index;
  UINT32                          FitTableOffset;
  FIRMWARE_INTERFACE_TABLE_ENTRY_PORT   *FitEntryPort;

  printf ("##############\n");
  printf ("# FIT Table: #\n");
  printf ("##############\n");

  printf ("FIT Pointer Offset: 0x%x\n", gFitTableContext.FitTablePointerOffset);
  FitTableOffset = *(UINT32 *)(FvBuffer + FvSize - gFitTableContext.FitTablePointerOffset);
  printf ("FIT Table Address:  0x%x\n", FitTableOffset);
  FitEntry = (FIRMWARE_INTERFACE_TABLE_ENTRY *)FLASH_TO_MEMORY(FitTableOffset, FvBuffer, FvSize);

  //
  // Check FitEntry is 16 byte aligned
  //
  if (((UINTN)FitEntry & 0xF) != 0) {
    printf("ERROR: invalid FitEntry address 0x%X!\n", (UINT32) (UINTN) FitEntry);
    return;
  }

  EntryNum = *(UINT32 *)(&FitEntry[0].Size[0]) & 0xFFFFFF;
  printf ("====== ================ ====== ======== ============== ==== ======== (====== ==== ====== ==== ======)\n");
  printf ("Index:      Address      Size  Version       Type      C_V  Checksum (Index  Data Width  Bit  Offset)\n");
  printf ("====== ================ ====== ======== ============== ==== ======== (====== ==== ====== ==== ======)\n");
  for (Index = 0; Index < EntryNum; Index++) {
    printf (" %02d:   %016llx %06x   %04x   %02x-%s  %02x     %02x   ",
      Index,
      (unsigned long long) FitEntry[Index].Address,
      *(UINT32 *)(&FitEntry[Index].Size[0]) & 0xFFFFFF,
      FitEntry[Index].Version,
      FitEntry[Index].Type,
      FitTypeToStr(&FitEntry[Index]),
      FitEntry[Index].C_V,
      FitEntry[Index].Checksum
      );

    if (Index == 0) {
      if (FitEntry[Index].Type != FIT_TABLE_TYPE_HEADER) {
        printf("ERROR: FIT Entry 0 is not Header Type %d!\n", FIT_TABLE_TYPE_HEADER);
        return;
      }
      if (strcmp(mFitSignatureInHeader, mFitSignature) != 0) {
        printf("ERROR: FIT Entry 0 signature invalid (%s, expected %s)!\n", mFitSignatureInHeader, mFitSignature);
        return;
      }

    }

    switch (FitEntry[Index].Type) {
    case FIT_TABLE_TYPE_TPM_POLICY:
    case FIT_TABLE_TYPE_TXT_POLICY:
      if (FitEntry[Index].Version == 0) {
        FitEntryPort = (FIRMWARE_INTERFACE_TABLE_ENTRY_PORT *)&FitEntry[Index];
        printf (" ( %04x  %04x   %02x    %02x   %04x )\n",
          FitEntryPort->IndexPort,
          FitEntryPort->DataPort,
          FitEntryPort->Width,
          FitEntryPort->Bit,
          FitEntryPort->Index
          );
        break;
      }
      // Not Port Configure, pass through
    default:
      printf ("\n");
      break;
    }
  }
  printf ("====== ================ ====== ======== ============== ==== ======== (====== ==== ====== ==== ======)\n");
  printf ("Index:      Address      Size  Version       Type      C_V  Checksum (Index  Data Width  Bit  Offset)\n");
  printf ("====== ================ ====== ======== ============== ==== ======== (====== ==== ====== ==== ======)\n");
}

/**

  This function dump raw data.

  @param  Data  raw data
  @param  Size  raw data size

**/
VOID
DumpData (
  IN UINT8  *Data,
  IN UINT32 Size
  )
{
  UINT32 Index;
  for (Index = 0; Index < Size; Index++) {
    printf ("%02x", Data[Index]);
  }
}

/**

  This function dump raw data with colume format.

  @param  Data  raw data
  @param  Size  raw data size

**/
VOID
DumpHex (
  IN UINT8  *Data,
  IN UINT32 Size
  )
{
  UINT32  Index;
  UINT32  Count;
  UINT32  Left;

#define COLUME_SIZE  (16 * 2)

  Count = Size / COLUME_SIZE;
  Left  = Size % COLUME_SIZE;
  for (Index = 0; Index < Count; Index++) {
    printf ("%04x: ", Index * COLUME_SIZE);
    DumpData (Data + Index * COLUME_SIZE, COLUME_SIZE);
    printf ("\n");
  }

  if (Left != 0) {
    printf ("%04x: ", Index * COLUME_SIZE);
    DumpData (Data + Index * COLUME_SIZE, Left);
    printf ("\n");
  }
}

//
// This table defines the ACM type string
//
CHAR8 *mAcmTypeStr[] = {
  "BIOS ACM",
  "SINIT ACM",
};

//
// This table defines the ACM capability string
//
CHAR8 *mCapabilityStr[] = {
  "GETSEC[WAKEUP] for RLP   ",
  "MONITOR address for RLP  ",
  "ECX for MLE PageTable    ",
  "STM support              ",
};

VOID
DumpAcm (
  IN ACM_FORMAT                    *Acm
  )
/*++

Routine Description:

  DumpAcm information

Arguments:

  Acm       - ACM buffer

Returns:

  None

--*/
{
  CHIPSET_ACM_INFORMATION_TABLE *ChipsetAcmInformationTable;
  CHIPSET_ID_LIST               *ChipsetIdList;
  PROCESSOR_ID_LIST             *ProcessorIdList;
  UINT32                        Index;
  UINT8                         *Buffer;

  printf (
    "*****************************************************************************\n"
    "*         ACM                                                               *\n"
    "*****************************************************************************\n"
    );

  printf ("ACM: (%08x)\n", (UINT32) (UINTN) Acm);
  printf ("  ModuleType                 - %04x\n", Acm->ModuleType);
  if (Acm->ModuleType == ACM_MODULE_TYPE_CHIPSET_ACM) {
    printf ("    Chipset ACM\n");
  }
  printf ("  ModuleSubType              - %04x\n", Acm->ModuleSubType);
  if ((Acm->ModuleSubType & ACM_MODULE_SUBTYPE_CAPABLE_OF_EXECUTE_AT_RESET) != 0) {
    printf ("    Capable of be Executed at Reset\n");
  }
  if ((Acm->ModuleSubType & ACM_MODULE_SUBTYPE_ANC_MODULE) != 0) {
    printf ("    AnC Module\n");
  }
  printf ("  HeaderLen                  - %08x\n", Acm->HeaderLen);
  printf ("  HeaderVersion              - %08x\n", Acm->HeaderVersion);
  printf ("  ChipsetID                  - %04x\n", Acm->ChipsetID);
  printf ("  Flags                      - %04x\n", Acm->Flags);
  printf ("    PreProduction            - %04x\n", Acm->Flags & ACM_MODULE_FLAG_PREPRODUCTION);
  printf ("    Debug Signed             - %04x\n", Acm->Flags & ACM_MODULE_FLAG_DEBUG_SIGN);
  printf ("  ModuleVendor               - %08x\n", Acm->ModuleVendor);
  printf ("  Date                       - %08x\n", Acm->Date);
  printf ("  Size                       - %08x\n", Acm->Size);
  printf ("  TxtSvn                     - %04x\n", Acm->TxtSvn);
  printf ("  SeSvn                      - %04x\n", Acm->SeSvn);
  printf ("  CodeControl                - %08x\n", Acm->CodeControl);
  printf ("  ErrorEntryPoint            - %08x\n", Acm->ErrorEntryPoint);
  printf ("  GDTLimit                   - %08x\n", Acm->GDTLimit);
  printf ("  GDTBasePtr                 - %08x\n", Acm->GDTBasePtr);
  printf ("  SegSel                     - %08x\n", Acm->SegSel);
  printf ("  EntryPoint                 - %08x\n", Acm->EntryPoint);
  printf ("  KeySize                    - %08x\n", Acm->KeySize);
  printf ("  ScratchSize                - %08x\n", Acm->ScratchSize);

  Buffer = (UINT8 *)(Acm + 1);
  printf ("  RSAPubKey                  - \n");
  DumpHex (Buffer, Acm->KeySize * 4);
  printf ("\n");
  Buffer += Acm->KeySize * 4;

  if (Acm->HeaderVersion == ACM_HEADER_VERSION_3) {
    printf ("  RSASig                     - \n");
    DumpHex (Buffer, ACM_PKCS_1_5_RSA_SIGNATURE_SHA384_SIZE); // PKCS #1.5 RSA Signature
    printf ("\n");
    Buffer += ACM_PKCS_1_5_RSA_SIGNATURE_SHA384_SIZE;
  } else {
    printf ("  RSAPubExp                  - %08x\n", *(UINT32 *)Buffer);
    Buffer += 4;

    printf ("  RSASig                     - \n");
    DumpHex (Buffer, ACM_PKCS_1_5_RSA_SIGNATURE_SHA256_SIZE); // PKCS #1.5 RSA Signature
    printf ("\n");
    Buffer += ACM_PKCS_1_5_RSA_SIGNATURE_SHA256_SIZE;
  }
  Buffer += Acm->ScratchSize * 4;

  if ((Acm->ModuleSubType & ACM_MODULE_SUBTYPE_ANC_MODULE) == 0) {
    ChipsetAcmInformationTable = (CHIPSET_ACM_INFORMATION_TABLE *)Buffer;
    printf ("Chipset ACM info:\n");
    printf (
      "  Guid                       - {%08x-%08x-%08x-%08x}\n",
      ChipsetAcmInformationTable->Guid.Guid0,
      ChipsetAcmInformationTable->Guid.Guid1,
      ChipsetAcmInformationTable->Guid.Guid2,
      ChipsetAcmInformationTable->Guid.Guid3
      );
    printf ("  ChipsetACMType             - %02x\n", ChipsetAcmInformationTable->ChipsetACMType);
    if (ChipsetAcmInformationTable->ChipsetACMType < sizeof(mAcmTypeStr)/sizeof(mAcmTypeStr[0])) {
      printf ("    %s\n", mAcmTypeStr[ChipsetAcmInformationTable->ChipsetACMType]);
    }
    printf ("  Version                    - %02x\n", ChipsetAcmInformationTable->Version);
    printf ("  Length                     - %04x\n", ChipsetAcmInformationTable->Length);
    printf ("  ChipsetIDList              - %08x\n", ChipsetAcmInformationTable->ChipsetIDList);
    printf ("  OsSinitTableVer            - %08x\n", ChipsetAcmInformationTable->OsSinitTableVer);
    printf ("  MinMleHeaderVer            - %08x\n", ChipsetAcmInformationTable->MinMleHeaderVer);
    if (ChipsetAcmInformationTable->Version >= CHIPSET_ACM_INFORMATION_TABLE_VERSION_3) {
      printf ("  Capabilities               - %08x\n", ChipsetAcmInformationTable->Capabilities);
      for (Index = 0; Index < sizeof(mCapabilityStr)/sizeof(mCapabilityStr[0]); Index++) {
        if (mCapabilityStr[Index] == NULL) {
          continue;
        }
        printf (
          "    %s- %08x\n",
          mCapabilityStr[Index],
          (ChipsetAcmInformationTable->Capabilities & (1 << Index))
          );
      }
      printf ("  AcmVersion                 - %02x\n", ChipsetAcmInformationTable->AcmVersion);
      printf ("  AcmRevision                - %02x.%02x.%02x\n", ChipsetAcmInformationTable->AcmRevision[0], ChipsetAcmInformationTable->AcmRevision[1], ChipsetAcmInformationTable->AcmRevision[2]);
    }
    if (ChipsetAcmInformationTable->Version >= CHIPSET_ACM_INFORMATION_TABLE_VERSION_4) {
      printf ("  ProcessorIDList            - %08x\n", ChipsetAcmInformationTable->ProcessorIDList);
    }

    ChipsetIdList = (CHIPSET_ID_LIST *)((UINTN)Acm + ChipsetAcmInformationTable->ChipsetIDList);
    printf ("Chipset ID List info:\n");
    printf ("  Count                      - %08x\n", ChipsetIdList->Count);
    for (Index = 0; Index < ChipsetIdList->Count; Index++) {
      printf ("  ID[%d]:\n", Index);
      printf ("    Flags                    - %08x\n", ChipsetIdList->ChipsetID[Index].Flags);
      printf ("      RevisionIdMask         - %08x\n", ChipsetIdList->ChipsetID[Index].Flags & ACM_CHIPSET_ID_REVISION_ID_MAKE);
      printf ("    VendorID                 - %04x\n", ChipsetIdList->ChipsetID[Index].VendorID);
      printf ("    DeviceID                 - %04x\n", ChipsetIdList->ChipsetID[Index].DeviceID);
      printf ("    RevisionID               - %04x\n", ChipsetIdList->ChipsetID[Index].RevisionID);
    }
    if (ChipsetAcmInformationTable->Version < CHIPSET_ACM_INFORMATION_TABLE_VERSION_4) {
      goto End;
    }
    ProcessorIdList = (PROCESSOR_ID_LIST *)((UINTN)Acm + ChipsetAcmInformationTable->ProcessorIDList);
    printf ("Processor ID List info:\n");
    printf ("  Count                      - %08x\n", ProcessorIdList->Count);
    for (Index = 0; Index < ProcessorIdList->Count; Index++) {
      printf ("  ID[%d]:\n", Index);
      printf ("    FMS                      - %08x\n", ProcessorIdList->ProcessorID[Index].FMS);
      printf ("    FMSMask                  - %08x\n", ProcessorIdList->ProcessorID[Index].FMSMask);
      printf ("    PlatformID               - %016llx\n", (unsigned long long) ProcessorIdList->ProcessorID[Index].PlatformID);
      printf ("    PlatformMask             - %016llx\n", (unsigned long long) ProcessorIdList->ProcessorID[Index].PlatformMask);
    }
  }

End:
  printf (
    "*****************************************************************************\n\n"
    );
}

BOOLEAN
CheckAcm (
  IN ACM_FORMAT                        *Acm,
  IN UINTN                             AcmMaxSize
  )
/*++

Routine Description:

  Check Acm information

Arguments:

  Acm        - ACM buffer
  AcmMaxSize - ACM max size

Returns:

  TRUE  - ACM is valid
  FALSE - ACM is invalid

--*/
{
  CHIPSET_ACM_INFORMATION_TABLE *ChipsetAcmInformationTable;
  CHIPSET_ID_LIST               *ChipsetIdList;
  PROCESSOR_ID_LIST             *ProcessorIdList;
  UINT8                         *Buffer;

  if (Acm->ModuleType != ACM_MODULE_TYPE_CHIPSET_ACM) {
    printf ("ACM invalid : ModuleType!\n");
    return FALSE;
  }
  if (Acm->Size * 4 > AcmMaxSize) {
    printf ("ACM invalid : Size!\n");
    return FALSE;
  }

  Buffer = (UINT8 *)(Acm + 1);
  Buffer += Acm->KeySize * 4;
  if (Acm->HeaderVersion == ACM_HEADER_VERSION_3) {
    Buffer += ACM_PKCS_1_5_RSA_SIGNATURE_SHA384_SIZE;
  } else {
    Buffer += 4;
    Buffer += ACM_PKCS_1_5_RSA_SIGNATURE_SHA256_SIZE;
  }
  Buffer += Acm->ScratchSize * 4;

  if ((Acm->ModuleSubType & ACM_MODULE_SUBTYPE_ANC_MODULE) == 0) {
    ChipsetAcmInformationTable = (CHIPSET_ACM_INFORMATION_TABLE *)Buffer;
    if ((UINTN)ChipsetAcmInformationTable >= (UINTN)Acm + AcmMaxSize) {
      printf ("ACM invalid : ChipsetAcmInformationTable!\n");
      return FALSE;
    }

    if (CompareGuid ((EFI_GUID *)&ChipsetAcmInformationTable->Guid, (EFI_GUID *)&mChipsetAcmInformationTableGuid03) != 0) {
      printf ("ACM invalid : ChipsetACMGuid!\n");
      return FALSE;
    }
    if (ChipsetAcmInformationTable->ChipsetACMType != CHIPSET_ACM_TYPE_BIOS) {
      printf ("ACM invalid : ChipsetACMType!\n");
      return FALSE;
    }
    if (ChipsetAcmInformationTable->Version < CHIPSET_ACM_INFORMATION_TABLE_VERSION_3) {
      printf ("ACM invalid : ChipsetACMVersion!\n");
      return FALSE;
    }
    if ((UINTN)ChipsetAcmInformationTable + ChipsetAcmInformationTable->Length > (UINTN)Acm + AcmMaxSize) {
      printf ("ACM invalid : ChipsetACMLength!\n");
      return FALSE;
    }

    if (ChipsetAcmInformationTable->ChipsetIDList >= AcmMaxSize) {
      printf ("ACM invalid : ChipsetACMChipsetIDList!\n");
      return FALSE;
    }
    ChipsetIdList = (CHIPSET_ID_LIST *)((UINTN)Acm + ChipsetAcmInformationTable->ChipsetIDList);
    if (ChipsetIdList->Count == 0) {
      printf ("ACM invalid : ChipsetACMChipsetIDListCount!\n");
      return FALSE;
    }
    if (ChipsetAcmInformationTable->ChipsetIDList + sizeof(CHIPSET_ID_LIST) + (ChipsetIdList->Count - 1) * sizeof(ACM_CHIPSET_ID) > AcmMaxSize) {
      printf ("ACM invalid : ChipsetACMChipsetIDList!\n");
      return FALSE;
    }

    if (ChipsetAcmInformationTable->Version < CHIPSET_ACM_INFORMATION_TABLE_VERSION_4) {
      goto End;
    }

    if (ChipsetAcmInformationTable->ProcessorIDList >= AcmMaxSize) {
      printf ("ACM invalid : ChipsetACMProcessorIDList!\n");
      return FALSE;
    }
    ProcessorIdList = (PROCESSOR_ID_LIST *)((UINTN)Acm + ChipsetAcmInformationTable->ProcessorIDList);
    if (ProcessorIdList->Count == 0) {
      printf ("ACM invalid : ChipsetACMProcessorIdListCount!\n");
      return FALSE;
    }
    if (ChipsetAcmInformationTable->ChipsetIDList + sizeof(PROCESSOR_ID_LIST) + (ChipsetIdList->Count - 1) * sizeof(ACM_PROCESSOR_ID) > AcmMaxSize) {
      printf ("ACM invalid : ChipsetACMProcessorIdList!\n");
      return FALSE;
    }
  }

End:

  return TRUE;
}

VOID
FillFitTable (
  IN UINT8     *FvBuffer,
  IN UINT32    FvSize,
  IN UINT8     *FitTableOffset
  )
/*++

Routine Description:

  Fill the FIT table information to FvRecovery

Arguments:

  FvBuffer       - FvRecovery binary buffer
  FvSize         - FvRecovery size
  FitTableOffset - The offset of FIT table in FvRecovery file

Returns:

  None

--*/
{
  FIRMWARE_INTERFACE_TABLE_ENTRY *FitEntry;
  UINT32                          FitIndex;
  UINT32                          Index;
  UINT8                           Checksum;
  UINTN                           SubIndex;
  FIT_TABLE_CONTEXT_ENTRY         TempContextEntry;
  FIRMWARE_INTERFACE_TABLE_ENTRY  TempTableEntry;

  //
  // 1. FitPointer
  //
  *(UINT64 *)(FvBuffer + FvSize - gFitTableContext.FitTablePointerOffset) = (UINT64)(UINTN)MEMORY_TO_FLASH (FitTableOffset, FvBuffer, FvSize);
  if (gFitTableContext.FitTablePointerOffset2 != 0) {
    *(UINT64 *)(FvBuffer + FvSize - gFitTableContext.FitTablePointerOffset2) = (UINT64)(UINTN)MEMORY_TO_FLASH (FitTableOffset, FvBuffer, FvSize);
  }

  FitEntry = (FIRMWARE_INTERFACE_TABLE_ENTRY *)FitTableOffset;
  FitIndex = 0;

  //
  // 2. FitHeader
  //
  FitEntry[FitIndex].Address             = *(UINT64 *)"_FIT_   ";
  *(UINT32 *)&FitEntry[FitIndex].Size[0] = gFitTableContext.FitEntryNumber;
  FitEntry[FitIndex].Version             = (UINT16)gFitTableContext.FitHeaderVersion;
  FitEntry[FitIndex].Type                = FIT_TABLE_TYPE_HEADER;
  FitEntry[FitIndex].C_V                 = 1;
  //
  // Checksum will be updated later...
  //
  FitEntry[FitIndex].Checksum            = 0;

  //
  // 3. Microcode
  //
  FitIndex++;
  for (Index = 0; Index < gFitTableContext.MicrocodeNumber; Index++) {
    FitEntry[FitIndex].Address             = gFitTableContext.Microcode[Index].Address;
    *(UINT32 *)&FitEntry[FitIndex].Size[0] = 0; //gFitTableContext.Microcode[Index].Size / 16;
    FitEntry[FitIndex].Version             = (UINT16)gFitTableContext.MicrocodeVersion;
    FitEntry[FitIndex].Type                = FIT_TABLE_TYPE_MICROCODE;
    FitEntry[FitIndex].C_V                 = 0;
    FitEntry[FitIndex].Checksum            = 0;
    FitIndex++;
  }

  //
  // 4. StartupAcm
  //
  if (gFitTableContext.StartupAcm.Address != 0) {
    FitEntry[FitIndex].Address             = gFitTableContext.StartupAcm.Address;
    *(UINT32 *)&FitEntry[FitIndex].Size[0] = 0; //gFitTableContext.StartupAcm.Size / 16;
    FitEntry[FitIndex].Version             = (UINT16)gFitTableContext.StartupAcmVersion;
    FitEntry[FitIndex].Type                = FIT_TABLE_TYPE_STARTUP_ACM;
    FitEntry[FitIndex].C_V                 = 0;
    FitEntry[FitIndex].Checksum            = 0;
    FitIndex++;
  }

  //
  // 5. BiosModule
  //
  //
  // BiosModule segments order needs to be put from low addresss to high for Btg requirement
  //
  if (gFitTableContext.BiosModuleNumber > 1) {
    for (Index = 0; Index < (UINTN)gFitTableContext.BiosModuleNumber - 1; Index++){
      for (SubIndex = 0; SubIndex < gFitTableContext.BiosModuleNumber - Index - 1; SubIndex++) {
        if (gFitTableContext.BiosModule[SubIndex].Address > gFitTableContext.BiosModule[SubIndex + 1].Address) {
          CopyMem (&TempContextEntry, &gFitTableContext.BiosModule[SubIndex], sizeof(FIT_TABLE_CONTEXT_ENTRY));
          CopyMem (&gFitTableContext.BiosModule[SubIndex], &gFitTableContext.BiosModule[SubIndex + 1], sizeof(FIT_TABLE_CONTEXT_ENTRY));
          CopyMem (&gFitTableContext.BiosModule[SubIndex + 1], &TempContextEntry, sizeof(FIT_TABLE_CONTEXT_ENTRY));
        }
      }
    }
  }
  for (Index = 0; Index < gFitTableContext.BiosModuleNumber; Index++) {
    FitEntry[FitIndex].Address             = gFitTableContext.BiosModule[Index].Address;
    *(UINT32 *)&FitEntry[FitIndex].Size[0] = gFitTableContext.BiosModule[Index].Size / 16;
    FitEntry[FitIndex].Version             = (UINT16)gFitTableContext.BiosModuleVersion;
    FitEntry[FitIndex].Type                = FIT_TABLE_TYPE_BIOS_MODULE;
    FitEntry[FitIndex].C_V                 = 0;
    FitEntry[FitIndex].Checksum            = 0;
    FitIndex++;
  }

  //
  // 6. Optional module
  //
  for (Index = 0; Index < gFitTableContext.OptionalModuleNumber; Index++) {
    FitEntry[FitIndex].Address             = gFitTableContext.OptionalModule[Index].Address;
    *(UINT32 *)&FitEntry[FitIndex].Size[0] = gFitTableContext.OptionalModule[Index].Size;
    FitEntry[FitIndex].Version             = (UINT16)gFitTableContext.OptionalModule[Index].Version;
    FitEntry[FitIndex].Type                = (UINT8)gFitTableContext.OptionalModule[Index].Type;
    FitEntry[FitIndex].C_V                 = 0;
    FitEntry[FitIndex].Checksum            = 0;
    FitIndex++;
  }

  //
  // 7. Port module
  //
  for (Index = 0; Index < gFitTableContext.PortModuleNumber; Index++) {
    FitEntry[FitIndex].Address             = gFitTableContext.PortModule[Index].Address + ((UINT64)gFitTableContext.PortModule[Index].Size << 32);
    *(UINT32 *)&FitEntry[FitIndex].Size[0] = 0;
    FitEntry[FitIndex].Version             = (UINT16)gFitTableContext.PortModule[Index].Version;
    FitEntry[FitIndex].Type                = (UINT8)gFitTableContext.PortModule[Index].Type;
    FitEntry[FitIndex].C_V                 = 0;
    FitEntry[FitIndex].Checksum            = 0;
    FitIndex++;
  }

  //
  // The FIT records must always be arranged in the ascending order of their type attribute in the FIT.
  //
  for (Index = 0; Index < (UINTN)FitIndex - 1; Index++){
    for (SubIndex = 0; SubIndex < FitIndex - Index - 1; SubIndex++) {
      if (FitEntry[SubIndex].Type > FitEntry[SubIndex + 1].Type) {
        CopyMem (&TempTableEntry, &FitEntry[SubIndex], sizeof(FIRMWARE_INTERFACE_TABLE_ENTRY));
        CopyMem (&FitEntry[SubIndex], &FitEntry[SubIndex + 1], sizeof(FIRMWARE_INTERFACE_TABLE_ENTRY));
        CopyMem (&FitEntry[SubIndex + 1], &TempTableEntry, sizeof(FIRMWARE_INTERFACE_TABLE_ENTRY));
      }
    }
  }

  //
  // Update FIT header signature as final step
  //
  Checksum = CalculateChecksum8 ((UINT8 *)&FitEntry[0], sizeof (FIRMWARE_INTERFACE_TABLE_ENTRY) * FitIndex);
  FitEntry[0].Checksum = Checksum;
}

VOID
ClearFitTable (
  IN UINT8     *FvBuffer,
  IN UINT32    FvSize
  )
/*++

Routine Description:

  Clear the FIT table information to Fvrecovery

Arguments:

  FvBuffer       - Fvrecovery binary buffer
  FvSize         - Fvrecovery size

Returns:

  None

--*/
{
  FIRMWARE_INTERFACE_TABLE_ENTRY *FitEntry;
  UINT32                          EntryNum;
  UINT32                          FitIndex;
  UINT64                          FitTablePointer;
  UINT8                           *Buffer;
  UINT32                          BufferSize;

  FitTablePointer = *(UINT64 *)(FvBuffer + FvSize - gFitTableContext.FitTablePointerOffset);
  FitEntry = (FIRMWARE_INTERFACE_TABLE_ENTRY *)FLASH_TO_MEMORY (FitTablePointer, FvBuffer, FvSize);

  //
  // Clear FIT pointer
  //
  *(UINT64 *)(FvBuffer + FvSize - gFitTableContext.FitTablePointerOffset) = 0xEEEEEEEEEEEEEEEEull;
  if (gFitTableContext.FitTablePointerOffset2 != 0) {
    *(UINT64 *)(FvBuffer + FvSize - gFitTableContext.FitTablePointerOffset2) = 0xEEEEEEEEEEEEEEEEull;
  }

  //
  // Clear FIT table
  //
  EntryNum = *(UINT32 *)(&FitEntry[0].Size[0]) & 0xFFFFFF;
  for (FitIndex = 0; FitIndex < EntryNum; FitIndex++) {
    switch (FitEntry[FitIndex].Type) {
    case FIT_TABLE_TYPE_BIOS_POLICY:
    case FIT_TABLE_TYPE_KEY_MANIFEST:
    case FIT_TABLE_TYPE_BOOT_POLICY_MANIFEST:
    case FIT_TABLE_TYPE_BIOS_DATA_AREA:
    case FIT_TABLE_TYPE_CSE_SECURE_BOOT:
      //
      // Clear FIT table data buffer
      //
      Buffer = FLASH_TO_MEMORY (FitEntry[FitIndex].Address, FvBuffer, FvSize);
      BufferSize = (*(UINT32 *)FitEntry[FitIndex].Size) & 0xFFFFFF;
      SetMem (Buffer, BufferSize, 0xFF);
      break;
    default:
      break;
    }
    //
    // Clear FIT table itself
    //
    SetMem (&FitEntry[FitIndex], sizeof(FitEntry[FitIndex]), 0xFF);
  }
}

STATUS
WriteOutputFile (
  IN CHAR8   *FileName,
  IN UINT8   *FileData,
  IN UINT32  FileSize
  )
/*++

Routine Description:

  Read input file

Arguments:

  FileName      - The input file name
  FileData      - The input file data
  FileSize      - The input file size

Returns:

  STATUS_SUCCESS - Write file data successfully
  STATUS_ERROR   - The file data is not written

--*/
{
  FILE                        *FpOut;

  //
  // Open the output FvRecovery.fv file
  //
  if ((FpOut = fopen (FileName, "w+b")) == NULL) {
    Error (NULL, 0, 0, "Unable to open file", "%s", FileName);
    return STATUS_ERROR;
  }
  //
  // Write the output FvRecovery.fv file
  //
  if ((fwrite (FileData, 1, FileSize, FpOut)) != FileSize) {
    Error (NULL, 0, 0, "Write output file error!", NULL);
    fclose (FpOut);
    return STATUS_ERROR;
  }

  //
  // Close the output FvRecovery.fv file
  //
  fclose (FpOut);

  return STATUS_SUCCESS;
}

UINT32
GetFvRecoveryInfoFromFd (
  IN UINT8                       *FdBuffer,
  IN UINT32                      FdFileSize,
  OUT UINT8                      **FvRecovery
  )
/*++

Routine Description:

  Get FvRecovery information from Fd file.

Arguments:

  FdBuffer     - Fd file buffer.
  FdFileSize   - Fd file size.
  FvRecovery   - FvRecovery pointer in Fd file buffer

Returns:
  FvRecovery file size

--*/
{
  UINT8                         *FileBuffer = NULL;
  UINT32                        FvRecoveryFileSize =0;
  EFI_GUID                      VTFGuid = EFI_FFS_VOLUME_TOP_FILE_GUID;
  UINT32                        FvLength;
  UINT32                        FileLength;

  *FvRecovery = NULL;
  FileBuffer = FindNextFvHeader (FdBuffer, FdFileSize);
  if (FileBuffer == NULL) {
    return 0;
  }

  while ((UINTN)FileBuffer < (UINTN)FdBuffer + FdFileSize) {
    FvLength         = (UINT32)((EFI_FIRMWARE_VOLUME_HEADER *)FileBuffer)->FvLength;

    if (FindFileFromFvByGuid (FileBuffer, FvLength, &VTFGuid, &FileLength) != NULL) {
      //
      // Found the VTF
      //
      FvRecoveryFileSize = FvLength;
      *FvRecovery = FileBuffer;
    }

    //
    // Next fv
    //
    FileBuffer = (UINT8 *)FileBuffer + FvLength;
    if ((UINTN)FileBuffer >= (UINTN)FdBuffer + FdFileSize) {
      break;
    }
    FileBuffer = FindNextFvHeader (FileBuffer, (UINTN)FdBuffer + FdFileSize - (UINTN)FileBuffer);
    if (FileBuffer == NULL) {
      break;
    }

  }

  //
  // Return
  //
  return FvRecoveryFileSize;
}

UINT32
GetFitEntryInfo (
  IN UINT8     *FvBuffer,
  IN UINT32    FvSize
  )
/*++

Routine Description:

  Fill the FIT table information to Fvrecovery

Arguments:

  FvBuffer       - Fvrecovery binary buffer
  FvSize         - Fvrecovery size

Returns:

  0 - Fit Table not found

--*/
{
  FIRMWARE_INTERFACE_TABLE_ENTRY *FitEntry;
  UINT32                          FitIndex;
  UINT32                          FitTableOffset;

  //
  // 1. FitPointer
  //
  if (gFitTableContext.FitTablePointerOffset == 0) {
    gFitTableContext.FitTablePointerOffset = DEFAULT_FIT_TABLE_POINTER_OFFSET;
  }
  gFitTableContext.FitTablePointerOffset2 = 0;

  FitTableOffset = *(UINT32 *)(FvBuffer + FvSize - gFitTableContext.FitTablePointerOffset);

  FitEntry = (FIRMWARE_INTERFACE_TABLE_ENTRY *)FLASH_TO_MEMORY(FitTableOffset, FvBuffer, FvSize);
  FitIndex = 0;

  //
  // 2. FitHeader
  //
  if (FitEntry[FitIndex].Address != *(UINT64 *)"_FIT_   ") {
    return 0;
  }
  if (FitEntry[FitIndex].Type != FIT_TABLE_TYPE_HEADER) {
    return 0;
  }
  gFitTableContext.FitEntryNumber = *(UINT32 *)&FitEntry[FitIndex].Size[0];
  gFitTableContext.FitHeaderVersion = FitEntry[FitIndex].Version;

  //
  // 3. FitEntry
  //
  FitIndex++;
  for (; FitIndex < gFitTableContext.FitEntryNumber; FitIndex++) {
    switch (FitEntry[FitIndex].Type) {
    case FIT_TABLE_TYPE_MICROCODE:
      gFitTableContext.Microcode[gFitTableContext.MicrocodeNumber].Address = (UINT32)FitEntry[FitIndex].Address;
      gFitTableContext.MicrocodeVersion                                    = FitEntry[FitIndex].Version;
      gFitTableContext.MicrocodeNumber ++;
      break;
    case FIT_TABLE_TYPE_STARTUP_ACM:
      gFitTableContext.StartupAcm.Address = (UINT32)FitEntry[FitIndex].Address;
      gFitTableContext.StartupAcmVersion  = FitEntry[FitIndex].Version;
      break;
    case FIT_TABLE_TYPE_BIOS_MODULE:
      gFitTableContext.BiosModule[gFitTableContext.BiosModuleNumber].Address = (UINT32)FitEntry[FitIndex].Address;
      gFitTableContext.BiosModule[gFitTableContext.BiosModuleNumber].Size    = *(UINT32 *)&FitEntry[FitIndex].Size[0] * 16;
      gFitTableContext.BiosModuleVersion                                     = FitEntry[FitIndex].Version;
      gFitTableContext.BiosModuleNumber ++;
      break;
    case FIT_TABLE_TYPE_TPM_POLICY:
    case FIT_TABLE_TYPE_TXT_POLICY:
      if (FitEntry[FitIndex].Version == 0) {
        gFitTableContext.PortModule[gFitTableContext.PortModuleNumber].Address = (UINT32)FitEntry[FitIndex].Address;
        gFitTableContext.PortModule[gFitTableContext.PortModuleNumber].Size    = (UINT32)(FitEntry[FitIndex].Address >> 32);
        gFitTableContext.PortModule[gFitTableContext.PortModuleNumber].Version = FitEntry[FitIndex].Version;
        gFitTableContext.PortModule[gFitTableContext.PortModuleNumber].Type    = FitEntry[FitIndex].Type;
        gFitTableContext.PortModuleNumber ++;
        break;
      }
      // Not Port Configure, pass through
    default: // Others
      gFitTableContext.OptionalModule[gFitTableContext.OptionalModuleNumber].Address = (UINT32)FitEntry[FitIndex].Address;
      gFitTableContext.OptionalModule[gFitTableContext.OptionalModuleNumber].Size    = *(UINT32 *)&FitEntry[FitIndex].Size[0];
      gFitTableContext.OptionalModule[gFitTableContext.OptionalModuleNumber].Version = FitEntry[FitIndex].Version;
      gFitTableContext.OptionalModule[gFitTableContext.OptionalModuleNumber].Type    = FitEntry[FitIndex].Type;
      gFitTableContext.OptionalModuleNumber ++;
      break;
    }
  }

  return gFitTableContext.FitEntryNumber;
}

STATUS
FitGen (
  IN INTN   argc,
  IN CHAR8  **argv
  )
/*++

Routine Description:

  Main function for FitGen.

Arguments:

  argc - Number of command line parameters.
  argv - Array of pointers to parameter strings.

Returns:
  STATUS_SUCCESS - Utility exits successfully.
  STATUS_ERROR   - Some error occurred during execution.

--*/
{
  UINT32                      FvRecoveryFileSize;
  UINT8                       *FileBuffer;
  UINT8                       *FileBufferRaw;
  UINTN                       FitEntryNumber;
  UINT8                       *FitTableOffset;
  STATUS                      Status;
  UINT32                      FitTableSize;

  BOOLEAN                     IsFv;
  UINT8                       *FdFileBuffer;
  UINT32                      FdFileSize;

  UINT8                       *AcmBuffer;

  //
  // Step 0: Check FV or FD
  //
  if (((strcmp (argv[1], "-D") == 0) ||
       (strcmp (argv[1], "-d") == 0)) ) {
    IsFv = FALSE;
  } else {
    IsFv = TRUE;
  }

  //
  // Step 1: Read InputFvRecovery.fv data
  //
  if (IsFv) {
    Status = ReadInputFile (argv[1], &FileBuffer, &FvRecoveryFileSize, &FileBufferRaw);
    if (Status != STATUS_SUCCESS) {
      Error (NULL, 0, 0, "Unable to open file", "%s", argv[1]);
      goto exitFunc;
    }
    FdFileBuffer = FileBuffer;
    FdFileSize = FvRecoveryFileSize;
  } else {
    Status = ReadInputFile (argv[2], &FdFileBuffer, &FdFileSize, &FileBufferRaw);
    if (Status != STATUS_SUCCESS) {
      Error (NULL, 0, 0, "Unable to open file", "%s", argv[2]);
      goto exitFunc;
    }

    //
    // Get Fvrecovery information
    //
    FvRecoveryFileSize = GetFvRecoveryInfoFromFd (FdFileBuffer, FdFileSize, &FileBuffer);
    if ((FvRecoveryFileSize == 0) || (FileBuffer == NULL)) {
      Error (NULL, 0, 0, "FvRecovery not found in Fd file!", NULL);
      Status = STATUS_ERROR;
      goto exitFunc;
    }
  }

  //
  // Step 2: Calculate FIT entry number.
  //
  FitEntryNumber = GetFitEntryNumber (argc, argv, FdFileBuffer, FdFileSize);
  if (!gFitTableContext.Clear) {
    if (FitEntryNumber == 0) {
      Status = STATUS_ERROR;
      goto exitFunc;
    }

    //
    // For debug
    //
    PrintFitData ();

    //
    // Add 1 more FitEntry as place holder, because we need exclude FIT table itself
    //
    FitEntryNumber++;

    //
    // Step 3: Get enough space in FvRecovery.fv
    //
    FitTableOffset = GetFreeSpaceFromFv (FileBuffer, FvRecoveryFileSize, FitEntryNumber);
    if (FitTableOffset == NULL) {
      return STATUS_ERROR;
    }
    FitTableSize = FitEntryNumber * sizeof(FIRMWARE_INTERFACE_TABLE_ENTRY);
    FitTableSize += FIT_ALIGNMENT;
    FitTableSize &= ~FIT_ALIGNMENT;

    CheckOverlap (
      MEMORY_TO_FLASH (FitTableOffset, FdFileBuffer, FdFileSize),
      FitTableSize
      );

    //
    // Get ACM buffer
    //
    if (gFitTableContext.StartupAcm.Address != 0) {
      AcmBuffer = FLASH_TO_MEMORY(gFitTableContext.StartupAcm.Address, FdFileBuffer, FdFileSize);
      if ((AcmBuffer < FdFileBuffer) || (AcmBuffer + gFitTableContext.StartupAcm.Size > FdFileBuffer + FdFileSize)) {
        printf ("ACM out of range - can not validate it\n");
        AcmBuffer = NULL;
      }

      if (AcmBuffer != NULL) {
        if (CheckAcm ((ACM_FORMAT *)AcmBuffer, gFitTableContext.StartupAcm.Size)) {
          DumpAcm ((ACM_FORMAT *)AcmBuffer);
        } else {
          Status = STATUS_ERROR;
          goto exitFunc;
        }
      }

    }

    //
    // Step 4: Fill the FIT table one by one
    //
    FillFitTable (FdFileBuffer, FdFileSize, FitTableOffset);

    //
    // For debug
    //
    PrintFitTable (FdFileBuffer, FdFileSize);
  } else {
    printf ("Clear FIT table ...\n");
    //
    // Step 3: Get FIT table info
    //
    FitEntryNumber = GetFitEntryInfo (FdFileBuffer, FdFileSize);
    if (FitEntryNumber == 0) {
      Error (NULL, 0, 0, "No FIT table found", NULL);
      return STATUS_ERROR;
    }

    //
    // For debug
    //
    PrintFitTable (FdFileBuffer, FdFileSize);

    //
    // Step 4: Clear FIT table
    //
    ClearFitTable (FdFileBuffer, FdFileSize);
    printf ("Clear FIT table Done!\n");
  }

  //
  // Step 5: Write OutputFvRecovery.fv data
  //
  if (IsFv) {
    Status = WriteOutputFile (argv[2], FileBuffer, FvRecoveryFileSize);
  } else {
    Status = WriteOutputFile (argv[3], FdFileBuffer, FdFileSize);
  }

exitFunc:
  if (FileBufferRaw != NULL) {
    free ((VOID *)FileBufferRaw);
  }
  return Status;
}

STATUS
FitView (
  IN INTN   argc,
  IN CHAR8  **argv
  )
/*++

Routine Description:

  View function for FitGen.

Arguments:

  argc - Number of command line parameters.
  argv - Array of pointers to parameter strings.

Returns:
  STATUS_SUCCESS - Utility exits successfully.
  STATUS_ERROR   - Some error occurred during execution.

--*/
{
  UINT32                      FvRecoveryFileSize;
  UINT8                       *FileBuffer;
  UINT8                       *FileBufferRaw = NULL;
  STATUS                      Status;

  //
  // Step 1: Read input file
  //
  Status = ReadInputFile (argv[2], &FileBuffer, &FvRecoveryFileSize, &FileBufferRaw);
  if (Status != STATUS_SUCCESS) {
    Error (NULL, 0, 0, "Unable to open file", "%s", argv[2]);
    goto exitFunc;
  }

  // no -f option, use default FIT pointer offset
  if (argc == 3) {
    //
    // Use default address
    //
    gFitTableContext.FitTablePointerOffset = DEFAULT_FIT_TABLE_POINTER_OFFSET;
  } else if (stricmp (argv[3], "-f") == 0) {
    if (argc == 5) {
      //
      // Get offset from parameter
      //
      gFitTableContext.FitTablePointerOffset = xtoi (argv[3 + 1]);
    } else {
      Error (NULL, 0, 0, "FIT offset not specified!", NULL);
      goto exitFunc;
    }
  } else {
    Error (NULL, 0, 0, "Invalid view option: ", "%s", argv[3]);
    goto exitFunc;
  }

  //
  // For debug
  //
  PrintFitTable (FileBuffer, FvRecoveryFileSize);

exitFunc:
  if (FileBufferRaw != NULL) {
    free ((VOID *)FileBufferRaw);
  }
  return Status;
}

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

Routine Description:

  Main function.

Arguments:

  argc - Number of command line parameters.
  argv - Array of pointers to parameter strings.

Returns:
  STATUS_SUCCESS - Utility exits successfully.
  STATUS_ERROR   - Some error occurred during execution.

--*/
{
  SetUtilityName (UTILITY_NAME);

  //
  // Display utility information
  //
  PrintUtilityInfo ();

  //
  // Verify the correct number of arguments
  //
  if (argc >= MIN_VIEW_ARGS && stricmp (argv[1], "-view") == 0) {
    return FitView (argc, argv);
  } else if (argc >= MIN_ARGS) {
    return FitGen (argc, argv);
  } else {
    Error (NULL, 0, 0, "invalid number of input parameters specified", NULL);
    PrintUsage ();
    return STATUS_ERROR;
  }
}

unsigned int
xtoi (
  char  *str
  )
/*++

Routine Description:

  Convert hex string to uint

Arguments:

  str  -  The string

Returns:

--*/
{
  unsigned int u;
  char         c;
  unsigned int m;

  if (str == NULL) {
    return 0;
  }

  m = (unsigned int) -1 >> 4;
  //
  // skip preceeding white space
  //
  while (*str && *str == ' ') {
    str += 1;
  }
  //
  // skip preceeding zeros
  //
  while (*str && *str == '0') {
    str += 1;
  }
  //
  // skip preceeding x/X character
  //
  if (*str && (*str == 'x' || *str == 'X')) {
    str += 1;
  }
  //
  // convert hex digits
  //
  u = 0;
  c = *(str++);
  while (c) {
    if (c >= 'a' && c <= 'f') {
      c -= 'a' - 'A';
    }

    if ((c >= '0' && c <= '9') || (c >= 'A' && c <= 'F')) {
      if (u > m) {
        return (unsigned int) -1;
      }

      u = (u << 4) | (c - (c >= 'A' ? 'A' - 10 : '0'));
    } else {
      //
      // Let application exit immediately
      //
      Error (NULL, 0, 0, "Hex value is expected!", NULL);
      exit (0);
      break;
    }

    c = *(str++);
  }

  return u;
}

