/**

  UEFI driver for enabling loading of OSX by using memory relocation.

  by dmazar

**/

#include <Library/UefiLib.h>
#include <Library/UefiBootServicesTableLib.h>
#include <Library/UefiRuntimeServicesTableLib.h>
#include <Library/BaseMemoryLib.h>
#include <Library/MemoryAllocationLib.h>
#include <Library/DebugLib.h>
#include <Library/CpuLib.h>

#include <Guid/GlobalVariable.h>

#include <Protocol/LoadedImage.h>

#include "BootFixes.h"
#include "DecodedKernelCheck.h"
#include "BootArgs.h"
#include "AsmFuncs.h"
#include "VMem.h"
#include "Lib.h"
#include "Hibernate.h"
#include "NVRAMDebug.h"


// DBG_TO: 0=no debug, 1=serial, 2=console
// serial requires
// [PcdsFixedAtBuild]
//  gEfiMdePkgTokenSpaceGuid.PcdDebugPropertyMask|0x07
//  gEfiMdePkgTokenSpaceGuid.PcdDebugPrintErrorLevel|0xFFFFFFFF
// in package DSC file
#define DBG_TO 0

#if DBG_TO == 2
	#define DBG(...) AsciiPrint(__VA_ARGS__);
#elif DBG_TO == 1
	#define DBG(...) DebugPrint(1, __VA_ARGS__);
#else
	#define DBG(...)
#endif

#include "../Version.h"
CONST CHAR8* CloverRevision = REVISION_STR;
STATIC UINTN Counter = 0;


// defines the size of block that will be allocated for kernel image relocation,
//   without RT and MMIO regions
// rehabman - Increase the size for ElCapitan to 128Mb 0x8000
// stinga11 - 0x6000
#define KERNEL_BLOCK_NO_RT_SIZE_PAGES	0x8000

// TRUE if we are doing hibernate wake
BOOLEAN gHibernateWake = FALSE;


// placeholders for storing original Boot Services functions
EFI_ALLOCATE_PAGES 			gStoredAllocatePages = NULL;
EFI_GET_MEMORY_MAP 			gStoredGetMemoryMap = NULL;
EFI_EXIT_BOOT_SERVICES 		gStoredExitBootServices = NULL;
EFI_IMAGE_START 			gStartImage = NULL;
EFI_HANDLE_PROTOCOL			gHandleProtocol = NULL;


// monitoring AlocatePages
EFI_PHYSICAL_ADDRESS gMinAllocatedAddr = 0;
EFI_PHYSICAL_ADDRESS gMaxAllocatedAddr = 0;

// relocation base address
EFI_PHYSICAL_ADDRESS gRelocBase = 0;
// relocation block size in pages
UINTN gRelocSizePages = 0;

// location of memory allocated by boot.efi for hibernate image
EFI_PHYSICAL_ADDRESS gHibernateImageAddress = 0;

// last memory map obtained by boot.efi
UINTN					gLastMemoryMapSize = 0;
EFI_MEMORY_DESCRIPTOR	*gLastMemoryMap = NULL;
UINTN					gLastDescriptorSize = 0;
UINT32					gLastDescriptorVersion = 0;

/** Helper function that calls GetMemoryMap() and returns new MapKey.
 * Uses gStoredGetMemoryMap, so can be called only after gStoredGetMemoryMap is set.
 */
EFI_STATUS
GetMemoryMapKey(OUT UINTN *MapKey)
{
	EFI_STATUS					Status;
	UINTN						MemoryMapSize;
	EFI_MEMORY_DESCRIPTOR		*MemoryMap;
	UINTN						DescriptorSize;
	UINT32						DescriptorVersion;
	
	Status = GetMemoryMapAlloc(gStoredGetMemoryMap, &MemoryMapSize, &MemoryMap, MapKey, &DescriptorSize, &DescriptorVersion);
	return Status;
}

/** Helper function that calculates number of RT and MMIO pages from mem map. */
EFI_STATUS
GetNumberOfRTPages(OUT UINTN *NumPages)
{
	EFI_STATUS					Status;
	UINTN						MemoryMapSize;
	EFI_MEMORY_DESCRIPTOR		*MemoryMap;
	UINTN						MapKey;
	UINTN						DescriptorSize;
	UINT32						DescriptorVersion;
	UINTN						NumEntries;
	UINTN						Index;
	EFI_MEMORY_DESCRIPTOR		*Desc;
	
	Status = GetMemoryMapAlloc(gBS->GetMemoryMap, &MemoryMapSize, &MemoryMap, &MapKey, &DescriptorSize, &DescriptorVersion);
	if (EFI_ERROR(Status)) {
		return Status;
	}
	
	//
	// Apply some fixes
	//
	FixMemMap(MemoryMapSize, MemoryMap, DescriptorSize, DescriptorVersion);
	
	//
	// Sum RT and MMIO areas - all that have runtime attribute
	//
	
	*NumPages = 0;
	Desc = MemoryMap;
	NumEntries = MemoryMapSize / DescriptorSize;
	
	for (Index = 0; Index < NumEntries; Index++) {
		if ((Desc->Attribute & EFI_MEMORY_RUNTIME) != 0) {
			*NumPages += Desc->NumberOfPages;
		}
		Desc = NEXT_MEMORY_DESCRIPTOR(Desc, DescriptorSize);
	}

	return Status;
}


/** Calculate the size of reloc block.
  * gRelocSizePages = KERNEL_BLOCK_NO_RT_SIZE_PAGES + RT&MMIO pages
  */
EFI_STATUS
CalculateRelocBlockSize(VOID)
{
	EFI_STATUS				Status;
	UINTN					NumPagesRT;
	
	
	// Sum pages needed for RT and MMIO areas
	Status = GetNumberOfRTPages(&NumPagesRT);
	if (EFI_ERROR(Status)) {
		DBGnvr("GetNumberOfRTPages: %r\n", Status);
		DBG("OsxAptioFixDrv: CalculateRelocBlockSize(): GetNumberOfRTPages: %r\n", Status);
		Print(L"OsxAptioFixDrv: CalculateRelocBlockSize(): GetNumberOfRTPages: %r\n", Status);
		return Status;
	}
	
	gRelocSizePages = KERNEL_BLOCK_NO_RT_SIZE_PAGES + NumPagesRT;
	DBGnvr("Reloc block: %x pages (%d MB) = kernel %x (%d MB) + RT&MMIO %x (%d MB)\n",
		   gRelocSizePages, EFI_PAGES_TO_SIZE(gRelocSizePages) >> 20,
		   KERNEL_BLOCK_NO_RT_SIZE_PAGES, EFI_PAGES_TO_SIZE(KERNEL_BLOCK_NO_RT_SIZE_PAGES) >> 20,
		   NumPagesRT, EFI_PAGES_TO_SIZE(NumPagesRT) >> 20
		   );
	
	return Status;
}

/** Allocate free block on top of mem for kernel image relocation (will be returned to boot.efi for kernel boot image). */
EFI_STATUS
AllocateRelocBlock()
{
	EFI_STATUS				Status;
	EFI_PHYSICAL_ADDRESS	Addr;
	
	
	// calculate the needed size for reloc block
	CalculateRelocBlockSize();
	
	gRelocBase = 0;
	Addr = 0x100000000; // max address
	Status = AllocatePagesFromTop(EfiBootServicesData, gRelocSizePages, &Addr);
	if (Status != EFI_SUCCESS) {
		DBG("OsxAptioFixDrv: AllocateRelocBlock(): can not allocate relocation block (0x%x pages below 0x%lx): %r\n",
			gRelocSizePages, Addr, Status);
		Print(L"OsxAptioFixDrv: AllocateRelocBlock(): can not allocate relocation block (0x%x pages below 0x%lx): %r\n",
			gRelocSizePages, Addr, Status);
	} else {
		gRelocBase = Addr;
		DBG("OsxAptioFixDrv: AllocateRelocBlock(): gRelocBase set to %lx - %lx\n", gRelocBase, gRelocBase + EFI_PAGES_TO_SIZE(gRelocSizePages) - 1);
		DBGnvr("gRelocBase set to %lx - %lx\n", gRelocBase, gRelocBase + EFI_PAGES_TO_SIZE(gRelocSizePages) - 1);
	}

	// set reloc addr in runtime vars for boot manager
	//Print(L"OsxAptioFixDrv: AllocateRelocBlock(): gRelocBase set to %lx - %lx\n", gRelocBase, gRelocBase + EFI_PAGES_TO_SIZE(gRelocSizePages) - 1);
	/*Status = */gRT->SetVariable(L"OsxAptioFixDrv-RelocBase", &gEfiAppleBootGuid, 
							  /*   EFI_VARIABLE_NON_VOLATILE |*/ EFI_VARIABLE_BOOTSERVICE_ACCESS | EFI_VARIABLE_RUNTIME_ACCESS,
							  sizeof(gRelocBase) ,&gRelocBase);
	return Status;
}

/** Releases relocation block. */
EFI_STATUS
FreeRelocBlock()
{
	
	return gBS->FreePages(gRelocBase, gRelocSizePages);
}



/** gBS->HandleProtocol override:
  * Boot.efi requires EfiGraphicsOutputProtocol on ConOutHandle, but it is not present
  * there on Aptio 2.0. EfiGraphicsOutputProtocol exists on some other handle.
  * If this is the case, we'll intercept that call and return EfiGraphicsOutputProtocol
  * from that other handle.
  */
EFI_STATUS EFIAPI
MOHandleProtocol(
	IN EFI_HANDLE		Handle,
	IN EFI_GUID			*Protocol,
	OUT VOID			**Interface
)
{
	EFI_STATUS			res;
	EFI_GRAPHICS_OUTPUT_PROTOCOL	*GraphicsOutput;
	
	// special handling if gEfiGraphicsOutputProtocolGuid is requested by boot.efi
	if (CompareGuid(Protocol, &gEfiGraphicsOutputProtocolGuid)) {
		res = gHandleProtocol(Handle, Protocol, Interface);
		if (res != EFI_SUCCESS) {
			// let's find it on some other handle
			res = gBS->LocateProtocol(&gEfiGraphicsOutputProtocolGuid, NULL, (VOID**)&GraphicsOutput);
			if (res == EFI_SUCCESS) {
				// return it
				*Interface = GraphicsOutput;
	//			DBG("->HandleProtocol(%p, %s, %p) = %r (returning from other handle)\n", Handle, GuidStr(Protocol), *Interface, res);
				DBGnvr("->HandleProtocol(%p, %s, %p) = %r (from other handle)\n", Handle, GuidStr(Protocol), *Interface, res);
				return res;
			}
		}
		DBGnvr("->HandleProtocol(%p, %s, %p) = %r\n", Handle, GuidStr(Protocol), *Interface, res);
	} else {
		res = gHandleProtocol(Handle, Protocol, Interface);
	}
//	DBG("->HandleProtocol(%p, %s, %p) = %r\n", Handle, GuidStr(Protocol), *Interface, res);
	return res;
}

/** gBS->AllocatePages override:
  * Returns pages from free memory block to boot.efi for kernel boot image.
  */
EFI_STATUS
EFIAPI
MOAllocatePages (
	IN EFI_ALLOCATE_TYPE		Type,
	IN EFI_MEMORY_TYPE			MemoryType,
	IN UINTN					NumberOfPages,
	IN OUT EFI_PHYSICAL_ADDRESS	*Memory
	)
{
	EFI_STATUS					Status;
	EFI_PHYSICAL_ADDRESS		UpperAddr;
//	EFI_PHYSICAL_ADDRESS		MemoryIn;
//	BOOLEAN						FromRelocBlock = FALSE;
	

//	MemoryIn = *Memory;
	
	if (Type == AllocateAddress && MemoryType == EfiLoaderData) {
		// called from boot.efi
		
		UpperAddr = *Memory + EFI_PAGES_TO_SIZE(NumberOfPages);
		
		// check if the requested mem can be served from reloc block
    // the upper address is compared to the size of the relocation block to achieve Address + gRelocBase for all
    // allocations, so that the entire block can be copied to the proper location on kernel entry
    // Comparing only the number of pages will not only give wrong results as gRelocSizePages is not decreased,
    // but also implies memory is 'stacked', which it is not.
    if (UpperAddr >= EFI_PAGES_TO_SIZE(gRelocSizePages)) {
			// no - exceeds our block - signal error
			Print(L"OsxAptioFixDrv: Error - requested memory exceeds our allocated relocation block\n");
			Print(L"Requested mem: %lx - %lx, Pages: %x, Size: %lx\n",
				  *Memory, UpperAddr - 1,
				  NumberOfPages, EFI_PAGES_TO_SIZE(NumberOfPages)
				  );
			Print(L"Reloc block: %lx - %lx, Pages: %x, Size: %lx\n",
				  gRelocBase, gRelocBase + EFI_PAGES_TO_SIZE(gRelocSizePages) - 1,
				  gRelocSizePages, EFI_PAGES_TO_SIZE(gRelocSizePages)
				  );
			Print(L"Reloc block can handle mem requests: %lx - %lx\n",
				  0, EFI_PAGES_TO_SIZE(gRelocSizePages) - 1
				  );
			Print(L"Exiting in 30 secs ...\n");
			gBS->Stall(30 * 1000000);
			
			return EFI_OUT_OF_RESOURCES;
		}
		
		// store min and max mem - can be used later to determine start and end of kernel boot image
		if (gMinAllocatedAddr == 0 || *Memory < gMinAllocatedAddr) gMinAllocatedAddr = *Memory;
		if (UpperAddr > gMaxAllocatedAddr) gMaxAllocatedAddr = UpperAddr;
		
		// give it from our allocated block
		*Memory += gRelocBase;
		//Status = gStoredAllocatePages(Type, MemoryType, NumberOfPages, Memory);
//		FromRelocBlock = TRUE;
		Status = EFI_SUCCESS;
		
	} else {
		// default page allocation
		Status = gStoredAllocatePages(Type, MemoryType, NumberOfPages, Memory);
	}

	//DBG("AllocatePages(%s, %s, %x, %lx/%lx) = %r %c\n",
	//	EfiAllocateTypeDesc[Type], EfiMemoryTypeDesc[MemoryType], NumberOfPages, MemoryIn, *Memory, Status, FromRelocBlock ? L'+' : L' ');
	return Status;
}


/** gBS->GetMemoryMap override:
  * Returns shrinked memory map. I think kernel can handle up to 128 entries.
  */
EFI_STATUS
EFIAPI
MOGetMemoryMap (
	IN OUT UINTN					*MemoryMapSize,
	IN OUT EFI_MEMORY_DESCRIPTOR	*MemoryMap,
	OUT UINTN						*MapKey,
	OUT UINTN						*DescriptorSize,
	OUT UINT32						*DescriptorVersion
	)
{
	EFI_STATUS						Status;

	Status = gStoredGetMemoryMap(MemoryMapSize, MemoryMap, MapKey, DescriptorSize, DescriptorVersion);
	//PrintMemMap(*MemoryMapSize, MemoryMap, *DescriptorSize, *DescriptorVersion);
	DBGnvr("GetMemoryMap: %p = %r\n", MemoryMap, Status);
	if (Status == EFI_SUCCESS) {
		FixMemMap(*MemoryMapSize, MemoryMap, *DescriptorSize, *DescriptorVersion);
		//ShrinkMemMap(MemoryMapSize, MemoryMap, *DescriptorSize, *DescriptorVersion);
		//PrintMemMap(*MemoryMapSize, MemoryMap, *DescriptorSize, *DescriptorVersion);
		
		// remember last/final memmap
		gLastMemoryMapSize = *MemoryMapSize;
		gLastMemoryMap = MemoryMap;
		gLastDescriptorSize = *DescriptorSize;
		gLastDescriptorVersion = *DescriptorVersion;
	}
	return Status;
}

/** gBS->ExitBootServices override:
  * Patches kernel entry point with jump to our KernelEntryPatchJumpBack().
  */
EFI_STATUS
EFIAPI
MOExitBootServices (
	IN EFI_HANDLE				ImageHandle,
	IN UINTN					MapKey
	)
{
	EFI_STATUS					Status;
	UINTN					 	NewMapKey;
	UINTN						SlideAddr = 0;
	VOID						*MachOImage = NULL;
	
	// for  tests: we can just return EFI_SUCCESS and continue using Print for debug.
//	Status = EFI_SUCCESS;
	//Print(L"ExitBootServices()\n");
	Status = gStoredExitBootServices(ImageHandle, MapKey);
	DBGnvr("ExitBootServices:  = %r\n", Status);
	if (EFI_ERROR (Status)) {
		// just report error as var in nvram to be visible from OSX with "nvrap -p"
		gRT->SetVariable(L"OsxAptioFixDrv-ErrorExitingBootServices",
						 &gEfiAppleBootGuid,
						 EFI_VARIABLE_BOOTSERVICE_ACCESS | EFI_VARIABLE_RUNTIME_ACCESS,
						 3,
						 "Yes"
						 );
		
		Status = GetMemoryMapKey(&NewMapKey);
		DBGnvr("ExitBootServices: GetMemoryMapKey = %r\n", Status);
		if (Status == EFI_SUCCESS) {
			// we have latest mem map and NewMapKey
			// we'll try again ExitBootServices with NewMapKey
			Status = gStoredExitBootServices(ImageHandle, NewMapKey);
			DBGnvr("ExitBootServices: 2nd try = %r\n", Status);
			if (EFI_ERROR (Status)) {
				// Error!
				Print(L"OsxAptioFixDrv: Error ExitBootServices() 2nd try = Status: %r\n", Status);
			}
		} else {
			Print(L"OsxAptioFixDrv: Error ExitBootServices(), GetMemoryMapKey() = Status: %r\n", Status);
			Status = EFI_INVALID_PARAMETER;
		}
		
	}
	
    if (EFI_ERROR(Status)) {
		Print(L"... waiting 10 secs ...\n");
		gBS->Stall(10*1000000);
        return Status;
    }
	
	DBG("ExitBootServices: gMinAllocatedAddr: %lx, gMaxAllocatedAddr: %lx\n", gMinAllocatedAddr, gMaxAllocatedAddr);
	MachOImage = (VOID*)(UINTN)(gRelocBase + 0x200000);
	KernelEntryFromMachOPatchJump(MachOImage, SlideAddr);
	
	return Status;
}


/** Callback called when boot.efi jumps to kernel. */
UINTN
EFIAPI
KernelEntryPatchJumpBack(UINTN bootArgs, BOOLEAN ModeX64)
{
	
	DBGnvr("\nBACK FROM KERNEL: BootArgs = %x, KernelEntry: %x, Kernel called in %s bit mode\n", bootArgs, AsmKernelEntry, (ModeX64 ? L"64" : L"32"));
	
	bootArgs = FixBootingWithRelocBlock(bootArgs, ModeX64);
	
	DBGnvr("BACK TO KERNEL: BootArgs = %x, KImgStartReloc = %x, KImgStart = %x, KImgSize = %x\n",
		   bootArgs, AsmKernelImageStartReloc, AsmKernelImageStart, AsmKernelImageSize);
	
	// debug for jumping back to kernel
	// put HLT to kernel entry point to stop there
	//SetMem((VOID*)(UINTN)(AsmKernelEntry + gRelocBase), 1, 0xF4);
	// put 0 to kernel entry point to restart
	//SetMem64((VOID*)(UINTN)(AsmKernelEntry + gRelocBase), 1, 0);
	
	return bootArgs;
}



/** SWITCH_STACK_ENTRY_POINT implementation:
  * Allocates kernel image reloc block, installs UEFI overrides and starts given image.
  * If image returns, then deinstalls overrides and releases kernel image reloc block.
  *
  * If started with ImgContext->JumpBuffer, then it will return with LongJump().
  */
EFI_STATUS
RunImageWithOverrides(IN EFI_HANDLE ImageHandle, OUT UINTN *ExitDataSize, OUT CHAR16 **ExitData  OPTIONAL)
{
	EFI_STATUS					Status;
	
	// save current 64bit state - will be restored later in callback from kernel jump
	// and relocate MyAsmCopyAndJumpToKernel32 code to higher mem (for copying kernel back to
	// proper place and jumping back to it)
	Status = PrepareJumpFromKernel();
	if (EFI_ERROR(Status)) {
		return Status;
	}
	
	// init VMem memory pool - will be used after ExitBootServices
	Status = VmAllocateMemoryPool();
	if (EFI_ERROR(Status)) {
		return Status;
	}
	
	// allocate block for kernel image relocation
	Status = AllocateRelocBlock();
	if (EFI_ERROR(Status)) {
		return Status;
	}
	
	// clear monitoring vars
	gMinAllocatedAddr = 0;
	gMaxAllocatedAddr = 0;
	
	// save original BS functions
	gStoredAllocatePages = gBS->AllocatePages;
	gStoredGetMemoryMap = gBS->GetMemoryMap;
	gStoredExitBootServices = gBS->ExitBootServices;
	gHandleProtocol = gBS->HandleProtocol;
	
	// install our overrides
	gBS->AllocatePages = MOAllocatePages;
	gBS->GetMemoryMap = MOGetMemoryMap;
	gBS->ExitBootServices = MOExitBootServices;
	gBS->HandleProtocol = MOHandleProtocol;

	gBS->Hdr.CRC32 = 0;
	gBS->CalculateCrc32(gBS, gBS->Hdr.HeaderSize, &gBS->Hdr.CRC32);
	
	// run image
	Status = gStartImage(ImageHandle, ExitDataSize, ExitData);
	
	// if we get here then boot.efi did not start kernel
	// and we'll try to do some cleanup ...
	
	// return back originals
	gBS->AllocatePages = gStoredAllocatePages;
	gBS->GetMemoryMap = gStoredGetMemoryMap;
	gBS->ExitBootServices = gStoredExitBootServices;
	gBS->HandleProtocol = gHandleProtocol;

	gBS->Hdr.CRC32 = 0;
	gBS->CalculateCrc32(gBS, gBS->Hdr.HeaderSize, &gBS->Hdr.CRC32);
	
	// release reloc block
	FreeRelocBlock();
	
	return Status;
}

/** gBS->StartImage override:
  * Called to start an efi image.
  *
  * If this is boot.efi, then run it with our overrides.
  */
EFI_STATUS
EFIAPI
MOStartImage (
	IN EFI_HANDLE			ImageHandle,
	OUT UINTN					*ExitDataSize,
	OUT CHAR16				**ExitData  OPTIONAL
	)
{
	EFI_STATUS				Status;
	EFI_LOADED_IMAGE_PROTOCOL	*Image;
	CHAR16						*FilePathText = NULL;
	UINTN						Size = 0;
  	VOID                        *Value        = NULL;
  	UINTN                       Size2         = 0;
  	CHAR16                      *StartFlag    = NULL;	
	
	DBG("StartImage(%lx)\n", ImageHandle);

	// find out image name from EfiLoadedImageProtocol
	Status = gBS->OpenProtocol(ImageHandle, &gEfiLoadedImageProtocolGuid, (VOID **) &Image, gImageHandle, NULL, EFI_OPEN_PROTOCOL_GET_PROTOCOL);
	if (Status != EFI_SUCCESS) {
		DBG("ERROR: MOStartImage: OpenProtocol(gEfiLoadedImageProtocolGuid) = %r\n", Status);
		return EFI_INVALID_PARAMETER;
	}
	FilePathText = FileDevicePathToText(Image->FilePath);
	if (FilePathText != NULL) {
		DBG("FilePath: %s\n", FilePathText);
	}
	DBG("ImageBase: %p - %lx (%lx)\n", Image->ImageBase, (UINT64)Image->ImageBase + Image->ImageSize, Image->ImageSize);
	Status = gBS->CloseProtocol(ImageHandle, &gEfiLoadedImageProtocolGuid, gImageHandle, NULL);
	if (EFI_ERROR(Status)) {
		DBG("CloseProtocol error: %r\n", Status);
	}

	//the presence of the variable means HibernateWake
	//if the wake is canceled then the variable must be deleted
	Status = gRT->GetVariable(L"boot-switch-vars", &gEfiAppleBootGuid, NULL, &Size, NULL);
	gHibernateWake = (Status == EFI_BUFFER_TOO_SMALL);
	
    if (StrStriBasic(FilePathText,L"boot.efi") /*|| StrStriBasic(FilePathText,L"booter")*/) {
      Status = GetVariable2 (L"aptiofixflag", &gEfiAppleBootGuid, &Value, &Size2);
      if (!EFI_ERROR(Status)) {
        Status = gRT->SetVariable(L"recovery-boot-mode", &gEfiAppleBootGuid,
                                  EFI_VARIABLE_NON_VOLATILE | EFI_VARIABLE_BOOTSERVICE_ACCESS | EFI_VARIABLE_RUNTIME_ACCESS,
                                  Size2, Value);
        if (EFI_ERROR(Status)) {
          DBG(" Something goes wrong while setting recovery-boot-mode\n");
        }
        Status = gRT->SetVariable (L"aptiofixflag", &gEfiAppleBootGuid, 0, 0, NULL);
        FreePool(Value);
      }
    
      Size2 =0;
      //Check recovery-boot-mode present for nested boot.efi
      Status = GetVariable2 (L"recovery-boot-mode", &gEfiAppleBootGuid, &Value, &Size2);
      if (!EFI_ERROR(Status)) {
        //If it presents, then wait for \com.apple.recovery.boot\boot.efi boot
        DBG(" recovery-boot-mode present\n");
        StartFlag = StrStriBasic(FilePathText,L"\\com.apple.recovery.boot\\boot.efi");
        if (Counter > 0x00){
          Status = gRT->SetVariable(L"aptiofixflag", &gEfiAppleBootGuid,
                                    EFI_VARIABLE_NON_VOLATILE | EFI_VARIABLE_BOOTSERVICE_ACCESS | EFI_VARIABLE_RUNTIME_ACCESS,
                                    Size2, Value);
          if (EFI_ERROR(Status)) {
            DBG("Something goes wrong! \n");
          }
          gRT->ResetSystem(EfiResetWarm, EFI_SUCCESS, 0, NULL);
        }
      } else {
        StartFlag = StrStriBasic(FilePathText,L"boot.efi");
        /*if (!StartFlag) {
          StartFlag = StrStriBasic(FilePathText,L"booter") ;
        }*/
      }
      FreePool(Value);
    }

	// check if this is boot.efi
	if (StartFlag && !gHibernateWake) {
		Counter++;
		Print(L"OsxAptioFixDrv: Starting overrides for %s\nUsing reloc block: yes, hibernate wake: %s \n",
			  FilePathText, gHibernateWake ? L"yes" : L"no");
		//gBS->Stall(2000000);

		// run with our overrides
		Status = RunImageWithOverrides(ImageHandle, ExitDataSize, ExitData);
		
	} else {
		// call original function to do the job
		Status = gStartImage(ImageHandle, ExitDataSize, ExitData);
	}
	
	if (FilePathText != NULL) {
		gBS->FreePool(FilePathText);
	}
	return Status;
}


/** Entry point. Installs our StartImage override.
  * All other stuff will be installed from there when boot.efi is started.
  */
EFI_STATUS
EFIAPI
OsxAptioFixDrvEntrypoint (
	IN EFI_HANDLE				ImageHandle,
	IN EFI_SYSTEM_TABLE			*SystemTable
	)
{
	// install StartImage override
	// all other overrides will be started when boot.efi is started
	gStartImage = gBS->StartImage;
	gBS->StartImage = MOStartImage;
	gBS->Hdr.CRC32 = 0;
	gBS->CalculateCrc32(gBS, gBS->Hdr.HeaderSize, &gBS->Hdr.CRC32);
	
	return EFI_SUCCESS;
}

