/** @file

Copyright (c) 2010 - 2011, Intel Corporation. All rights reserved.<BR>

This program and the accompanying materials are licensed and made available 
under the terms and conditions of the BSD License which accompanies this 
distribution.  The full text of the license may be found at 
http://opensource.org/licenses/bsd-license.php

THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.

**/

#include "WinNtInclude.h"

#ifndef __GNUC__
#include <windows.h>
#include <io.h>
#endif
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
#include <ctype.h>

#include <Common/UefiBaseTypes.h>
#include <IndustryStandard/PeImage.h>

#include "EfiUtilityMsgs.h"

#include "GenFw.h"
#include "ElfConvert.h"
#include "Elf32Convert.h"
#include "Elf64Convert.h"

//
// Result Coff file in memory.
//
UINT8 *mCoffFile = NULL;

//
// COFF relocation data
//
EFI_IMAGE_BASE_RELOCATION *mCoffBaseRel;
UINT16                    *mCoffEntryRel;

//
// Current offset in coff file.
//
UINT32 mCoffOffset;

//
// Offset in Coff file of headers and sections.
//
UINT32 mTableOffset;

//
//*****************************************************************************
// Common ELF Functions
//*****************************************************************************
//

VOID
CoffAddFixupEntry(
  UINT16 Val
  )
{
  *mCoffEntryRel = Val;
  mCoffEntryRel++;
  mCoffBaseRel->SizeOfBlock += 2;
  mCoffOffset += 2;
}

VOID
CoffAddFixup(
  UINT32 Offset,
  UINT8  Type
  )
{
  if (mCoffBaseRel == NULL
      || mCoffBaseRel->VirtualAddress != (Offset & ~0xfff)) {
    if (mCoffBaseRel != NULL) {
      //
      // Add a null entry (is it required ?)
      //
      CoffAddFixupEntry (0);
      
      //
      // Pad for alignment.
      //
      if (mCoffOffset % 4 != 0)
        CoffAddFixupEntry (0);
    }

    mCoffFile = realloc (
      mCoffFile,
      mCoffOffset + sizeof(EFI_IMAGE_BASE_RELOCATION) + 2*0x1000
      );
    memset (
      mCoffFile + mCoffOffset, 0,
      sizeof(EFI_IMAGE_BASE_RELOCATION) + 2*0x1000
      );

    mCoffBaseRel = (EFI_IMAGE_BASE_RELOCATION*)(mCoffFile + mCoffOffset);
    mCoffBaseRel->VirtualAddress = Offset & ~0xfff;
    mCoffBaseRel->SizeOfBlock = sizeof(EFI_IMAGE_BASE_RELOCATION);

    mCoffEntryRel = (UINT16 *)(mCoffBaseRel + 1);
    mCoffOffset += sizeof(EFI_IMAGE_BASE_RELOCATION);
  }

  //
  // Fill the entry.
  //
  CoffAddFixupEntry((UINT16) ((Type << 12) | (Offset & 0xfff)));
}

VOID
CreateSectionHeader (
  const CHAR8 *Name,
  UINT32      Offset,
  UINT32      Size,
  UINT32      Flags
  )
{
  EFI_IMAGE_SECTION_HEADER *Hdr;
  Hdr = (EFI_IMAGE_SECTION_HEADER*)(mCoffFile + mTableOffset);

  strcpy((char *)Hdr->Name, Name);
  Hdr->Misc.VirtualSize = Size;
  Hdr->VirtualAddress = Offset;
  Hdr->SizeOfRawData = Size;
  Hdr->PointerToRawData = Offset;
  Hdr->PointerToRelocations = 0;
  Hdr->PointerToLinenumbers = 0;
  Hdr->NumberOfRelocations = 0;
  Hdr->NumberOfLinenumbers = 0;
  Hdr->Characteristics = Flags;

  mTableOffset += sizeof (EFI_IMAGE_SECTION_HEADER);
}

//
//*****************************************************************************
// Functions called from GenFw main code.
//*****************************************************************************
//

INTN
IsElfHeader (
  UINT8  *FileBuffer
)
{
  return (FileBuffer[EI_MAG0] == ELFMAG0 && 
          FileBuffer[EI_MAG1] == ELFMAG1 &&
          FileBuffer[EI_MAG2] == ELFMAG2 &&
          FileBuffer[EI_MAG3] == ELFMAG3);
}

BOOLEAN
ConvertElf (
  UINT8  **FileBuffer,
  UINT32 *FileLength
  )
{
  ELF_FUNCTION_TABLE              ElfFunctions;
  UINT8                           EiClass;

  //
  // Determine ELF type and set function table pointer correctly.
  //
  VerboseMsg ("Check Elf Image Header");
  EiClass = (*FileBuffer)[EI_CLASS];
  if (EiClass == ELFCLASS32) {
    if (!InitializeElf32 (*FileBuffer, &ElfFunctions)) {
      return FALSE;
    }
  } else if (EiClass == ELFCLASS64) {
    if (!InitializeElf64 (*FileBuffer, &ElfFunctions)) {
      return FALSE;
    }
  } else {
    Error (NULL, 0, 3000, "Unsupported", "ELF EI_CLASS not supported.");
    return FALSE;
  }

  //
  // Compute sections new address.
  //  
  VerboseMsg ("Compute sections new address.");
  ElfFunctions.ScanSections ();

  //
  // Write and relocate sections.
  //
  VerboseMsg ("Write and relocate sections.");
  ElfFunctions.WriteSections (SECTION_TEXT);
  ElfFunctions.WriteSections (SECTION_DATA);
  ElfFunctions.WriteSections (SECTION_HII);

  //
  // Translate and write relocations.
  //
  VerboseMsg ("Translate and write relocations.");
  ElfFunctions.WriteRelocations ();

  //
  // Write debug info.
  //
  VerboseMsg ("Write debug info.");
  ElfFunctions.WriteDebug ();

  //
  // Make sure image size is correct before returning the new image.
  //
  VerboseMsg ("Set image size.");
  ElfFunctions.SetImageSize ();

  //
  // Replace.
  //
  free (*FileBuffer);
  *FileBuffer = mCoffFile;
  *FileLength = mCoffOffset;

  //
  // Free resources used by ELF functions.
  //
  ElfFunctions.CleanUp ();
  
  return TRUE;
}
