/*
 * Copyright (c) 2011 - 2022, Intel Corporation.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 *
 *    * Redistributions of source code must retain the above copyright
 *      notice, this list of conditions and the following disclaimer.
 *    * Redistributions in binary form must reproduce the above copyright
 *      notice, this list of conditions and the following disclaimer
 *      in the documentation and/or other materials provided with the
 *      distribution.
 *    * Neither the name of Intel Corporation nor the names of its
 *      contributors may be used to endorse or promote products
 *      derived from this software without specific prior written
 *      permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
 * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
 * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
 * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
 * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
 * OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 * This file contains some wrappers around the gnu-efi functions. As
 * we're not going through uefi_call_wrapper() directly, this allows
 * us to get some type-safety for function call arguments and for the
 * compiler to check that the number of function call arguments is
 * correct.
 *
 * It's also a good place to document the EFI interface.
 */

#include <efi.h>
#include <efilib.h>
#include "stdlib.h"

#define	DOS_FILE_MAGIC_NUMBER	0x5A4D  /* "MZ" */
struct DosFileHeader {
	uint16_t  mMagic;
        uint16_t  LastSize;
        uint16_t  nBlocks;
        uint16_t  nReloc;
        uint16_t  HdrSize;
        uint16_t  MinAlloc;
        uint16_t  MaxAlloc;
        uint16_t  ss;
        uint16_t  sp;
        uint16_t  Checksum;
        uint16_t  ip;
        uint16_t  cs;
        uint16_t  RelocPos;
        uint16_t  nOverlay;
        uint16_t  reserved[4];
        uint16_t  OEMId;
        uint16_t  OEMInfo;
        uint16_t  reserved2[10];
        uint32_t  ExeHeader;
} __attribute__((packed));

#define IMAGE_FILE_MACHINE_I386	0x14c
#define IMAGE_FILE_MACHINE_AMD64    0x8664
#define PE_FILE_MAGIC_NUMBER	0x00004550 	/* "PE\0\0" */
struct PeHeader {
	uint32_t mMagic; 
	uint16_t mMachine;
	uint16_t mNumberOfSections;
	uint32_t mTimeDateStamp;
	uint32_t mPointerToSymbolTable;
	uint32_t mNumberOfSymbols;
	uint16_t mSizeOfOptionalHeader;
	uint16_t mCharacteristics;
} __attribute__((packed));

struct OptionHeader {
	uint16_t Format;
	uint8_t  MajorLinkVer;
	uint8_t  MinorLinkVer;
	uint32_t  CodeSize;
	uint32_t  InitializedDataSize;
	uint32_t  UninitializedDataSize;
	uint32_t  EntryPoint;
	uint32_t  BaseOfCode;
	uint32_t  BaseOfDate;
} __attribute__((packed));


struct PeSectionHeader {
	char  mName[8];
	uint32_t mVirtualSize;
	uint32_t mVirtualAddress;
	uint32_t mSizeOfRawData;
	uint32_t mPointerToRawData;
	uint32_t mPointerToRealocations;
	uint32_t mPointerToLinenumbers;
	uint16_t mNumberOfRealocations;
	uint16_t mNumberOfLinenumbers;
	uint32_t mCharacteristics;
} __attribute__((packed));


EFI_STATUS get_pe_section(CHAR8 *base, char *section_name,
		UINTN section_name_len, UINTN *vaddr, UINTN *size)
{
        struct PeSectionHeader *ph;
        struct DosFileHeader *dh;
        struct PeHeader *pe;
        UINTN i;
        UINTN offset;

        dh = (struct DosFileHeader *)base;

        if (dh->mMagic != DOS_FILE_MAGIC_NUMBER)
                return EFI_LOAD_ERROR;

        pe = (struct PeHeader *)&base[dh->ExeHeader];
		if (pe->mMagic != PE_FILE_MAGIC_NUMBER)
                return EFI_LOAD_ERROR;

        if ((pe->mMachine != IMAGE_FILE_MACHINE_AMD64)
           && (pe->mMachine != IMAGE_FILE_MACHINE_I386))
                return EFI_LOAD_ERROR;

        offset = dh->ExeHeader + sizeof(*pe) + pe->mSizeOfOptionalHeader;

        for (i = 0; i < pe->mNumberOfSections; i++) {
                ph = (struct PeSectionHeader *)&base[offset];
                if (CompareMem(ph->mName, section_name, section_name_len) == 0) {
			*vaddr = (UINTN)ph->mVirtualAddress;
                         *size = (UINTN)ph->mVirtualSize;
			break;
		}

                offset += sizeof(*ph);
        }

        return EFI_SUCCESS;
}


EFI_IMAGE_ENTRY_POINT get_pe_entry(CHAR8 *base)
{
	struct DosFileHeader* dh;
	struct PeHeader* pe;
	struct OptionHeader* oh;
	UINTN offset;

	dh = (struct DosFileHeader *)base;

	if (dh->mMagic != DOS_FILE_MAGIC_NUMBER)
		return NULL;

	pe = (struct PeHeader *)&base[dh->ExeHeader];
	if (pe->mMagic != PE_FILE_MAGIC_NUMBER)
		return NULL;

	if ((pe->mMachine != IMAGE_FILE_MACHINE_AMD64)
		&& (pe->mMachine != IMAGE_FILE_MACHINE_I386))
		return NULL;

    offset = dh->ExeHeader + sizeof(*pe);
	oh = (struct OptionHeader*)&base[offset];

	return (EFI_IMAGE_ENTRY_POINT)((UINT64)base + oh->EntryPoint);
}
