/*++

Copyright (c)  1999 - 2006 Intel Corporation. All rights reserved
This software and associated documentation (if any) is furnished
under a license and may only be used or copied in accordance
with the terms of the license. Except as permitted by such
license, no part of this software or documentation may be
reproduced, stored in a retrieval system, or transmitted in any
form or by any means without the express written consent of
Intel Corporation.


Module Name:

  GenBsfFixup.c

Abstract:

  Utility to Fixup the SEC component for IA32.  This is an 
  interim tool in place of the full GenBsfImage support for 
  IA32.
  This tool supports the Synch3 update

--*/

#include "BaseTypes.h"
#include "UefiBaseTypes.h"
#include "EfiImage.h"
#include "FirmwareVolumeHeader.h"
#include "FirmwareVolumeImageFormat.h"
#include "ParseInf.h"
#include "CommonLib.h"
#include "FirmwareFileSystem.h"
#include "FvLib.h"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <assert.h>

//
// BugBug -- this port to the new FFS is really weird.
//           A lot of the file-header stuff has been ported, but
//           not the section information.
//

#define UTILITY_NAME "GenBsfFixup"
#define UTILITY_MAJOR_VERSION 0
#define UTILITY_MINOR_VERSION 1

UINT32  gFixup;

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

Routine Description:

  GC_TODO: Add function description

Arguments:

  ActualSize  - GC_TODO: add argument description
  Alignment   - GC_TODO: add argument description

Returns:

  GC_TODO: add return values

--*/
{
  UINT32  OccupiedSize;

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

  return OccupiedSize;
}

static
void
Version (
  VOID
  )
{
  printf ("%s v%d.%d -EDK Utility to Fixup the SEC component for IA32.\n", UTILITY_NAME, UTILITY_MAJOR_VERSION, UTILITY_MINOR_VERSION);
  printf ("Copyright (c) 1999-2006 Intel Corporation. All rights reserved.\n");
}


VOID
Usage (
  VOID
  )
{
  Version();
  printf ("\nUsage: " UTILITY_NAME " FvVolumeImageFile AddressOfFvInMemory OffsetOfFixup OutputFileName \n");
}

int
ReadHeader (
  FILE    *In,
  UINT32  *FvSize
  )
/*++

Routine Description:

  Reads in Firmware Volume information from the volume header file.

Arguments:

  In: Firmware Volume header file to read from
  FvSize: Size of Firmware Volume

Returns:

  int: Number of bytes read

--*/
{
  EFI_FIRMWARE_VOLUME_HEADER  VolumeHeader;
  EFI_FV_BLOCK_MAP_ENTRY      BlockMap;
  INT32                       SigTemp[2];
  INT32                       Invert;
  INT32                       bytesread;
  UINT32                      size;

  size      = 0;
  Invert    = 0;
  bytesread = 0;

  if (In == NULL) {
    printf ("Error: Input file is NULL.\n");
    return -1;
  }

  fread (&VolumeHeader, sizeof (EFI_FIRMWARE_VOLUME_HEADER) - sizeof (EFI_FV_BLOCK_MAP_ENTRY), 1, In);
  bytesread   = sizeof (EFI_FIRMWARE_VOLUME_HEADER) - sizeof (EFI_FV_BLOCK_MAP_ENTRY);
  SigTemp[0]  = VolumeHeader.Signature;
  SigTemp[1]  = 0;

  if (VolumeHeader.Attributes & EFI_FVB_ERASE_POLARITY) {
    Invert = 1;
  }

  do {
    fread (&BlockMap, sizeof (EFI_FV_BLOCK_MAP_ENTRY), 1, In);
    bytesread += sizeof (EFI_FV_BLOCK_MAP_ENTRY);

    if (BlockMap.NumBlocks != 0) {
      size += BlockMap.NumBlocks * BlockMap.BlockLength;
    }

  } while (BlockMap.NumBlocks != 0);

  *FvSize = size;
  rewind (In);

  if (Invert == 1) {
    bytesread *= -1;
  }

  return bytesread;
}

UINT32
GetSectionLength (
  IN  UINT32  *Length
  )
/*++

Routine Description:

  Converts a UINT8[3] array to a UINT32

Arguments:

  Length        A pointer to a 3 byte array

Returns:

  UINT32:       The length.

--*/
{
  return *Length & 0x00FFFFFF;
}

int
Readfile (
  UINT8   *FvImage,
  int     bytes,
  int     Invert
  )
/*++

Routine Description:

  GC_TODO: Add function description

Arguments:

  FvImage - GC_TODO: add argument description
  bytes   - GC_TODO: add argument description
  Invert  - GC_TODO: add argument description

Returns:

  GC_TODO: add return values

--*/
{
  UINT32                    FileLength;
  UINT32                    OccupiedFileLength;
  EFI_FFS_FILE_HEADER       *FileHeader;
  UINT8                     FileState;
  UINT8                     Checksum;
  UINT8                     *Ptr;
  UINT32                    SectionLength;
  EFI_COMMON_SECTION_HEADER *SectionHeader;
  EFI_IMAGE_NT_HEADERS      *PeHeader;
  UINT32                    PeiCoreOffset;

  Ptr         = FvImage + bytes;

  FileHeader  = (EFI_FFS_FILE_HEADER *) Ptr;

  FileState   = GetFileState ((UINT8) Invert, FileHeader);

  switch (FileState) {
  case EFI_FILE_HEADER_CONSTRUCTION:
  case EFI_FILE_HEADER_INVALID:
    return sizeof (EFI_FFS_FILE_HEADER);

  case EFI_FILE_HEADER_VALID:
    Checksum  = CalculateSum8 ((UINT8 *) FileHeader, sizeof (EFI_FFS_FILE_HEADER));
    Checksum  = (UINT8) (Checksum - FileHeader->IntegrityCheck.Checksum.File);
    Checksum  = (UINT8) (Checksum - FileHeader->State);
    if (Checksum != 0) {
      return -1;
    }
    //
    // Now do the fixup stuff - begin
    //
    if (FileHeader->Type == EFI_FV_FILETYPE_PEI_CORE) {
      SectionHeader = (EFI_COMMON_SECTION_HEADER *) FileHeader + sizeof (EFI_FFS_FILE_HEADER);
      SectionLength = GetSectionLength ((UINT32 *) &SectionHeader->Size[0]);

      printf ("Section length is 0x%X\n", SectionLength);

      if (SectionHeader->Type == EFI_SECTION_PE32) {

        gFixup    = bytes + sizeof (EFI_FFS_FILE_HEADER) + sizeof (EFI_COMMON_SECTION_HEADER);

        PeHeader  = (EFI_IMAGE_NT_HEADERS *) Ptr + sizeof (EFI_FFS_FILE_HEADER) + sizeof (EFI_COMMON_SECTION_HEADER);

        if (((EFI_IMAGE_DOS_HEADER *) PeHeader)->e_magic == EFI_IMAGE_DOS_SIGNATURE) {
          //
          // DOS image header is present, so read the PE header after the DOS image header
          //
          PeHeader = (EFI_IMAGE_NT_HEADERS *) ((UINTN) PeHeader + (UINTN) ((((EFI_IMAGE_DOS_HEADER *) PeHeader)->e_lfanew) & 0x0ffff));

        }

        PeiCoreOffset = (UINTN) ((UINTN) (PeHeader->OptionalHeader.AddressOfEntryPoint & 0x0ffffffff));

        gFixup += PeiCoreOffset;
      }
    }

    FileLength          = GetLength (FileHeader->Size);
    OccupiedFileLength  = GetOccupiedSize (FileLength, 8);
    return OccupiedFileLength;

  case EFI_FILE_DATA_VALID:
    //
    // Calculate header checksum
    //
    Checksum  = CalculateSum8 ((UINT8 *) FileHeader, sizeof (EFI_FFS_FILE_HEADER));
    Checksum  = (UINT8) (Checksum - FileHeader->IntegrityCheck.Checksum.File);
    Checksum  = (UINT8) (Checksum - FileHeader->State);
    if (Checksum != 0) {
      return -1;
    }
    //
    // Determine file length
    //
    FileLength          = GetLength (FileHeader->Size);
    OccupiedFileLength  = GetOccupiedSize (FileLength, 8);

    //
    // Determine if file checksum is valid or fixed
    //
    if (FileHeader->Attributes & FFS_ATTRIB_CHECKSUM) {
      Checksum  = CalculateSum8 (Ptr, FileLength);
      Checksum  = (UINT8) (Checksum - FileHeader->State);
      if (Checksum != 0) {
        return -1;
      }
    } else {
      if (FileHeader->IntegrityCheck.Checksum.File != FFS_FIXED_CHECKSUM) {
        return -1;
      }
    }
    break;

  case EFI_FILE_MARKED_FOR_UPDATE:
  case EFI_FILE_DELETED:
    //
    // Calculate header checksum
    //
    Checksum  = CalculateSum8 ((UINT8 *) FileHeader, sizeof (EFI_FFS_FILE_HEADER));
    Checksum  = (UINT8) (Checksum - FileHeader->IntegrityCheck.Checksum.File);
    Checksum  = (UINT8) (Checksum - FileHeader->State);
    if (Checksum != 0) {
      return -1;
    }
    //
    // Determine file length
    //
    FileLength          = GetLength (FileHeader->Size);
    OccupiedFileLength  = GetOccupiedSize (FileLength, 8);

    //
    // Determine if file checksum is valid or fixed
    //
    if (FileHeader->Attributes & FFS_ATTRIB_CHECKSUM) {
      Checksum  = CalculateSum8 (Ptr, FileLength);
      Checksum  = (UINT8) (Checksum - FileHeader->State);
      if (Checksum != 0) {
        return -1;
      }
    } else {
      if (FileHeader->IntegrityCheck.Checksum.File != FFS_FIXED_CHECKSUM) {
        return -1;
      }
    }

    return OccupiedFileLength;

  default:
    return sizeof (EFI_FFS_FILE_HEADER);
  }

  return OccupiedFileLength;
}

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

Routine Description:

  Runs GenBsfFixup

Arguments:

  argc: number of command line arguments

  arg[0] = This file name
  arg[1] = Firmware Volume Name
  arg[2] = Base Address to relocate
  arg[3] = Relative offset of the fixup to perform
  arg[4] = Output File Name

Returns:
    
  int: 0 code success, -1 code failure

--*/
{
  FILE        *In;
  FILE        *Out;
  int         ByteStart;
  int         Invert;
  int         Index;
  int         cnt;
  UINT8       *FvImage;
  int         ByteRead;
  UINT32      FvSize;
  UINT64      delta;
  UINT32      Idx;
  UINT64      FvOffset;
  EFI_STATUS  Status;

  Index   = 0;
  Invert  = 0;
 
  if (argc == 1) {
    Usage();
    return -1;
  }
  
  if ((strcmp(argv[1], "-h") == 0) || (strcmp(argv[1], "--help") == 0) ||
      (strcmp(argv[1], "-?") == 0) || (strcmp(argv[1], "/?") == 0)) {
    Usage();
    return -1;
  }
  
  if ((strcmp(argv[1], "-V") == 0) || (strcmp(argv[1], "--version") == 0)) {
    Version();
    return -1;
  }
 
  if (argc != 5) {
    Usage();
    return -1;
  }

  In = fopen (argv[1], "rb");

  if (In == NULL) {
    printf ("Unable to open FV image file \"%s\"\n", argv[1]);
    return -1;
  }

  ByteStart = ReadHeader (In, &FvSize);

  if (ByteStart < 0) {
    Invert = 1;
    ByteStart *= -1;
  }

  FvImage = malloc (FvSize);
  if (FvImage == NULL) {
    printf ("Cannot allocate memory\n");
    fclose (In);
    return -1;
  }

  ByteRead = fread (FvImage, 1, FvSize, In);

  if ((unsigned int) ByteRead != FvSize) {
    printf ("Read File error\n");
    fclose (In);
    return -1;
  }

  cnt = 0;
  while ((unsigned int) ByteStart < FvSize && cnt != -1) {
    cnt = Readfile (FvImage, ByteStart, Invert);

    if (cnt != -1) {
      ByteStart += cnt;
    }

    if (cnt != sizeof (EFI_FFS_FILE_HEADER)) {
      Index++;
    }
  }

  if (cnt == -1) {
    printf ("Firmware Volume image corrupted\n");
    return -1;
  }

  fclose (In);

  Out = fopen (argv[4], "wb");

  if (Out == NULL) {
    printf ("Unable to open FV image file \"%s\"\n", argv[4]);
    return -1;
  }

  In = fopen (argv[1], "rb");

  if (In == NULL) {
    printf ("Unable to open FV image file \"%s\"\n", argv[1]);
    return -1;
  }

  if (gFixup != 0) {

    printf ("Fixup of 0x%X\n", gFixup);

    Status = AsciiStringToUint64 (argv[2], TRUE, &FvOffset);

    gFixup += (UINT32) FvOffset;

    ByteStart = ReadHeader (In, &FvSize);

    Readfile (FvImage, ByteStart, Invert);

    cnt     = 0;
    Status  = AsciiStringToUint64 (argv[3], TRUE, &delta);

    fclose (In);
    In = fopen (argv[1], "rb");

    if (In == NULL) {
      printf ("Unable to open FV image file \"%s\"\n", argv[1]);
      return -1;
    }

    for (Idx = 0; Idx < delta - FvOffset; Idx++) {
      fputc (fgetc (In), Out);
    }

    fwrite (&gFixup, sizeof (UINT32), 1, Out);
    fseek (In, sizeof (UINT32), SEEK_CUR);

    for (Idx = 0; Idx < FvSize - (delta - FvOffset) - sizeof (UINT32); Idx++) {
      fputc (fgetc (In), Out);
    }

    fclose (In);
    fclose (Out);
  } else {
    printf ("There was no fixup to perform\n");
  }

  free (FvImage);

  return 0;
}
