/*++

Copyright (c)  1999 - 2002 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:

    SecFixup.c

Abstract:

    This utility is part of build process for IA32 SEC FFS file.
    
    It fixup the reset vector data. The reset vector data binary file
    will be wrapped as a RAW section and be located immediately after
    the PE/TE section.

    The SEC EXE file can be either PE or TE file.
    
--*/

#include <stdio.h>

#include <Common/UefiBaseTypes.h>
#include <Common/EfiImage.h>
#include <Common/FirmwareVolumeImageFormat.h>

#include "EfiUtilityMsgs.c"
#include "SecFixup.h"

VOID
PrintUtilityInfo (
  VOID
  )
/*++

Routine Description:

  Displays the standard utility information to SDTOUT

Arguments:

  None

Returns:

  None

--*/
{
  printf (
    "%s - Tiano IA32 SEC Fixup 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: %s SecExeFile ResetVectorDataFile OutputFile\n", UTILITY_NAME);
  printf ("  Where:\n");
  printf ("\tSecExeFile           - Name of the IA32 SEC EXE file.\n");
  printf ("\tResetVectorDataFile  - Name of the reset vector data binary file.\n");
  printf ("\tOutputFileName       - Name of the output file.\n\n");
}

STATUS
main (
  IN INTN   argc,
  IN CHAR8  **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.

--*/
{
  FILE    *FpIn;

  FILE    *FpOut;
  UINT32  AddressOfEntryPoint;
  INT32   DestRel;
  STATUS  Status;
  UINT32  SecFileSize;

  SetUtilityName (UTILITY_NAME);

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

  //
  // Verify the correct number of arguments
  //
  if (argc != MAX_ARGS) {
    Error (NULL, 0, 0, "invalid number of input parameters specified", NULL);
    PrintUsage ();
    return STATUS_ERROR;
  }
  //
  // Open the SEC exe file
  //
  if ((FpIn = fopen (argv[1], "rb")) == NULL) {
    Error (NULL, 0, 0, "Unable to open file", argv[1]);
    return STATUS_ERROR;
  }
  //
  // Get the entry point of the EXE file
  //
  Status = GetEntryPoint (FpIn, &AddressOfEntryPoint);
  if (Status != STATUS_SUCCESS) {
    fclose (FpIn);
    return STATUS_ERROR;
  }
  //
  // Get the SEC file size
  //
  fseek (FpIn, 0, SEEK_END);
  SecFileSize = ftell (FpIn);

  //
  // Close the SEC file
  //
  fclose (FpIn);

  //
  // Open the reset vector data file
  //
  if ((FpIn = fopen (argv[2], "rb")) == NULL) {
    Error (NULL, 0, 0, "Unable to open file", argv[2]);
    return STATUS_ERROR;
  }
  //
  // Open the output file
  //
  if ((FpOut = fopen (argv[3], "w+b")) == NULL) {
    Error (NULL, 0, 0, "Unable to open file", argv[3]);
    fclose (FpIn);
    return STATUS_ERROR;
  }
  //
  // Copy the input file to the output file
  //
  if (CopyFile (FpIn, FpOut) != STATUS_SUCCESS) {
    fclose (FpIn);
    fclose (FpOut);
    return STATUS_ERROR;
  }
  //
  // Close the reset vector data file
  //
  fclose (FpIn);

  //
  // Fix the destination relative in the jmp instruction
  // in the reset vector data structure
  //
  fseek (FpOut, -DEST_REL_OFFSET, SEEK_END);
  DestRel = AddressOfEntryPoint - (SecFileSize + sizeof (EFI_COMMON_SECTION_HEADER) + (UINT32) (ftell (FpOut)) + 2);
  if (DestRel <= -65536) {
    Error (NULL, 0, 0, "The SEC EXE file size is too big", NULL);
    fclose (FpOut);
    return STATUS_ERROR;
  }

  if (fwrite (&DestRel, sizeof (UINT16), 1, FpOut) != 1) {
    Error (NULL, 0, 0, "Failed to write to the output file", NULL);
    fclose (FpOut);
    return STATUS_ERROR;
  }
  //
  // Close the output file
  //
  fclose (FpOut);

  return STATUS_SUCCESS;
}

STATUS
GetEntryPoint (
  IN  FILE   *ExeFile,
  OUT UINT32 *EntryPoint
  )
/*++

Routine Description:

  Get the address of the entry point of a PE/TE file.

Arguments:

  PeFile     - File pointer to the specified PE/TE file.
  EntryPoint - Buffer for the address of the entry point to be returned.

Returns:
  STATUS_SUCCESS - Function completed successfully.
  STATUS_ERROR   - Error occured.

--*/
// GC_TODO:    ExeFile - add argument and description to function comment
{
  EFI_IMAGE_DOS_HEADER    DosHeader;
  EFI_IMAGE_NT_HEADERS32  NtHeader;
  EFI_TE_IMAGE_HEADER     TeHeader;

  //
  // Check if it is a TE file
  //
  fseek (ExeFile, 0, SEEK_SET);
  //
  // Attempt to read the TE header
  //
  if (fread (&TeHeader, sizeof (TeHeader), 1, ExeFile) == 1) {
    if (TeHeader.Signature == EFI_TE_IMAGE_HEADER_SIGNATURE) {
      if (TeHeader.Machine != EFI_IMAGE_MACHINE_IA32) {
        Error (NULL, 0, 0, "The SEC file is PE but is not PE32 for IA32", NULL);
        return STATUS_ERROR;
      }

      *EntryPoint = TeHeader.AddressOfEntryPoint + sizeof (EFI_TE_IMAGE_HEADER) - TeHeader.StrippedSize;
      return STATUS_SUCCESS;
    }
  }
  //
  // Check if it is a PE file
  //
  fseek (ExeFile, 0, SEEK_SET);
  //
  // Attempt to read the DOS header
  //
  if (fread (&DosHeader, sizeof (DosHeader), 1, ExeFile) != 1) {
    goto InvalidFile;
  }
  //
  // Check the magic number
  //
  if (DosHeader.e_magic != EFI_IMAGE_DOS_SIGNATURE) {
    goto InvalidFile;
  }
  //
  // Position into the file and read the NT PE header
  //
  fseek (ExeFile, (long) DosHeader.e_lfanew, SEEK_SET);
  if (fread (&NtHeader, sizeof (NtHeader), 1, ExeFile) != 1) {
    goto InvalidFile;
  }
  //
  // Check the PE signature in the header
  //
  if (NtHeader.Signature != EFI_IMAGE_NT_SIGNATURE) {
    goto InvalidFile;
  }
  //
  // Make sure the PE file is PE32 for IA32
  //
  if (NtHeader.FileHeader.Machine != EFI_IMAGE_MACHINE_IA32 ||
      NtHeader.OptionalHeader.Magic != EFI_IMAGE_NT_OPTIONAL_HDR32_MAGIC
      ) {
    Error (NULL, 0, 0, "The SEC file is PE but is not PE32 for IA32", NULL);
    return STATUS_ERROR;
  }
  //
  // Get the entry point from the optional header
  //
  *EntryPoint = NtHeader.OptionalHeader.AddressOfEntryPoint;
  return STATUS_SUCCESS;

InvalidFile:
  Error (NULL, 0, 0, "The SEC file is neither PE nor TE file", NULL);
  return STATUS_ERROR;
}

STATUS
CopyFile (
  FILE    *FpIn,
  FILE    *FpOut
  )
/*++

Routine Description:

  Copy file.

Arguments:

  FpIn  - File pointer to the source file.
  FpOut - File pointer to the destination file.

Returns:
  STATUS_SUCCESS - Function completed successfully.
  STATUS_ERROR   - Error occured.

--*/
{
  INTN  FileSize;

  INTN  Offset;

  INTN  Length;
  UINT8 Buffer[BUF_SIZE];

  fseek (FpIn, 0, SEEK_END);
  FileSize = ftell (FpIn);

  fseek (FpIn, 0, SEEK_SET);
  fseek (FpOut, 0, SEEK_SET);

  Offset = 0;
  while (Offset < FileSize) {
    Length = sizeof (Buffer);
    if (FileSize - Offset < Length) {
      Length = FileSize - Offset;
    }

    if (fread (Buffer, Length, 1, FpIn) != 1 || fwrite (Buffer, Length, 1, FpOut) != 1) {
      Error (NULL, 0, 0, "Copy file error", NULL);
      return STATUS_ERROR;
    }

    Offset += Length;
  }

  return STATUS_SUCCESS;
}
