/*
sets up all the needed data structures 
copies dbvm into physical memory
jumps into dbvm's os entry point

*/
#pragma warning( disable: 4100 4103 4152 4189 4456)

#ifndef AMD64
#pragma warning( disable: 4740)
#endif

#include <ntifs.h>
#include <windef.h>

#include "dbkfunc.h"
#include "vmxoffload.h"
#include "vmxhelper.h"

#ifdef TOBESIGNED
#include "sigcheck.h"
#endif

unsigned char *vmm;

#pragma pack(2) 
struct
{
	WORD limit;
	UINT_PTR base;
} NewGDTDescriptor;
#pragma pack()

#pragma pack(1) 
typedef struct _INITVARS
{
	UINT64 loadedOS; //physical address of the loadedOS section
	UINT64 vmmstart; //physical address of virtual address 00400000 (obsoletish...)
	UINT64 pagedirlvl4; //Virtual address of the pml4 table (the virtual memory after this until the next 4MB alignment is free to use)
	UINT64 nextstack; //The virtual address of the stack for the next CPU (vmloader only sets it up when 0)
	UINT64 extramemory; //Physical address of some extra initial memory (physically contiguous)
	UINT64 extramemorysize; //the number of pages that extramemory spans
	UINT64 contiguousmemory; //Physical address of some extra initial memory (physically contiguous)
	UINT64 contiguousmemorysize; //the number of pages that extramemory spans
} INITVARS, *PINITVARS;


typedef struct
{ //ok, everything uint64, I hate these incompatibilities with alignment between gcc and ms c
	UINT64		cpucount;
	UINT64		originalEFER;
	UINT64		originalLME;
	UINT64		idtbase;
	UINT64		idtlimit;
	UINT64		gdtbase;
	UINT64		gdtlimit;
	UINT64		cr0;
	UINT64		cr2;
	UINT64		cr3;
	UINT64		cr4;
	UINT64		dr7;
	UINT64		rip;

	UINT64		rax;
	UINT64		rbx;
	UINT64		rcx;
	UINT64		rdx;
	UINT64		rsi;
	UINT64		rdi;
	UINT64		rbp;
	UINT64		rsp;
	UINT64		r8;
	UINT64		r9;
	UINT64		r10;
	UINT64		r11;
	UINT64		r12;
	UINT64		r13;
	UINT64		r14;
	UINT64		r15;

	UINT64		rflags;
	UINT64		cs;
	UINT64		ss;
	UINT64		ds;
	UINT64		es;
	UINT64		fs;
	UINT64		gs;
	UINT64		tr;
	UINT64		ldt;

	UINT64		cs_AccessRights;
	UINT64		ss_AccessRights;
	UINT64		ds_AccessRights;
	UINT64		es_AccessRights;
	UINT64		fs_AccessRights;
	UINT64		gs_AccessRights;

	UINT64		cs_Limit;
	UINT64		ss_Limit;
	UINT64		ds_Limit;
	UINT64		es_Limit;
	UINT64		fs_Limit;
	UINT64		gs_Limit;
	
	UINT64		fsbase;
	UINT64		gsbase;

} OriginalState, *POriginalState;
#pragma pack() 

unsigned char *enterVMM2;
PMDL enterVMM2MDL;
POriginalState originalstate; //one of the reasons why multiple cpu's don't start at exactly the same time
PMDL originalstateMDL;

UINT_PTR enterVMM2PA;

PVOID TemporaryPagingSetup;
UINT_PTR TemporaryPagingSetupPA;
PMDL TemporaryPagingSetupMDL;

UINT_PTR DBVMPML4PA;
UINT_PTR originalstatePA;
UINT_PTR NewGDTDescriptorVA;
UINT_PTR vmmPA;

int initializedvmm=0;

KSPIN_LOCK LoadedOSSpinLock; //spinlock to prevent LoadedOS from being overwritten (should not be needed, but just being safe)

#ifdef AMD64
extern void enterVMM( void ); //declared in vmxoffloada.asm
extern void enterVMMPrologue(void);
extern void enterVMMEpilogue(void);
extern void JTAGBP(void);
#else
_declspec( naked ) void enterVMM( void )
{

	__asm
	{
begin:
		xchg bx,bx //trigger bochs breakpoint

		//setup the GDT
		lgdt [ebx] //ebx is the 'virtual address' so just do that before disabling paging ok...

		//switch to identify mapped pagetable
		mov cr3,edx
		jmp short weee
weee:
		


		//now jump to the physical address (identity mapped to the same virtual address)
		mov eax,secondentry
	    sub eax,begin
		add eax,esi
		jmp eax

secondentry:

		

		//disable paging		
		mov eax,cr0
		and eax,0x7FFFFFFF
		mov cr0,eax
		//paging off
		jmp short weee2
weee2:

		

		//load paging for vmm (but don't apply yet, in nonpaged mode)
		mov cr3,ecx

		//enable PAE and PSE
		mov eax,0x30
		__emit 0x0f  //-|
		__emit 0x22  //-|-mov cr4,eax  (still WTF's me that visual studio doesn't know about cr4)
		__emit 0xe0  //-|


		mov ecx,0xc0000080 //enable efer_lme
		rdmsr
		or eax,0x100
		wrmsr

		//mov eax,cr0		
		//or eax,0x80000020 //re-enable pg (and ne to be sure)
		//edit, who cares, fuck the original state, it's my own state now
		mov eax,0x80000021
		mov cr0,eax

		mov eax,edi //tell dbvm it's an OS entry and a that location the start info is
		mov ebx,ebp //tell vmmPA

		__emit 0xea  //-|
		__emit 0x00  //-|
		__emit 0x00  //-|
		__emit 0x40  //-|JMP FAR 0x50:0x00400000
		__emit 0x00  //-|
		__emit 0x50  //-|
		__emit 0x00  //-|

		__emit 0xce
		__emit 0xce
		__emit 0xce
		__emit 0xce
		__emit 0xce
		__emit 0xce
		__emit 0xce

	}
}
#endif

PMDL DBVMMDL;

PINITVARS initvars;

void cleanupDBVM() {
	if (!initializedvmm)
		return;

	if (enterVMM2MDL) {
		MmUnlockPages(enterVMM2MDL);
		IoFreeMdl(enterVMM2MDL);
		enterVMM2MDL = 0;
	}

	if (enterVMM2) {
		RtlZeroMemory(enterVMM2, 4096);
		MmFreeContiguousMemory(enterVMM2);
		enterVMM2 = 0;
	}

	if (TemporaryPagingSetupMDL) {
		MmUnlockPages(TemporaryPagingSetupMDL);
		IoFreeMdl(TemporaryPagingSetupMDL);
		TemporaryPagingSetupMDL = 0;
	}

	if (TemporaryPagingSetup) {
		RtlZeroMemory(TemporaryPagingSetup, 4096 * 4);
		ExFreePool(TemporaryPagingSetup);
		TemporaryPagingSetup = 0;
	}

	if (originalstateMDL) {
		MmUnlockPages(originalstateMDL);
		IoFreeMdl(originalstateMDL);
		originalstateMDL = 0;
	}

	if (originalstate) {
		RtlZeroMemory(originalstate, 4096);
		ExFreePool(originalstate);
		originalstate = 0;
	}

	initializedvmm = 0;
}

void initializeDBVM(PCWSTR dbvmimgpath)
/*
Runs at passive mode
*/
{
	if (initializedvmm)
		return; //already initialized

	DbgPrint("First time run. Initializing vmm section");

	PHYSICAL_ADDRESS LowAddress, HighAddress, SkipBytes;
	LowAddress.QuadPart = 0;
	HighAddress.QuadPart = -1;
	SkipBytes.QuadPart = 0;

	DBVMMDL = MmAllocatePagesForMdlEx(LowAddress, HighAddress, SkipBytes, 4 * 1024 * 1024, MmCached, MM_ALLOCATE_REQUIRE_CONTIGUOUS_CHUNKS | MM_ALLOCATE_FULLY_REQUIRED);
	if (!DBVMMDL) {
		DbgPrint("Failure allocating the required 4MB\n");
		return;
	}

	vmm = MmMapLockedPagesSpecifyCache(DBVMMDL, KernelMode, MmCached, NULL, FALSE, 0);
	
	//default password when dbvm is just loaded (needed for adding extra ram)
	vmx_password1 = 0x76543210;
	vmx_password2 = 0xfedcba98;
	vmx_password3 = 0x90909090;


	if (vmm)
	{
		int i;
		PHYSICAL_ADDRESS maxPA;
		HANDLE dbvmimghandle;
		UNICODE_STRING filename;
		IO_STATUS_BLOCK statusblock;
		OBJECT_ATTRIBUTES oa;
		NTSTATUS OpenedFile;

		vmmPA = (UINT_PTR)MmGetPhysicalAddress(vmm).QuadPart;

		DbgPrint("Allocated memory at virtual address %p (physical address %I64x)\n", vmm, MmGetPhysicalAddress(vmm));
		vmmPA = MmGetMdlPfnArray(DBVMMDL)[0] << 12;
		DbgPrint("(physical address %I64x)\n", vmmPA);

		RtlZeroMemory(vmm, 4 * 1024 * 1024); //initialize

		RtlInitUnicodeString(&filename, dbvmimgpath);


		//Load the .img file
		InitializeObjectAttributes(&oa, &filename, 0, NULL, NULL);
		OpenedFile = ZwCreateFile(&dbvmimghandle, SYNCHRONIZE | STANDARD_RIGHTS_READ, &oa, &statusblock, NULL, FILE_SYNCHRONOUS_IO_NONALERT | FILE_ATTRIBUTE_NORMAL, 0, FILE_OPEN, 0, NULL, 0);

#ifdef TOBESIGNED
		if (OpenedFile==STATUS_SUCCESS)
			OpenedFile=CheckSignatureOfFile(&filename, FALSE);
#endif

		if (OpenedFile == STATUS_SUCCESS)
		{
			WORD startsector;
			LARGE_INTEGER byteoffset;
			FILE_STANDARD_INFORMATION fsi;
			NTSTATUS ReadFile;






			//Getting filesize
			ZwQueryInformationFile(dbvmimghandle, &statusblock, &fsi, sizeof(fsi), FileStandardInformation);

			//fsi.EndOfFile contains the filesize

			if (fsi.EndOfFile.QuadPart>4 * 1024 * 1024)
			{
				DbgPrint("File bigger than 4MB. Big retard detected\n");
				return;
			}


			byteoffset.QuadPart = 0x8; //offset containing sectornumber of the vmm location
			ReadFile = ZwReadFile(dbvmimghandle, NULL, NULL, NULL, &statusblock, &startsector, 2, &byteoffset, NULL);

			if (ReadFile == STATUS_PENDING)
			{
				if (ZwWaitForSingleObject(dbvmimghandle, FALSE, NULL) != STATUS_SUCCESS)
				{
					DbgPrint("Read failure\n");
					return;
				}
			}

			if (statusblock.Status == STATUS_SUCCESS)
			{
				DWORD vmmsize = fsi.EndOfFile.LowPart;// -(startsector * 512);

				//now read the vmdisk into the allocated memory
				DbgPrint("The startsector=%d (that's offset %d)\n", startsector, startsector * 512);

				byteoffset.QuadPart = startsector * 512;
				ReadFile = ZwReadFile(dbvmimghandle, NULL, NULL, NULL, &statusblock, vmm, vmmsize, &byteoffset, NULL);
				if (ReadFile == STATUS_PENDING)
					ZwWaitForSingleObject(dbvmimghandle, FALSE, NULL);

				vmmsize = (vmmsize + 4096) & 0xfffffffffffff000ULL; //adjust the size internally to a page boundary (sure, there's some mem loss, but it's predicted, dbvm assumes first 10 pages are scratch pages)

				DbgPrint("vmmsize=%x\n", vmmsize);

				if (statusblock.Status == STATUS_SUCCESS)
				{
					//basic paging setup for the vmm, will get expanded by the vmm itself
					UINT64		*GDTBase;
					PPDPTE_PAE	PageMapLevel4;
					PPDPTE_PAE	PageDirPtr;
					PPDE_PAE	PageDir;
					PPTE_PAE	PageTable1, PageTable2;
					UINT_PTR	FreeVA = (((UINT_PTR)vmm + vmmsize) & 0xfffffffffffff000ULL) + 4096; //next free virtual address

					UINT64		mainstack;
					initvars = (PINITVARS)&vmm[0x10];

					mainstack = FreeVA; FreeVA += 16 * 4096;

					GDTBase = (UINT64*)FreeVA; FreeVA += 4096;
					PageDirPtr = (PPDPTE_PAE)FreeVA; FreeVA += 4096;
					PageDir = (PPDE_PAE)FreeVA;  FreeVA += 4096;
					PageTable1 = (PPTE_PAE)FreeVA;  FreeVA += 4096;
					PageTable2 = (PPTE_PAE)FreeVA;  FreeVA += 4096;
					PageMapLevel4 = (PPDPTE_PAE)FreeVA;  FreeVA += 4096; //has to be the last alloc

					DBVMPML4PA = (UINT_PTR)MmGetPhysicalAddress(PageMapLevel4).QuadPart;


					//blame MS for making this hard to read
					DbgPrint("Setting up initial paging table for vmm\n");

					*(PUINT64)(&PageMapLevel4[0]) = MmGetPhysicalAddress(PageDirPtr).QuadPart;
					PageMapLevel4[0].P = 1;
					PageMapLevel4[0].RW = 1;

					*(PUINT64)(&PageDirPtr[0]) = MmGetPhysicalAddress(PageDir).QuadPart;
					PageDirPtr[0].P = 1;
					PageDirPtr[0].RW = 1;

					//DBVM 11 does no longer need the map at 0 to 00400000
					*(PUINT64)(&PageDir[0]) = 0; //00000000-00200000
					PageDir[0].P = 1;
					PageDir[0].RW = 0; //map as readonly (only for the jump to 0x00400000)
					PageDir[0].PS = 1;

					*(PUINT64)(&PageDir[1]) = 0x00200000; //00200000-00400000
					PageDir[1].P = 1;
					PageDir[1].RW = 0;
					PageDir[1].PS = 1;


					{
						*(PUINT64)(&PageDir[2]) = MmGetPhysicalAddress(PageTable1).QuadPart;
						PageDir[2].P = 1;
						PageDir[2].RW = 1;
						PageDir[2].PS = 0; //points to a pagetable 

						*(PUINT64)(&PageDir[3]) = MmGetPhysicalAddress(PageTable2).QuadPart;
						PageDir[3].P = 1;
						PageDir[3].RW = 1;
						PageDir[3].PS = 0;
					}

					//fill in the pagetables
					for (i = 0; i<1024; i++) //pagetable1 and 2 are allocated after eachother, so 1024 can be used here using pagetable1
					{
						*(PUINT64)(&PageTable1[i]) = MmGetPhysicalAddress((PVOID)(((UINT_PTR)vmm) + (4096 * i))).QuadPart;
						PageTable1[i].P = 1;
						PageTable1[i].RW = 1;
					}


					i = (int)((UINT64)((mainstack - (UINT64)vmm)) >> 12);
					PageTable1[i].P = 0; //mark the first page of the stack as unreadable



					//setup GDT
					GDTBase[0] = 0;						//0 :
					GDTBase[1] = 0x00cf92000000ffffULL;	//8 : 32-bit data
					GDTBase[2] = 0x00cf96000000ffffULL;	//16: test, stack, failed, unused
					GDTBase[3] = 0x00cf9b000000ffffULL;	//24: 32-bit code
					GDTBase[4] = 0x00009a000000ffffULL;	//32: 16-bit code
					GDTBase[5] = 0x000092000000ffffULL;	//40: 16-bit data
					GDTBase[6] = 0x00009a030000ffffULL;	//48: 16-bit code, starting at 0x30000
					GDTBase[7] = 0;						//56: 32-bit task	
					GDTBase[8] = 0;						//64: 64-bit task
					GDTBase[9] = 0;						//72:  ^   ^   ^
					GDTBase[10] = 0x00af9b000000ffffULL;	//80: 64-bit code
					GDTBase[11] = 0;						//88:  ^   ^   ^
					GDTBase[12] = 0;						//96: 64-bit tss descriptor (2)
					GDTBase[13] = 0;						//104: ^   ^   ^


					NewGDTDescriptor.limit = 0x6f; //111
					NewGDTDescriptor.base = 0x00400000 + (UINT64)GDTBase - (UINT64)vmm;

					DbgPrint("&NewGDTDescriptor=%p, &NewGDTDescriptor.limit=%p, &NewGDTDescriptor.base=%p\n", &NewGDTDescriptor, &NewGDTDescriptor.limit, &NewGDTDescriptor.base);
					DbgPrint("NewGDTDescriptor.limit=%x\n", NewGDTDescriptor.limit);
					DbgPrint("NewGDTDescriptor.base=%p\n", NewGDTDescriptor.base);

					NewGDTDescriptorVA = (UINT_PTR)&NewGDTDescriptor;


					maxPA.QuadPart = 0x003fffffULL; //allocate 4k at the lower 4MB
					DbgPrint("Before enterVMM2 alloc: maxPA=%I64x\n", maxPA.QuadPart);

					enterVMM2 = MmAllocateContiguousMemory(4096, maxPA);
					if (enterVMM2)
					{
						unsigned char *original = (unsigned char *)enterVMM;
						RtlZeroMemory(enterVMM2, 4096);

						enterVMM2MDL = IoAllocateMdl(enterVMM2, 4096, FALSE, FALSE, NULL);
						MmProbeAndLockPages(enterVMM2MDL, KernelMode, IoReadAccess);

						DbgPrint("enterVMM is located at %p (%I64x)\n", enterVMM, MmGetPhysicalAddress(enterVMM).QuadPart);
						DbgPrint("enterVMM2 is located at %p (%I64x)\n", enterVMM2, MmGetPhysicalAddress(enterVMM2).QuadPart);


						DbgPrint("Copying function till end\n");
						//copy memory

						i = 0;
						while ((i<4096) && ((original[i] != 0xce) || (original[i + 1] != 0xce) || (original[i + 2] != 0xce) || (original[i + 3] != 0xce) || (original[i + 4] != 0xce)))
							i++;

						DbgPrint("size is %d", i);

						RtlCopyMemory(enterVMM2, original, i);
						DbgPrint("Copy done\n");
					}
					else
					{
						DbgPrint("Failure allocating enterVMM2\n");
						return;
					}




					//now create a paging setup where enterVMM2 is identity mapped AND mapped at the current virtual address, needed to be able to go down to nonpaged mode
					//easiest way, make every page point to enterVMM2				

					//allocate 4 pages
					DbgPrint("Allocating memory for the temp pagedir\n");
					TemporaryPagingSetup = ExAllocatePool(PagedPool, 4 * 4096);
					if (TemporaryPagingSetup == NULL)
					{
						DbgPrint("TemporaryPagingSetup==NULL!!!\n");
						return;
					}

					TemporaryPagingSetupMDL = IoAllocateMdl(TemporaryPagingSetup, 4 * 4096, FALSE, FALSE, NULL);
					MmProbeAndLockPages(TemporaryPagingSetupMDL, KernelMode, IoReadAccess);



					RtlZeroMemory(TemporaryPagingSetup, 4096 * 4);
					DbgPrint("TemporaryPagingSetup is located at %p (%I64x)\n", TemporaryPagingSetup, MmGetPhysicalAddress(TemporaryPagingSetup).QuadPart);


					TemporaryPagingSetupPA = MmGetMdlPfnArray(TemporaryPagingSetupMDL)[0] << 12; // (UINT_PTR)MmGetPhysicalAddress(TemporaryPagingSetup).QuadPart;

					enterVMM2PA = MmGetMdlPfnArray(enterVMM2MDL)[0] << 12;
					DbgPrint("TemporaryPagingSetupPA = (%I64x) (Should be %I64x)\n", (UINT64)TemporaryPagingSetupPA, (UINT64)MmGetPhysicalAddress(TemporaryPagingSetup).QuadPart);
#ifdef AMD64			
					DbgPrint("Setting up temporary paging setup for x64\n");


					{
						PUINT64 PML4Table = (PUINT64)TemporaryPagingSetup;
						PUINT64	PageDirPtr = (PUINT64)((UINT_PTR)TemporaryPagingSetup + 4096);
						PUINT64	PageDir = (PUINT64)((UINT_PTR)TemporaryPagingSetup + 2 * 4096);
						PUINT64	PageTable = (PUINT64)((UINT_PTR)TemporaryPagingSetup + 3 * 4096);

						DbgPrint("PAE paging\n");
						for (i = 0; i<512; i++)
						{
							PML4Table[i] = MmGetPhysicalAddress(PageDirPtr).QuadPart;
							((PPDPTE_PAE)(&PML4Table[i]))->P = 1;

							PageDirPtr[i] = MmGetPhysicalAddress(PageDir).QuadPart;
							((PPDPTE_PAE)(&PageDirPtr[i]))->P = 1;

							PageDir[i] = MmGetPhysicalAddress(PageTable).QuadPart;
							((PPDE_PAE)(&PageDir[i]))->P = 1;
							((PPDE_PAE)(&PageDir[i]))->PS = 0; //4KB

							PageTable[i] = enterVMM2PA;
							((PPTE_PAE)(&PageTable[i]))->P = 1;
						}
					}

#else
					DbgPrint("Setting up temporary paging setup\n");
					if (PTESize==8) //PAE paging
					{
						PUINT64	PageDirPtr=(PUINT64)TemporaryPagingSetup;						
						PUINT64	PageDir=(PUINT64)((UINT_PTR)TemporaryPagingSetup+4096);
						PUINT64	PageTable=(PUINT64)((UINT_PTR)TemporaryPagingSetup+2*4096);

						DbgPrint("PAE paging\n");
						for (i=0; i<512; i++)
						{
							PageDirPtr[i]=MmGetPhysicalAddress(PageDir).QuadPart;
							((PPDPTE_PAE)(&PageDirPtr[i]))->P=1;
							//((PPDPTE_PAE)(&PageDirPtr[i]))->RW=1;


							PageDir[i]=MmGetPhysicalAddress(PageTable).QuadPart;
							((PPDE_PAE)(&PageDir[i]))->P=1;
							//((PPDE_PAE)(&PageDir[i]))->RW=1;							
							((PPDE_PAE)(&PageDir[i]))->PS=0; //4KB

							PageTable[i]=MmGetPhysicalAddress(enterVMM2).QuadPart;
							((PPTE_PAE)(&PageTable[i]))->P=1;
							//((PPTE_PAE)(&PageTable[i]))->RW=1;					

						}

					}	
					else
					{
						//normal(old) 4 byte page entries
						PDWORD PageDir=(PDWORD)TemporaryPagingSetup;
						PDWORD PageTable=(PDWORD)((DWORD)TemporaryPagingSetup+4096);
						DbgPrint("Normal paging\n");
						for (i=0; i<1024; i++)
						{
							PageDir[i]=MmGetPhysicalAddress(PageTable).LowPart;
							((PPDE)(&PageDir[i]))->P=1;
							((PPDE)(&PageDir[i]))->RW=1;							
							((PPDE)(&PageDir[i]))->PS=0; //4KB

							PageTable[i]=MmGetPhysicalAddress(enterVMM2).LowPart;
							((PPTE)(&PageTable[i]))->P=1;
							((PPTE)(&PageTable[i]))->RW=1;	
						}

					}
#endif

					DbgPrint("Temp paging has been setup\n");


					//enterVMM2PA = (UINT_PTR)MmGetPhysicalAddress(enterVMM2).QuadPart;




					originalstate = ExAllocatePool(PagedPool, 4096);
					originalstateMDL = IoAllocateMdl(originalstate, 4096, FALSE, FALSE, NULL);
					MmProbeAndLockPages(originalstateMDL, KernelMode, IoReadAccess);

					RtlZeroMemory(originalstate, 4096);
					originalstatePA = MmGetMdlPfnArray(originalstateMDL)[0] << 12; //(UINT_PTR)MmGetPhysicalAddress(originalstate).QuadPart;					

					DbgPrint("enterVMM2PA=%llx\n", enterVMM2PA);
					DbgPrint("originalstatePA=%llx\n", originalstatePA);
					DbgPrint("originalstatePA=%llx\n", (UINT_PTR)MmGetPhysicalAddress(originalstate).QuadPart);

					//setup init vars	
					initvars->loadedOS = originalstatePA;
					initvars->vmmstart = vmmPA;
					initvars->pagedirlvl4 = 0x00400000 + ((UINT64)PageMapLevel4 - (UINT64)vmm);
					initvars->nextstack = 0x00400000 + ((UINT64)mainstack - (UINT64)vmm) + (16 * 4096) - 0x40;
					initvars->contiguousmemory = 0;
					
					PMDL contiguousMDL = MmAllocatePagesForMdlEx(LowAddress, HighAddress, SkipBytes, 8 * 4096, MmCached, MM_ALLOCATE_REQUIRE_CONTIGUOUS_CHUNKS | MM_ALLOCATE_FULLY_REQUIRED);
					if (contiguousMDL) {
						initvars->contiguousmemory = MmGetMdlPfnArray(contiguousMDL)[0] << 12;
						DbgPrint("contiguous PA =%llx\n", initvars->contiguousmemory);
						initvars->contiguousmemorysize = 8;
						ExFreePool(contiguousMDL);
					}
					else
						DbgPrint("Failed allocating 32KB of contiguous memory");


					initializedvmm = TRUE;

					KeInitializeSpinLock(&LoadedOSSpinLock);

				}
			}
			ZwClose(dbvmimghandle);


			DbgPrint("Opened and processed: %S\n", filename.Buffer);
		}
		else
		{
			DbgPrint("Failure opening the file. Status=%x  (filename=%S)\n", OpenedFile, filename.Buffer);
		}
		//fill in some specific memory regions
		MmUnmapLockedPages(vmm, DBVMMDL);
	}
	else
	{
		DbgPrint("Failure allocating the required 4MB\n");
	}
	ExFreePool(DBVMMDL);
}

void vmxoffload(void)
{
	//save entry state for easy exit in ReturnFromvmxoffload
	EFLAGS eflags;

	PHYSICAL_ADDRESS minPA, maxPA,boundary;
	GDT gdt;
	IDT idt;
	
	/*
	__try
	{
		DbgBreakPoint();
	}
	__except (1)
	{
		DbgPrint("No debugger\n");
	}*/
	
	
	//allocate 8MB of contigues physical memory
	minPA.QuadPart=0;
	maxPA.QuadPart=0xffffffffff000000ULL;
	boundary.QuadPart=0x00800000ULL; //8 mb boundaries


	DbgPrint("vmxoffload\n");



	DbgPrint("initializedvmm=%d\n", initializedvmm); 
	if (initializedvmm)
	{
		DbgPrint("cpunr=%d\n",cpunr());
		
		DbgPrint("Storing original state\n");
		originalstate->cpucount=getCpuCount();
		DbgPrint("originalstate->cpucount=%d",originalstate->cpucount);


		originalstate->originalEFER=readMSR(0xc0000080); //amd prefers this over an LME

		originalstate->originalLME=(int)(((DWORD)(readMSR(0xc0000080)) >> 8) & 1);		
		DbgPrint("originalstate->originalLME=%d",originalstate->originalLME);

		
		originalstate->cr0=(UINT_PTR)getCR0();
		

		DbgPrint("originalstate->cr0=%I64x",originalstate->cr0);

		/*
		{
			int xxx;
			unsigned char *x;
			x=&originalstate->cr0;
			for (xxx=0; xxx<8; xxx++)
			{
				DbgPrint("%x ",x[xxx]);
			}
		}
		*/

		originalstate->cr2=(UINT_PTR)getCR2();
		DbgPrint("originalstate->cr2=%I64x",originalstate->cr2);
		/*
		{
			int xxx;
			unsigned char *x;
			x=&originalstate->cr2;
			for (xxx=0; xxx<8; xxx++)
			{
				DbgPrint("%x ",x[xxx]);
			}
		}*/

		originalstate->cr3=(UINT_PTR)getCR3();
		//DbgPrint("originalstate->cr3=%I64x",originalstate->cr3);

		originalstate->cr4=(UINT_PTR)getCR4();
		//DbgPrint("originalstate->cr4=%I64x",originalstate->cr4);

		originalstate->ss=getSS();
		originalstate->ss_AccessRights = getAccessRights(originalstate->ss);
		originalstate->ss_Limit = getSegmentLimit(originalstate->ss);

		//DbgPrint("originalstate->ss=%I64x",originalstate->ss);
		originalstate->cs=getCS();
		originalstate->cs_AccessRights = getAccessRights(originalstate->cs);
		originalstate->cs_Limit = getSegmentLimit(originalstate->cs);
		//DbgPrint("originalstate->cs=%I64x",originalstate->cs);
		originalstate->ds=getDS();
		originalstate->ds_AccessRights = getAccessRights(originalstate->ds);
		originalstate->ds_Limit = getSegmentLimit(originalstate->ds);
		//DbgPrint("originalstate->ds=%I64x",originalstate->ds);
		originalstate->es=getES();
		originalstate->es_AccessRights = getAccessRights(originalstate->es);
		originalstate->es_Limit = getSegmentLimit(originalstate->es);
		//DbgPrint("originalstate->es=%I64x",originalstate->es);
		originalstate->fs=getFS();
		originalstate->fs_AccessRights = getAccessRights(originalstate->fs);
		originalstate->fs_Limit = getSegmentLimit(originalstate->fs);
		//DbgPrint("originalstate->fs=%I64x",originalstate->fs);
		originalstate->gs=getGS();
		originalstate->gs_AccessRights = getAccessRights(originalstate->gs);
		originalstate->gs_Limit = getSegmentLimit(originalstate->gs);
		//DbgPrint("originalstate->gs=%I64x",originalstate->gs);
		originalstate->ldt=GetLDT();
		//DbgPrint("originalstate->ldt=%I64x",originalstate->ldt);
		originalstate->tr=GetTR();
		//DbgPrint("originalstate->tr=%I64x",originalstate->tr);		


		originalstate->fsbase=readMSR(0xc0000100);
		originalstate->gsbase=readMSR(0xc0000101);

		//DbgPrint("originalstate->fsbase=%I64x originalstate->gsbase=%I64x\n", originalstate->fsbase, originalstate->gsbase);


		originalstate->dr7=getDR7();

		
		gdt.vector=0;
		gdt.wLimit=0;
		GetGDT(&gdt);									
		originalstate->gdtbase=(ULONG_PTR)gdt.vector;
		originalstate->gdtlimit=gdt.wLimit;

		//DbgPrint("originalstate->gdtbase=%I64x",originalstate->gdtbase);
		//DbgPrint("originalstate->gdtlimit=%I64x",originalstate->gdtlimit);

		GetIDT(&idt);
		originalstate->idtbase=(ULONG_PTR)idt.vector;
		originalstate->idtlimit=idt.wLimit;

		//DbgPrint("originalstate->idtbase=%I64x",originalstate->idtbase);
		//DbgPrint("originalstate->idtlimit=%I64x",originalstate->idtlimit);
		
		
		eflags=getEflags();		
		eflags.IF = 0;
		originalstate->rflags=*(PUINT_PTR)&eflags;

		originalstate->rsp=getRSP();
		//DbgPrint("originalstate->rsp=%I64x",originalstate->rsp);
		originalstate->rbp=getRBP();
		//DbgPrint("originalstate->rbp=%I64x",originalstate->rbp);

		originalstate->rax=getRAX();
		//DbgPrint("originalstate->rax=%I64x",originalstate->rax);
		originalstate->rbx=getRBX();
		//DbgPrint("originalstate->rbx=%I64x",originalstate->rbx);
		originalstate->rcx=getRCX();
		//DbgPrint("originalstate->rcx=%I64x",originalstate->rcx);
		originalstate->rdx=getRDX();
		//DbgPrint("originalstate->rdx=%I64x",originalstate->rdx);
		originalstate->rsi=getRSI();
		//DbgPrint("originalstate->rsi=%I64x",originalstate->rsi);
		originalstate->rdi=getRDI();
		//DbgPrint("originalstate->rdi=%I64x",originalstate->rdi);
#ifdef AMD64
		originalstate->r8=getR8();
		//DbgPrint("originalstate->r8=%I64x",originalstate->r8);
		originalstate->r9=getR9();
		//DbgPrint("originalstate->r9=%I64x",originalstate->r9);
		originalstate->r10=getR10();
		//DbgPrint("originalstate->r10=%I64x",originalstate->r10);
		originalstate->r11=getR11();
		//DbgPrint("originalstate->r11=%I64x",originalstate->r11);
		originalstate->r12=getR12();
		//DbgPrint("originalstate->r12=%I64x",originalstate->r12);
		originalstate->r13=getR13();
		//DbgPrint("originalstate->r13=%I64x",originalstate->r13);
		originalstate->r14=getR14();
		//DbgPrint("originalstate->r14=%I64x",originalstate->r14);
		originalstate->r15=getR15();
		//DbgPrint("originalstate->r15=%I64x",originalstate->r15);
#endif
		

#ifdef AMD64
		
		originalstate->rsp-=8; //adjust rsp for the "call entervmmprologue"
 		originalstate->rip=(UINT_PTR)enterVMMEpilogue; //enterVMMEpilogue is an address inside the entervmmprologue function

		//DbgPrint("originalstate->rip=%llx",originalstate->rip);

		//DbgPrint("Calling entervmm2. (Originalstate=%p (%llx))\n",originalstate,originalstatePA);


		

		//call to entervmmprologue, pushes the return value on the stack
		enterVMMPrologue();

		
		enableInterrupts();

		//DbgPrint("Returned from enterVMMPrologue\n");

		//DbgPrint("cpunr=%d\n",cpunr());

	
		

	
		

		//KeLowerIrql(oldirql);


		
		//DbgPrint("cpunr=%d\n",cpunr());
#else

		
		{
			ULONG vmmentryeip;
			
			__asm
			{
				lea eax,[enterVMMEpilogue]
				mov vmmentryeip,eax 
			}	
			originalstate->rip=(UINT64)vmmentryeip;
		}
		


		__asm{
			
			cli //goodbye interrupts						
			xchg bx,bx


			mov ebx,vmmPA
			__emit 0x8b
			__emit 0xeb //mov ebp,ebx
			

			lea ebx,NewGDTDescriptor
			mov ecx,DBVMPML4PA;
			mov edx,TemporaryPagingSetupPA //for the mov cr3,ecx
			
			mov esi,enterVMM2PA
			mov edi,originalstatePA
			
			call [enterVMM2]
			
			//Will never get here. NEVER
			FUUUUU:
			xchg bx,bx
			jmp FUUUUU

		

enterVMMEpilogue:
			//cli //test
			nop
			nop
			xchg bx,bx //bochs bp
			nop
			nop
			sti				
			nop
			nop
			nop						
			nop
			nop
			nop						
		}
		//KeLowerIrql(oldirql);
		
#endif
		//DbgPrint("Returning\n");

		return;



	}
	
}

void vmxoffload_override(CCHAR cpunr, PKDEFERRED_ROUTINE Dpc, PVOID DeferredContext, PVOID *SystemArgument1, PVOID *SystemArgument2)
{
	//runs at passive (in any unrelated cpu)

	//allocate 64KB of extra memory for this(and every other) cpu's DBVM
	PHYSICAL_ADDRESS LowAddress, HighAddress, SkipBytes;
	PMDL mdl;
	DbgPrint("vmxoffload_override\n");
	LowAddress.QuadPart = 0;
	HighAddress.QuadPart = 0xffffffffffffffffI64;
	SkipBytes.QuadPart = 0;
	mdl = MmAllocatePagesForMdlEx(LowAddress, HighAddress, SkipBytes, 64 * 1024, MmCached, MM_ALLOCATE_REQUIRE_CONTIGUOUS_CHUNKS | MM_ALLOCATE_FULLY_REQUIRED); //do not free this, EVER

	if (mdl)
	{
		//convert the pfnlist to a list DBVM understands
		PDBVMOffloadMemInfo mi = ExAllocatePool(NonPagedPool, sizeof(DBVMOffloadMemInfo));
		int i;
		PFN_NUMBER *pfnlist;

		DbgPrint("vmxoffload_override: mi=%p\n", mi);
		
		mi->List = ExAllocatePool(NonPagedPool, sizeof(UINT64) * 16);

		DbgPrint("vmxoffload_override: mi->list=%p\n", mi->List);

		pfnlist = MmGetMdlPfnArray(mdl);
		
		for (i = 0; i < 16; i++)
		  mi->List[i] = pfnlist[i] << 12;

		mi->Count = 16;

		ExFreePool(mdl);

		*SystemArgument1 = mi;
	}
}

__drv_functionClass(KDEFERRED_ROUTINE) 
__drv_maxIRQL(DISPATCH_LEVEL) 
__drv_minIRQL(DISPATCH_LEVEL) 
__drv_requiresIRQL(DISPATCH_LEVEL)
__drv_sameIRQL VOID
vmxoffload_dpc(
__in struct _KDPC *Dpc,
__in_opt PVOID DeferredContext,
__in_opt PVOID SystemArgument1,
__in_opt PVOID SystemArgument2
)
{
	int c = cpunr();
	DbgPrint("vmxoffload_dpc: CPU %d\n", c);
	KeAcquireSpinLockAtDpcLevel(&LoadedOSSpinLock);
	vmxoffload();

	//still here so very likely DBVM is loaded
	if (SystemArgument1)
	{
		int x;
		PDBVMOffloadMemInfo mi = (PDBVMOffloadMemInfo)SystemArgument1;
		DbgPrint("mi->List=%p mi->Count=%d\n", mi->List, mi->Count);

		x=vmx_add_memory(mi->List, mi->Count);
		DbgPrint("vmx_add_memory returned %x\n", x);

		if (mi->List)
			ExFreePool(mi->List);

		ExFreePool(mi);
	}
	else
		DbgPrint("Error: SystemArgument1=NULL\n");
	KeReleaseSpinLockFromDpcLevel(&LoadedOSSpinLock);
}
