/* vmm.c: This is the virtual machine
 * It will be loaded at virtual address 0x00400000 (vmma.asm that is which just jumps short to intialize paging)
 * On initialisation 0 to 4MB is identity mapped, to the stored memory regions are available to mess with
 */



#include "common.h"
#include "main.h"
#include "mm.h"
#include "neward.h"
#include "apic.h"
#include "displaydebug.h"

#include "multicore.h"
#include "inthandlers.h"
#include "vmmhelper.h"
#include "vmeventhandler.h"

#include "distorm.h"
#include "keyboard.h"

#include "pci.h"
#include "offloados.h"
#include "msrnames.h"
#include "vmxcontrolstructures.h"

#include "test.h"
#include "vmcall.h"
#include "vmpaging.h"
#include "vmxsetup.h"
#include "vmcall.h"
#include "exports.h"

#include "luahandler.h"


//#include "psod.h" //for pink screen of death support

/*
//#include "logo.c"
*/

#define APIC_ID_OFFSET 0x020
#define APIC_SVR_OFFSET 0x0f0



void menu(void);
void menu2(void);



PINT_VECTOR intvector=NULL;

unsigned char *ffpage=NULL;
PPTE_PAE   ffpagetable=NULL;
PPDE_PAE   ffpagedir=NULL;


PTSS testTSS=NULL;

int cpu_stepping;
int cpu_model;
int cpu_familyID;
int cpu_type;
int cpu_ext_modelID;
int cpu_ext_familyID;



unsigned long long IA32_APIC_BASE=0xfee00000;
unsigned long long APIC_ID=0xfee00020;
unsigned long long APIC_SVR=0xfee000f0;

unsigned int BOOT_ID=0xffffffff;

extern unsigned int isAP;
volatile int AP_Terminate; //set to 1 to terminate the AP cpu's
volatile int AP_Launch; //set to 1 to launch the AP cpu's

#if (DISPLAYDEBUG==0)
int needtospawnApplicationProcessors=1;
#else
int needtospawnApplicationProcessors=0; //the display is cluttered as it is
#endif


#define SETINT(INTNR) intvector[INTNR].wLowOffset=(WORD)(UINT64)inthandler##INTNR; \
                      intvector[INTNR].wHighOffset=(WORD)((UINT64)inthandler##INTNR >> 16);


#ifdef DEBUG
int autostart=0; //since simnow amd emu doesn't support serial import...
#else
int autostart=1;
#endif //debug

//int isrunning=0;

pcpuinfo firstcpuinfo, lastaddedcpuinfo; //just for debugging, nothing important


#ifdef DEBUGINTHANDLER
criticalSection cinthandlerMenuCS;
#endif

int IntHandlerDebug=0;

char bootdisk;



int cinthandler(unsigned long long *stack, int intnr) //todo: move to it's own sourcefile
{
  PRFLAGS rflags;
  int errorcode=0;
  UINT64 errorcodeValue;
  int i;
  DWORD thisAPICID;
  int cpunr=0;

  enableserial();

#ifdef DEBUG
  sendstringCS.ignorelock=1;
  sendstringfCS.ignorelock=1;
#endif

  ddDrawRectangle(DDHorizontalResolution-100,0,100,100,_rdtsc());

  if (readMSRSafe(IA32_FS_BASE_MSR)==0)
  {
    sendstringf("Invalid FS base during exception\n");
    ddDrawRectangle(0,DDVerticalResolution-100,100,100,0xff0000);
    while (1) outportb(0x80,0xc5);
  }

  pcpuinfo cpuinfo=getcpuinfo();
  cpunr=cpuinfo->cpunr;

  //debug, remove:
  //if PIC_StillEnabled
  //outportb(0x20,0x20);
  //outportb(0xa0,0x20);


  //apic_eoi();
  //^^^


  UINT64 originalDR7=getDR7();

  if (intnr==1)
  {
    //disable breakpoints
    setDR7(0ULL);
  }

  thisAPICID=getAPICID();

#ifdef CHECKAPICID
  if (thisAPICID!=cpuinfo->apicid)
  {
    sendstringCS.ignorelock=1;
    sendstringfCS.ignorelock=1;
    sendstringf("Interrupt %d. Invalid cpuinfo", intnr);
    while(1);
  }
#endif



  sendstringCS.lockcount=0;
  sendstringCS.locked=0;
  sendstringfCS.lockcount=0;
  sendstringfCS.locked=0;


 // sendstringf("interrupt fired : %d (%x)\n\r", intnr,intnr);

  sendstringf("cpunr=%d (apicid=%d)\n\r",cpunr, thisAPICID);
  sendstringf("intnr=%d\n\r",intnr);
  sendstringf("rsp=%x\n\r",getRSP());
  sendstringf("cr2=%6\n\r",getCR2());
  errorcode=0;


  if ((stack[17]==80) && (stack[18]==80))
  {
    //not sure...
    if ((stack[16]>=0x00400000) && (stack[16]<0x00800000))
    {
      //in the region of the code of the vmm, so I guess it's no errorcode (and cs and eflags=80)
      errorcode=0;
    }
    else
      errorcode=1;
  }
  else
  if (stack[18]==80)
    errorcode=1;
  else
  if (stack[17]==80)
    errorcode=0;

  if (errorcode)
  {

    errorcodeValue=stack[16];
    sendstringf("Interrupt has errorcode : %x (",errorcodeValue);
    if (errorcodeValue & 1)
    {
      sendstring("EXT ");
    }

    if (errorcodeValue & 2)
    {
      sendstring("IDT ");
    }

    if (errorcodeValue & 4)
    {
      sendstring("TI ");
    }

    sendstringf("%x ",errorcodeValue & 0xFFFFFFF8);

    sendstringf(")\n\r");
  }
  else
  {
    //sendstringf("Interrupt has no errorcode\n\r");
  }

  sendstringf("rip=%x\n\r",stack[16+errorcode]);
  sendstringf("rflags=%x\n\r",stack[16+2+errorcode]);


  rflags=(PRFLAGS)&stack[16+2+errorcode];


  if ((intnr==2) && (rflags->IF==0))
  {
    cpuinfo->NMIOccured=1;
    NMIcount++;

    cpuinfo->NMIOccured=2;
    /*

    //set up NMI window exiting

    if (vmx_enableNMIWindowExiting()==0) //todo: test this code. I think it enters an invalid state
    {
      sendstringf("NMI handling: failed to set PBEF_NMI_WINDOW_EXITING.  Raising NMI like a retard\n");
      cpuinfo->NMIOccured=2;
    }
    */
    ddDrawRectangle(0,DDVerticalResolution-100,100,100,0xff0000);

    return 0;
  }

  sendstringf("Checking if it was an expected interrupt\n\r");
  cpuinfo->LastExceptionRIP=stack[16+errorcode];

  if (cpuinfo->OnException[0].RIP)
  {
    nosendchar[thisAPICID]=0;
    sendstringf("OnException is set. Passing it to longjmp\n");  //no need to set rflags back, the original state contains that info

    longjmp(cpuinfo->OnException, 0x100 | intnr);

    sendstringf("longjmp just went through...\n");
    ddDrawRectangle(0,DDVerticalResolution-100,100,100,0xff0000);
    while (1) outportb(0x80,0xc6);
  }

  if (cpuinfo->OnInterrupt.RIP)
  {
    QWORD oldrip=stack[16+errorcode];
    sendstringf("Yes, OnInterrupt is set to %x\n\r",cpuinfo->OnInterrupt);

    stack[8+errorcode]=(QWORD)(cpuinfo->OnInterrupt.RBP);
    stack[16+errorcode]=(QWORD)(cpuinfo->OnInterrupt.RIP);
    stack[19+errorcode]=(QWORD)(cpuinfo->OnInterrupt.RSP);

    rflags->IF=0; //disable the IF flag in the eflags register stored on the stack (when called during checks if an int was pending)


    cpuinfo->LastInterrupt=(unsigned char)intnr;
    if (errorcode)
    {
      cpuinfo->LastInterruptHasErrorcode=1;
      cpuinfo->LastInterruptErrorcode=(WORD)stack[16];
    }
    cpuinfo->LastInterruptHasErrorcode=0;

    cpuinfo->OnInterrupt.RIP=0; //clear exception handler
    cpuinfo->OnInterrupt.RBP=0;
    cpuinfo->OnInterrupt.RSP=0;

    sendstringf("changed rip(was %6 is now %6)\n\r", oldrip, stack[16+errorcode]);
    sendstringf("rflags upon return is %x\n\r", stack[16+2+errorcode]);

    sendstring("returning now\n");

    return errorcode;
  }
  sendstring("not expected\n\r");

  ddDrawRectangle(0,DDVerticalResolution-100,100,100,0xff0000);



  sendstring("Status:\n\r");
  sendstringf("r15=%6\n\r",stack[0]);
  sendstringf("r14=%6\n\r",stack[1]);
  sendstringf("r13=%6\n\r",stack[2]);
  sendstringf("r12=%6\n\r",stack[3]);
  sendstringf("r11=%6\n\r",stack[4]);
  sendstringf("r10=%6\n\r",stack[5]);
  sendstringf("r9=%6\n\r",stack[6]);
  sendstringf("r8=%6\n\r",stack[7]);
  sendstringf("rbp=%6\n\r",stack[8]);
  sendstringf("rsi=%6\n\r",stack[9]);
  sendstringf("rdi=%6\n\r",stack[10]);
  sendstringf("rdx=%6\n\r",stack[11]);
  sendstringf("rcx=%6\n\r",stack[12]);
  sendstringf("rbx=%6\n\r",stack[13]);
  sendstringf("rax=%6\n\r",stack[14]);
  sendstringf("intnr=%6\n\r",stack[15]);
  sendstringf("stack[16]=%6\n\r",stack[16]);
  sendstringf("stack[17]=%6\n\r",stack[17]);
  sendstringf("stack[18]=%6\n\r",stack[18]);
  sendstringf("stack[19]=%6\n\r",stack[19]);
  sendstringf("--------------\n\r");

  sendstringf("DR6=%6\n\r",getDR6());
  if (intnr==14)
  {
    sendstringf("DR2=%6\n\r",getDR2());
  }
  //16=errorcode/eip
  //17=eip/cs
  //18=cs/eflags

	sendstringf("eip=%6\n\r",stack[16+errorcode]);
	sendstringf("cs=%6\n\r",stack[17+errorcode]);




  sendDissectedFlags(rflags);

  sendstringf("Trying to disassemble caller instruction\n\r");

  int found=0;
  unsigned int used=0;
  unsigned int start=0;
  _DecodedInst disassembled[22];

  while (start<30)
  {
    distorm_decode(stack[16+errorcode]-30+start, (unsigned char *)(stack[16+errorcode]-30+start), 120, Decode64Bits, disassembled, 22, &used);

    for (i=0; (unsigned)i<used; i++)
      if (disassembled[i].offset==stack[16+errorcode])
      {
        found=1;
        break;
      }

    if (found)
      break;
    start++;
  }

  for (i=0; (unsigned)i<used; i++)
  {
    if (disassembled[i].offset==stack[16+errorcode])
    {
      sendstringf(">>");
    }

    sendstringf("%x : %s - %s %s\n\r",
                    disassembled[i].offset,
                    disassembled[i].instructionHex.p,
                    disassembled[i].mnemonic.p,
                    disassembled[i].operands.p);
  }



  autostart=0;
  setDR7(originalDR7);


  sendstring("End of interrupt\n\r");


#ifdef DEBUGINTHANDLER

  csEnter(&cinthandlerMenuCS);
 // inthandleroverride=1;
  IntHandlerDebug=1;


  unsigned char key;
  while (1)
  {
    sendstring("----------------------------\n\r");
    sendstring("Interrupt handler debug menu\n\r");
    sendstring("----------------------------\n\r");
    sendstring("1: Exit from interrupt\n\r");
    sendstring("2: Check CRC values\n\r");
    sendstring("3: Get vmstate\n\r");
    sendstring("p: Previous vmstates\n\r");



    key=waitforchar();
    if (key==0xff) //serial port borked
      key='1';

    switch (key)
    {
      case '1':
        sendstring("Exiting from interrupt\n\r");
        IntHandlerDebug=0;
        csLeave(&cinthandlerMenuCS);
        return errorcode;

      case '2':
        CheckCRCValues();
        break;

      case '3':
        sendvmstate(cpuinfo, NULL);
        break;

      case 'p':
    	displayPreviousStates();
        break;
    }
  }



#else
	return errorcode;
#endif
}

void startNextCPU(void)
{
  //setup a stack for the first AP cpu
  nextstack=(QWORD)malloc2(4096*16);
  markPageAsNotReadable((void *)nextstack);  //when the thread tries to allocate more than it can it'll cause a pagefault instead of fucking with other memory

  nextstack=(QWORD)nextstack+(4096*16)-16;

  sendstringf("startNextCPU. nextstack=%6\n", nextstack);

  asm volatile ("": : :"memory");
  initcs=0; //let the next cpu pass
  asm volatile ("": : :"memory");
}


void CheckCRCValues(void)
{
  unsigned int newcrc;

  sendstringf("Original VMM crc = %x\n\r",originalVMMcrc);
  newcrc=generateCRC((void *)vmxloop,0x2a000);
  sendstringf("Current  VMM crc = %x\n\r",newcrc);
  if (originalVMMcrc!=newcrc)
  {
    sendstring("!!!!!!!!!!MISMATCH!!!!!!!!!!\n\r");
  }


  return;
}

void setints(void)
{
  IDT tidt;
  int i;

  tidt.wLimit=16*256;
  tidt.vector=intvector;

  if (intvector==NULL)
  {
    sendstring("setints was called too early");
    ddDrawRectangle(0,DDVerticalResolution-100,100,100,0xff0000);
    while(1);
  }


  zeromemory(intvector, 256*sizeof(INT_VECTOR));
  for (i=0; i<256; i++)
  {
    intvector[i].wSelector=80;
    intvector[i].bUnused=0;
    intvector[i].bAccess=0x8e; //10001110
  }

  intvector[8].bUnused=1; //double fault
  intvector[11].bUnused=1; //segment fault
  intvector[12].bUnused=1; //stack fault

  SETINT(0);
  SETINT(1);
  SETINT(2);
  SETINT(3);
  SETINT(4);
  SETINT(5);
  SETINT(6);
  SETINT(7);
  SETINT(8);
  SETINT(9);
  SETINT(10);
  SETINT(11);
  SETINT(12);
  SETINT(13);
  SETINT(14);
  SETINT(15);
  SETINT(16);
  SETINT(17);
  SETINT(18);
  SETINT(19);
  SETINT(20);
  SETINT(21);
  SETINT(22);
  SETINT(23);
  SETINT(24);
  SETINT(25);
  SETINT(26);
  SETINT(27);
  SETINT(28);
  SETINT(29);
  SETINT(30);
  SETINT(31);
  SETINT(32);
  SETINT(33);
  SETINT(34);
  SETINT(35);
  SETINT(36);
  SETINT(37);
  SETINT(38);
  SETINT(39);
  SETINT(40);
  SETINT(41);
  SETINT(42);
  SETINT(43);
  SETINT(44);
  SETINT(45);
  SETINT(46);
  SETINT(47);
  SETINT(48);
  SETINT(49);
  SETINT(50);
  SETINT(51);
  SETINT(52);
  SETINT(53);
  SETINT(54);
  SETINT(55);
  SETINT(56);
  SETINT(57);
  SETINT(58);
  SETINT(59);
  SETINT(60);
  SETINT(61);
  SETINT(62);
  SETINT(63);
  SETINT(64);
  SETINT(65);
  SETINT(66);
  SETINT(67);
  SETINT(68);
  SETINT(69);
  SETINT(70);
  SETINT(71);
  SETINT(72);
  SETINT(73);
  SETINT(74);
  SETINT(75);
  SETINT(76);
  SETINT(77);
  SETINT(78);
  SETINT(79);
  SETINT(80);
  SETINT(81);
  SETINT(82);
  SETINT(83);
  SETINT(84);
  SETINT(85);
  SETINT(86);
  SETINT(87);
  SETINT(88);
  SETINT(89);
  SETINT(90);
  SETINT(91);
  SETINT(92);
  SETINT(93);
  SETINT(94);
  SETINT(95);
  SETINT(96);
  SETINT(97);
  SETINT(98);
  SETINT(99);
  SETINT(100);
  SETINT(101);
  SETINT(102);
  SETINT(103);
  SETINT(104);
  SETINT(105);
  SETINT(106);
  SETINT(107);
  SETINT(108);
  SETINT(109);
  SETINT(110);
  SETINT(111);
  SETINT(112);
  SETINT(113);
  SETINT(114);
  SETINT(115);
  SETINT(116);
  SETINT(117);
  SETINT(118);
  SETINT(119);
  SETINT(120);
  SETINT(121);
  SETINT(122);
  SETINT(123);
  SETINT(124);
  SETINT(125);
  SETINT(126);
  SETINT(127);
  SETINT(128);
  SETINT(129);
  SETINT(130);
  SETINT(131);
  SETINT(132);
  SETINT(133);
  SETINT(134);
  SETINT(135);
  SETINT(136);
  SETINT(137);
  SETINT(138);
  SETINT(139);
  SETINT(140);
  SETINT(141);
  SETINT(142);
  SETINT(143);
  SETINT(144);
  SETINT(145);
  SETINT(146);
  SETINT(147);
  SETINT(148);
  SETINT(149);
  SETINT(150);
  SETINT(151);
  SETINT(152);
  SETINT(153);
  SETINT(154);
  SETINT(155);
  SETINT(156);
  SETINT(157);
  SETINT(158);
  SETINT(159);
  SETINT(160);
  SETINT(161);
  SETINT(162);
  SETINT(163);
  SETINT(164);
  SETINT(165);
  SETINT(166);
  SETINT(167);
  SETINT(168);
  SETINT(169);
  SETINT(170);
  SETINT(171);
  SETINT(172);
  SETINT(173);
  SETINT(174);
  SETINT(175);
  SETINT(176);
  SETINT(177);
  SETINT(178);
  SETINT(179);
  SETINT(180);
  SETINT(181);
  SETINT(182);
  SETINT(183);
  SETINT(184);
  SETINT(185);
  SETINT(186);
  SETINT(187);
  SETINT(188);
  SETINT(189);
  SETINT(190);
  SETINT(191);
  SETINT(192);
  SETINT(193);
  SETINT(194);
  SETINT(195);
  SETINT(196);
  SETINT(197);
  SETINT(198);
  SETINT(199);
  SETINT(200);
  SETINT(201);
  SETINT(202);
  SETINT(203);
  SETINT(204);
  SETINT(205);
  SETINT(206);
  SETINT(207);
  SETINT(208);
  SETINT(209);
  SETINT(210);
  SETINT(211);
  SETINT(212);
  SETINT(213);
  SETINT(214);
  SETINT(215);
  SETINT(216);
  SETINT(217);
  SETINT(218);
  SETINT(219);
  SETINT(220);
  SETINT(221);
  SETINT(222);
  SETINT(223);
  SETINT(224);
  SETINT(225);
  SETINT(226);
  SETINT(227);
  SETINT(228);
  SETINT(229);
  SETINT(230);
  SETINT(231);
  SETINT(232);
  SETINT(233);
  SETINT(234);
  SETINT(235);
  SETINT(236);
  SETINT(237);
  SETINT(238);
  SETINT(239);
  SETINT(240);
  SETINT(241);
  SETINT(242);
  SETINT(243);
  SETINT(244);
  SETINT(245);
  SETINT(246);
  SETINT(247);
  SETINT(248);
  SETINT(249);
  SETINT(250);
  SETINT(251);
  SETINT(252);
  SETINT(253);
  SETINT(254);
  SETINT(255);

  cLIDT(&tidt);
}


void vmm_entry2_hlt(pcpuinfo currentcpuinfo)
{
  UINT64 a,b,c,d;
  if (currentcpuinfo)
    sendstringf("CPU %d : Terminating...\n\r",currentcpuinfo->cpunr);

  while (1)
  {
    ddDrawRectangle(0,DDVerticalResolution-100,100,100,0xff0000);

    if (currentcpuinfo)
      currentcpuinfo->active=0;
    a=1;
    _cpuid(&a,&b,&c,&d);  //always serialize after writing to a structure read by a different cpu
    __asm("hlt\n\r");
  }
}


void setupFSBase(void *fsbase)
{
  writeMSR(IA32_FS_BASE_MSR, (UINT64)fsbase);
}

volatile int inflooponerror=1;
volatile int initializedCPUCount; //assume the first (main) cpu managed to start up

void vmm_entry2(void)
//Entry for application processors
//Memory manager has been initialized and GDT/IDT copies have been made
{
  unsigned int cpunr;
  if (AP_Terminate)
  {
    sendstringf("AP_Terminate!=0\n");
    startNextCPU();
    vmm_entry2_hlt(NULL);
  }

  if ((needtospawnApplicationProcessors>1) || (needtospawnApplicationProcessors<0))
  {
    sendstringf("memory corruption\n");

    while (inflooponerror) ;
  }


  //debug code on my 8 core cpu:
  //if (initializedCPUCount>=8)
  //{
  //  sendstringf("More cpu's than expected\n");
  //  while (inflooponerror);
  //}


  //setup the GDT and IDT
  setGDT((UINT64)GDT_BASE, GDT_SIZE);
  setIDT((UINT64)intvector, 16*256);

  //cLIDT(&intvector);

  //sendstringf("Welcome to a extra cpu (cpunr=%d)\n",cpucount);
  cpunr=initializedCPUCount;
  sendstringf("Setting up cpunr=%d\n",cpunr);


  initializedCPUCount++;

  if (!loadedOS)
    cpucount++; //cpucount is known, don't increase it

  pcpuinfo cpuinfo=malloc2(sizeof(tcpuinfo)<4096?4096:sizeof(tcpuinfo)<4096);

  zeromemory(cpuinfo, sizeof(tcpuinfo));
  cpuinfo->active=1;
  cpuinfo->cpunr=cpunr;
  cpuinfo->apicid=getAPICID();

  cpuinfo->TSbase=0;
  cpuinfo->TSlimit=0xffff;
  cpuinfo->TSsegment=0;
  cpuinfo->TSaccessRights=0x8b;
  cpuinfo->self=cpuinfo;

  lastaddedcpuinfo->next=cpuinfo;
  lastaddedcpuinfo=cpuinfo;

  displayline("%d: New CPU CORE. CPUNR=%d APICID=%d (cpuinfo struct at : %p rsp=%x)\n",cpunr, cpuinfo->cpunr, cpuinfo->apicid, cpuinfo, getRSP());


  setupFSBase((void *)cpuinfo);

  sendstringf("%d: launching next CPU\n", cpunr);
  startNextCPU(); //put at start for async

  sendstringf("%d: Waiting till AP_Launch is not 0\n", cpunr);

  while (AP_Launch==0)
  {
    resync();
    if (AP_Terminate==1)
      vmm_entry2_hlt(cpuinfo);
  }

  if (AP_Terminate==1)
  {
    startNextCPU();
    vmm_entry2_hlt(cpuinfo);
  }


  startvmx(cpuinfo);

   // while (1); //debug

  displayline("CPU CORE %d: entering VMX mode\n",cpunr);

  sendstringf("Application cpu returned from startvmx\n\r");

  vmm_entry2_hlt(cpuinfo);
  while (1);
}





void vmm_entry(void)
{
  //make sure WP is on
  enableserial();

  sendstringf("vmm_entry\n");

  setCR0(getCR0() | CR0_WP);

  if (isAP)
  {
    vmm_entry2();
    sendstringf("vmm_entry2 has PHAILED!!!!");
    while (1) outportb(0x80,0xc7);
  }
  isAP=1; //all other entries will be an AP

  initializedCPUCount=1; //I managed to run this at least...

  int i,k;
  UINT64 a,b,c,d;
  pcpuinfo cpuinfo;


  //stack has been properly setup, so lets allow other cpu's to launch as well
  InitCommon();

  Password1=0x76543210; //later to be filled in by user, sector on disk, or at compile time
  Password2=0xfedcba98;

  /*version 1 was the 32-bit only version,
   * 2 added 64-bit,
   * 3 had a revised int1 redirect option,
   * 4 has major bugfixes,
   * 5=more fixes and some basic device recog,
   * 6=Even more compatibility fixes,
   * rm emu, and new vmcalls,
   * 7=driver loading ,
   * 8=amd support,
   * 9 memory usage decrease and some fixes for newer systems,
   * 10=xsaves (win10)
   * 11=new memory manager , dynamic cpu initialization, UEFI boot support, EPT, unrestricted support, and other new features
   * 12=vpid
   * 13=basic TSC emulation
   * 14=properly emulate debug step
   */
  dbvmversion=14;
  int1redirection=1; //redirect to int vector 1 (might change this to the perfcounter interrupt in the future so I don't have to deal with interrupt prologue/epilogue)
  int3redirection=3;
  int14redirection=14;

  //get max physical address
  QWORD rax=0x80000008;
  QWORD rbx=0;
  QWORD rcx=0;
  QWORD rdx=0;
  _cpuid(&rax, &rbx, &rcx, &rdx);
  MAXPHYADDR=rax & 0xff;

  MAXPHYADDRMASK=0xFFFFFFFFFFFFFFFFULL;
  MAXPHYADDRMASK=MAXPHYADDRMASK >> MAXPHYADDR; //if MAXPHYADDR==36 then MAXPHYADDRMASK=0x000000000fffffff
  MAXPHYADDRMASK=~(MAXPHYADDRMASK << MAXPHYADDR); //<< 36 = 0xfffffff000000000 .  after inverse : 0x0000000fffffffff
  MAXPHYADDRMASKPB=MAXPHYADDRMASK & 0xfffffffffffff000ULL; //0x0000000ffffff000

  sendstringf("MAXPHYADDR=%d", MAXPHYADDR);
  sendstringf("MAXPHYADDRMASK=%6", MAXPHYADDRMASK);
  sendstringf("MAXPHYADDRMASKPB=%6", MAXPHYADDRMASK);





  //enableserial();




  sendstringf("If you see this that means that the transition from unpaged to paged was a success\n\r");
  sendstringf("loadedOS=%6\n",loadedOS);

  currentdisplayline=7;


  displayline("BOOT CPU CORE initializing\n");

  displayline("CR3=%6\n", getCR3());
  displayline("pagedirlvl4=%6\n",(UINT64)pagedirlvl4);
  displayline("&pagedirlvl4=%6\n",(UINT64)&pagedirlvl4);
  displayline("vmmstart=%6 (this is virtual address 00400000)\n",(UINT64)vmmstart);

  //displayline("press any key to continue\n");


  //initialize

  sendstring("Welcome to Dark Byte\'s Virtual Machine Manager\n\r");
  sendstringf("pagedirlvl4=%6\n\r",(unsigned long long)pagedirlvl4);

  sendstring("Initializing MM\n\r");
  InitializeMM((QWORD)pagedirlvl4+4096);
  sendstring("Initialized MM\n\r");
  /*
   * POST INIT
   */


  cpucount=1;
  cpuinfo=malloc2(sizeof(tcpuinfo)<4096?4096:sizeof(tcpuinfo));
  sendstringf("allocated cpuinfo at %6\n\r", cpuinfo);
  zeromemory(cpuinfo,sizeof(tcpuinfo));
  cpuinfo->active=1;
  cpuinfo->cpunr=0;
  cpuinfo->apicid=getAPICID();
  cpuinfo->isboot=1;
  cpuinfo->self=cpuinfo;



  setupFSBase((void*)cpuinfo);


  //debug info
  firstcpuinfo=cpuinfo;
  lastaddedcpuinfo=cpuinfo;

#if DISPLAYDEBUG==1
  initialize_displaydebuglogs();
#endif


  sendstringf("initialized cpuinfo at %6\n\r", cpuinfo);


  //IA32_APIC_BASE=(unsigned long long)readMSR(0x1b);

  IA32_APIC_BASE=(QWORD)mapPhysicalMemory((unsigned long long)readMSR(0x1b) & 0xfffffffffffff000ULL, 4096);
  sendstringf("IA32_APIC_BASE=%6\n\r",IA32_APIC_BASE);

  APIC_ID=IA32_APIC_BASE+APIC_ID_OFFSET;
  APIC_SVR=IA32_APIC_BASE+APIC_SVR_OFFSET;

  SetPageToWriteThrough((void*)IA32_APIC_BASE);




  displayline("IA32_APIC_BASE=%6\n\r",IA32_APIC_BASE);
  sendstringf("IA32_APIC_BASE=%6\n\r",IA32_APIC_BASE);
  sendstringf("\tLocal APIC base=%6\n\r",IA32_APIC_BASE & 0xfffff000);
  sendstringf("\tAPIC global enable/disable=%d\n\r",(IA32_APIC_BASE >> 11) & 1);
  sendstringf("\tBSP=%d\n\r",(IA32_APIC_BASE >> 8) & 1);

  a=1;
  _cpuid(&a,&b,&c,&d);
  displayline("CPUID.1: %8, %8, %8, %8\n",a,b,c,d);

  cpu_stepping=a & 0xf;
  cpu_model=(a >> 4) & 0xf;
  cpu_familyID=(a >> 8) & 0xf;
  cpu_type=(a >> 12) & 0x3;
  cpu_ext_modelID=(a >> 16) & 0xf;
  cpu_ext_familyID=(a >> 20) & 0xff;


  cpu_model=cpu_model + (cpu_ext_modelID << 4);
  cpu_familyID=cpu_familyID + (cpu_ext_familyID << 4);


  //if (0)
  if (1) //((d & (1<<28))>0) //this doesn't work in vmware, so find a different method
  {
    QWORD entrypage=0x30000;
    unsigned long long initialcount;
    unsigned int foundcpus;
    sendstring("Multi processor supported\n\r");
    sendstring("Launching application cpu's\n");


    //displaystring("Multi processor supported\n");
    displayline("Launching other cpu cores if present\n");
    sendstring("Starting other cpu's\n\r");


    APStartsInSIPI=1;

    if ((needtospawnApplicationProcessors>1) || (needtospawnApplicationProcessors<0))
    {
      sendstringf("memory corruption\n");
      volatile int debug=1;
      while (debug) ;
    }

    if (loadedOS)
    {
      sendstringf("mapping loadedOS (%6)...\n", loadedOS);

      POriginalState original=(POriginalState)mapPhysicalMemory(loadedOS, sizeof(OriginalState));
      sendstringf("Success. It has been mapped at virtual address %6\n",original);

      entrypage=original->APEntryPage;

      sendstringf("original->cpucount=%d\n", original->cpucount);
      if (original->cpucount>1000)
      {
        sendstringf("More than 1000 cpu\'s are currently not supported\n");
        ddDrawRectangle(0,DDVerticalResolution-100,100,100,0xff0000);
        while (1);
      }

      if (original->cpucount)
      {
        needtospawnApplicationProcessors=0;
        foundcpus=original->cpucount;
        APStartsInSIPI=0; //AP should start according to the original state
      }

      if (original->FrameBufferBase)
      {
        DDFrameBufferBase=original->FrameBufferBase;
        DDFrameBufferSize=original->FrameBufferSize;
        DDHorizontalResolution=original->HorizontalResolution;
        DDVerticalResolution=original->VerticalResolution;
        DDPixelsPerScanLine=original->PixelsPerScanLine;

        if (DDFrameBufferBase)
        {
          char c=0;
          /* sendstring("Before mapping of the framebuffer\n");
          while (c==0)
          {
            c=waitforchar();
          }
          sendstring("Mapping framebuffer\n");*/

          //DDFrameBuffer=mapPhysicalMemoryGlobal(0x90000000, 8294400);
          //DDFrameBuffer=(unsigned char *)mapPhysicalMemory(0x90000000, 8294400);


          DDFrameBuffer=(unsigned char *)mapPhysicalMemoryGlobal(DDFrameBufferBase, DDFrameBufferSize);
          if (DDFrameBuffer==NULL)
          {
            sendstring("Failure mapping memory");
          }
          else
          {



         /* SetPageToWriteThrough(DDFrameBuffer);*/
            ddDrawRectangle(0,0,DDHorizontalResolution, DDVerticalResolution,0x00ff00);
        /*    ddDrawRectangle(0,0,100,100,0xff00ff);
            ddDrawRectangle(DDHorizontalResolution-100,0,100,100,0xff0000);
            ddDrawRectangle(0,DDVerticalResolution-100,100,100,0x0000ff);
            ddDrawRectangle(DDHorizontalResolution-100,DDVerticalResolution-100,100,100,0xffffff);*/

/*
            unsigned int pi;
            for (pi=0; pi<DDFrameBufferSize; pi++)
            {
              DDFrameBuffer[pi]=0x30; //*(unsigned char *)((QWORD)0x00400000+(QWORD)pi);
            }*/
          }

        }
      }


      //while (1) ;
      unmapPhysicalMemory(original, sizeof(OriginalState));
    }

    if (needtospawnApplicationProcessors) //e.g UEFI boot with missing mpsupport
    {
      sendstringf("needtospawnApplicationProcessors!=0\n");
#ifndef NOMP

      BOOT_ID=apic_getBootID();

      //setup some info so that the AP cpu can find  this
      APBootVar_CR3=getCR3();

      void *GDTbase=(void *)getGDTbase();
      int GDTsize=getGDTsize();
      APBootVar_Size=GDTsize;

      if (GDTsize>192)
      {
        sendstringf("Update the AP boot GDT section to be bigger than 192 bytes");
      }
      memcpy(APBootVar_GDT, GDTbase, GDTsize);

      APBootVar_GDT[2 ]=0x00cf9b000000ffffULL;  //24: 32-bit code
      APBootVar_GDT[2 ]|=(entrypage >> 8) << 24;
      //with 0x5e000 or it with 0x05e0

      sendstringf("vmmentrycount before launch=%d\n", vmmentrycount);
      foundcpus=initAPcpus(entrypage);

      sendstringf("foundcpus=%d cpucount=%d. Waiting till cpucount==foundcpus, or timeout\n",foundcpus, cpucount);

      QWORD timeout=2000000000ULL;

      for (i=0; i<3; i++)
      {
        initialcount=_rdtsc();
        while ((_rdtsc()-initialcount) < timeout)
        {
          //sendstringf("cpucount=%d foundcpus=%d\n\r", cpucount, foundcpus);

          if (vmmentrycount>=foundcpus)
            break;

          _pause();
        }
        displayline(".");
        if (vmmentrycount>=foundcpus)
          break;
      }

      if (i>=3)
        displayline("Timeout\n");
      else
        displayline("\n");

    }
    else
      AP_Launch=1; //no need to let the others wait. the launcher will decide when to load

    displayline("Wait done. Cpu's found : %d (expected %d)\n",vmmentrycount, foundcpus);
    sendstringf("vmmentrycount after launch=%d\n", vmmentrycount);

    //the other CPU's should now be waiting in the spinlock at the start of dbvm
#endif
  }

  //copy GDT and IDT to VMM memory
  GDT_BASE=malloc(4096);
  GDT_SIZE=4096; //getGDTsize();

  if (GDT_BASE==NULL)
  {
    sendstring("Memory allocation failed\n");
    ddDrawRectangle(0,DDVerticalResolution-100,100,100,0xff0000);
    while (1) outportb(0x80,0xc8);
  }

  sendstringf("Allocated GDT_BASE %6\n", GDT_BASE);

  {
    void *GDTbase=(void *)getGDTbase();
    int GDTsize=getGDTsize();

    sendstringf("getGDTbase=%p, getGDTsize=%d\n",GDTbase,GDTsize );

    copymem(GDT_BASE,GDTbase,GDTsize);
  }
  sendstringf("Allocated and copied GDT to %x\n\r",(UINT64)GDT_BASE);
  setGDT((UINT64)GDT_BASE, 4096);

  {
    //set the GDT to the way I like it (loaders could fuck this up)
    QWORD *g=(QWORD *)GDT_BASE;
    g[0 ]=0;            //0 :
    g[1 ]=0x00cf92000000ffffULL;  //8 : 32-bit data
    g[2 ]=0x00cf96000000ffffULL;  //16: test, stack, failed, unused
    g[3 ]=0x00cf9b000000ffffULL;  //24: 32-bit code
    g[4 ]=0x00009a000000ffffULL;  //32: 16-bit code
    g[5 ]=0x000092000000ffffULL;  //40: 16-bit data
    g[6 ]=0x00009a030000ffffULL;  //48: 16-bit code, starting at 0x30000
    g[7 ]=0;            //56: 32-bit task
    g[8 ]=0;            //64: 64-bit task
    g[9 ]=0;            //72:  ^   ^   ^
    g[10]=0x00af9b000000ffffULL;  //80: 64-bit code
    g[11]=0x00cf9b000000ffffULL;  //88: 32-bit code compat mode
    g[12]=0;            //96: 64-bit tss descriptor (2)
    g[13]=0;            //104: ^   ^   ^
  }

  //now replace the old IDT with a new one
  intvector=malloc(sizeof(INT_VECTOR)*256);
  zeromemory(intvector,sizeof(INT_VECTOR)*256);
  sendstringf("Allocated intvector at %6\n\r",(unsigned long long)intvector);

  setints();
  sendstring("after setints()\n");

  i=0;
  setDR0((QWORD)((volatile void *)&&BPTest));
  setDR6(0xffff0ff0);
  setDR7(getDR7() | (1<<0));
  displayline("Going to execute test breakpoint\n");

  cpuinfo->OnInterrupt.RSP=getRSP();
  cpuinfo->OnInterrupt.RBP=getRBP();
  cpuinfo->OnInterrupt.RIP=(QWORD)((volatile void *)&&AfterBPTest);
  asm volatile ("": : :"memory");
BPTest:
  i=1;
  sendstring("<<---------------WRONG!!! BPTest got executed...(ok if a jtag debugger is present)\n");
  asm volatile ("": : :"memory");
AfterBPTest:
  if (i==0)
    sendstring("BPTest successfull\n");
  else
    sendstring(":(\n");

  cpuinfo->OnInterrupt.RSP=0;
  cpuinfo->OnInterrupt.RBP=0;
  cpuinfo->OnInterrupt.RIP=0;



  sendstringf("Letting the first AP cpu go through\n");
  startNextCPU();



  fakeARD=malloc(4096);
  fakeARD[0].Type=255;
  sendstringf("Allocated fakeARD at %6\n",(unsigned long long)fakeARD);
  sendstringf("That is physical address %6\n", VirtualToPhysical(fakeARD));


  if (!loadedOS)
  {
    sendstring("Copying ARD from 80000 to ARD location");
    copymem((void *)fakeARD,(void *)0x80000,4096); //that should be enough
  }

  //ARD Setup
  //The ARD is used for old boot mechanisms. This way DBVM can tell the guest which physical pages are 'reserved' by the system and should not be used/overwritten
  //In loadedOS mode DBVM makes use of built-in memory allocation systems so not needed

  sendstring("Calling initARDcount()\n");
  initARDcount();
  sendstring("Calling sendARD()\n");
  sendARD();

  sendstring("after sendARD()\n");



  // alloc VirtualMachineTSS_V8086
  VirtualMachineTSS_V8086=malloc(3*4096);
  zeromemory(VirtualMachineTSS_V8086,3*4096);

  RealmodeRing0Stack=malloc(4096); //(not even 1 KB used, but let's waste some mem)


  //configure ffpage

  //create a page filled with 0xff (for faking non present memory access)
  ffpage=malloc(4096);
  for (i=0; i<4096; i++)
    ffpage[i]=0xce;

  sendstringf("Physical address of ffpage=%6\n\r",(UINT64)VirtualToPhysical(ffpage));


  //create a pagetable with only 0xff (2MB 0xff)
  ffpagetable=malloc(4096);
  zeromemory(ffpagetable,4096);
  for (i=0; i<4096/8; i++)
  {
    *(QWORD*)(&ffpagetable[i])=VirtualToPhysical(ffpage);
    ffpagetable[i].P=1;
    ffpagetable[i].RW=0;
  }

  sendstringf("Physical address of ffpagetable=%6\n\r",(UINT64)VirtualToPhysical((void *)ffpagetable));

  //create a pagedir where all entries point to the ffpagetable
  ffpagedir=malloc(4096);
  zeromemory(ffpagedir,4096);
  for (i=0; i<4096/8; i++)
  {
    *(QWORD*)(&ffpagedir[i])=VirtualToPhysical((void *)ffpagetable);
    ffpagedir[i].P=1;
    ffpagedir[i].RW=0;
    ffpagedir[i].PS=0;
  }

  sendstringf("Physical address of ffpagedir=%6\n\r",(UINT64)VirtualToPhysical((void *)ffpagedir));

  __asm("mov %cr3,%rax\n  mov %rax,%cr3\n");

  displayline("emulated virtual memory has been configured\n");

  displayline("Paging:\n");
  displayline("0x00000000 is at %6\n", (UINT64)VirtualToPhysical((void *)0));
  displayline("0x00200000 is at %6\n", (UINT64)VirtualToPhysical((void *)0x00200000));
  displayline("0x00400000 is at %6\n", (UINT64)VirtualToPhysical((void *)0x00400000));
  displayline("0x00600000 is at %6\n", (UINT64)VirtualToPhysical((void *)0x00600000));



  //setup nonpagedEmulationPagedir


  displayline("Calling hascpuid()\n");
	if (hascpuid())
	{
		char *t;
		a=0;
		b=0;
		c=0;
		d=0;
		_cpuid(&a,&b,&c,&d);
		sendstringf("Your comp supports cpuid! (%d , %x %x %x )\n\r",a,b,d,c);

		t=(char *)&b;
		sendstringf("Max basicid=%x\n\r",a);
		sendstringf("%c%c%c%c",t[0],t[1],t[2],t[3]);


		t=(char *)&d;
		sendstringf("%c%c%c%c",t[0],t[1],t[2],t[3]);


		t=(char *)&c;
		sendstringf("%c%c%c%c\n\r",t[0],t[1],t[2],t[3]);



    if ((b==0x68747541) && (d==0x69746e65) && (c==0x444d4163))
    {
      isAMD=1;
      AMD_hasDecodeAssists=0;
      sendstring("This is an AMD system. going to use the AMD virtualization tech\n\r");
    }
    else
      isAMD=0;


    //a=0x80000000; _cpuid(&a,&b,&c,&d);
    //if (!(a & 0x80000000))
    {
      unsigned int j;

     // sendstring("\n\r\n\rBranch string=");
      displayline("Branch string=");

      for (j=0x80000002; j<=0x80000004; j++)
      {
        a=j;
        _cpuid(&a,&b,&c,&d);

        t=(char *)&a;
        for (k=0; k<4; k++)
          if (t[k]<32)
            t[k]=' ';
        //sendstringf("%c%c%c%c",t[0],t[1],t[2],t[3]);
        displayline("%c%c%c%c",t[0],t[1],t[2],t[3]);

        t=(char *)&b;
        for (k=0; k<4; k++)
          if (t[k]<32)
            t[k]=' ';
        //sendstringf("%c%c%c%c",t[0],t[1],t[2],t[3]);
        displayline("%c%c%c%c",t[0],t[1],t[2],t[3]);

        t=(char *)&c;
        for (k=0; k<4; k++)
          if (t[k]<32)
            t[k]=' ';
      //  sendstringf("%c%c%c%c",t[0],t[1],t[2],t[3]);
        displayline("%c%c%c%c",t[0],t[1],t[2],t[3]);

        t=(char *)&d;
        for (k=0; k<4; k++)
          if (t[k]<32)
            t[k]=' ';
     //   sendstringf("%c%c%c%c",t[0],t[1],t[2],t[3]);
        displayline("%c%c%c%c",t[0],t[1],t[2],t[3]);
      }
    //  sendstringf("\n\r");
      displayline(" \n");

    }


	}
	else
  {
		sendstring("Your crappy comp doesn\'t support cpuid\n\r");
    displayline("Your system does not support CPUID\n");
  }

  cpuinfo->TSbase=0;
  cpuinfo->TSlimit=0xffff;
  cpuinfo->TSsegment=0;
  cpuinfo->TSaccessRights=0x8b;

  sendstringf("Setting up idttable and jumptable\n\r");

  jumptable=malloc(4096);
  idttable32=malloc(4096);

  sendstringf("jumptable allocated at %x (%6)\n\r",(UINT64)jumptable, VirtualToPhysical(jumptable));
  sendstringf("idttable32 allocated at %x (%6)\n\r",(UINT64)idttable32, VirtualToPhysical(idttable32));

  //fill jumptable and IDT
  PINT_VECTOR32 idt32=(PINT_VECTOR32)idttable32;
  UINT64 pa=VirtualToPhysical(jumptable);
  UINT64 inthandler32address=(UINT64)VirtualToPhysical(&inthandler_32);

  unsigned char *jumptablepc;
  jumptablepc=(unsigned char *)jumptable;
  for (i=0; i<256; i++, pa+=10)
  {
    //push i
    jumptablepc[i*10]=0x68; //push
    *(DWORD *)&jumptablepc[i*10+1]=i;

    //jmp inthandler_32
    jumptablepc[i*10+5]=0xe9;
    *(DWORD *)(&jumptablepc[i*10+6])=inthandler32address-(pa+10); //from (current offset+5(including push 7)) to inthandler_32
    idt32[i].bAccess=0x8e;
    idt32[i].bUnused=0;
    idt32[i].wHighOffset=pa >> 16;
    idt32[i].wLowOffset=pa & 0xffff;
    idt32[i].wSelector=24;

    //sendstringf("idt32[%x]=%x\n\r",i, *(UINT64*)&idt32[i]);
  }

  sendstring("setting up gdt entry at offset 0x64 as virtual8086 task\n\r");
  PGDT_ENTRY currentgdt=(PGDT_ENTRY)getGDTbase();


  sendstringf("currentgdt is %x (limit=%x)\n\r",(UINT64)currentgdt, getGDTsize());
  ULONG length=(ULONG)sizeof(TSS)+32+8192+1;


  currentgdt[8].Limit0_15=length;
  currentgdt[8].Base0_23=(QWORD)VirtualMachineTSS_V8086;
  currentgdt[8].Type=0x9;
  currentgdt[8].NotSystem=0;
  currentgdt[8].DPL=3;
  currentgdt[8].P=1;
  currentgdt[8].Limit16_19=length >> 16;
  currentgdt[8].AVL=1;
  currentgdt[8].L=0;
  currentgdt[8].B_D=0;
  currentgdt[8].G=0;
  currentgdt[8].Base24_31=(QWORD)VirtualMachineTSS_V8086 >> 24;
  *(QWORD*)&currentgdt[9]=((QWORD)VirtualMachineTSS_V8086) >> 32;

  //setup GDT for realmode jump
  currentgdt[4].Base0_23=0x20000;


  //if (!loadedOS)
  {
    sendstringf("Setting up 64-bit TS and TSS\n\r");

    TSS64 *temp=malloc(4096);

    sendstringf("temp allocated at %x\n", temp);
    ownTSS=temp;
    zeromemory(temp,4096);
    currentgdt=(PGDT_ENTRY)(getGDTbase()+96);

    currentgdt[0].Limit0_15=4096;
    currentgdt[0].Base0_23=(UINT64)temp;
    currentgdt[0].Type=0x9;
    currentgdt[0].NotSystem=0;
    currentgdt[0].DPL=0;
    currentgdt[0].P=1;
    currentgdt[0].Limit16_19=4096 >> 16;
    currentgdt[0].AVL=1;
    currentgdt[0].L=0;
    currentgdt[0].B_D=0;
    currentgdt[0].G=0;
    currentgdt[0].Base24_31=((UINT64)temp) >> 24;
    *(QWORD*)&currentgdt[1]=((UINT64)temp) >> 32;

    temp->IST1=(QWORD)malloc(8192)+8192-0x10; //panic stack

    loadTaskRegister(96);
  }

  displayline("Generating debug information\n\r");
  originalVMMcrc=generateCRC((void*)vmxloop,0x2a000);


  displayline("Virtual machine manager loaded\n");




#ifdef DEBUG
  displayline("Entering menu system\n");
#else
  displayline("Skipping menu system and autostarting VM\n");
#endif

  {
    //mark the region between 0 to 0x00400000 as readonly, if you need to write, map it
    PPDPTE_PAE pml4entry;
    PPDPTE_PAE pagedirpointerentry;
    PPDE_PAE pagedirentry;
    PPTE_PAE pagetableentry;

    VirtualAddressToPageEntries(0, &pml4entry, &pagedirpointerentry, &pagedirentry, &pagetableentry);
    pagedirentry[0].RW=0;
    pagedirentry[1].RW=0;
  }

#if DISPLAYDEBUG==0
  if (needtospawnApplicationProcessors)
#endif
    textmemory=(QWORD)mapPhysicalMemory(0xb8000, 4096); //at least enough for 80*25*2


  InitExports();

  //outportb(0x80,0x10);

  menu2();
  return;
}

//#pragma GCC push_options
//#pragma GCC optimize ("O0")
int testexception(void)
{

  volatile int result=2;


  bochsbp();

  __asm("nop");
  __asm("nop");
  __asm("nop");

  result=readMSRSafe(553);

  //nothing happened
  //result=0;
  displayline("result=%d\n", result);


  return result;
}

void vmcalltest(void)
{
  int dbvmversion;
  dbvmversion=0;

  try
  {
    dbvmversion=vmcalltest_asm();
    sendstringf("dbvm is loaded. Version %x\n", dbvmversion);
  }
  except
  {
    sendstringf("dbvm is not loaded\n");
  }
  tryend
}


//#pragma GCC pop_options



void reboot(int skipAPTerminationWait)
{
  {
    //remapping pagetable entry 0 to 0x00400000 so it's writabe (was marked unwritable after entry)
    PPDPTE_PAE pml4entry;
    PPDPTE_PAE pagedirpointerentry;
    PPDE_PAE pagedirentry;
    PPTE_PAE pagetableentry;

    VirtualAddressToPageEntries(0, &pml4entry, &pagedirpointerentry, &pagedirentry, &pagetableentry);
    pagedirentry[0].RW=1;
    pagedirentry[1].RW=1;
    asm volatile ("": : :"memory");
  }

  //Disable the AP cpu's as on a normal reboot, the memory they are looping in will first be zeroed out (it picks the same memory block)
  AP_Terminate=1; //tells the AP cpu's to stop

  if (skipAPTerminationWait==0) //can be skipped if ran by vmlaunch (the other cpu's will stay active as they are in wait-for-sipi mode)
  {
    startNextCPU(); //just make sure there is none waiting

    int stillactive=1;
    while (stillactive)
    {
      pcpuinfo c=firstcpuinfo->next;
      stillactive=0;
      while (c)
      {
        if ((c->vmxsetup==0) && (c->active)) //not configured to be a VMX, and still active
          stillactive=1;

        c=c->next;
      }
    }
  }

  UINT64 gdtaddress=getGDTbase();  //0x40002 contains the address of the GDT table

  sendstring("Copying gdt to low memory\n\r");
  copymem((void *)0x50000,(void *)(UINT64)gdtaddress,128); //copy gdt to 0x50000

  sendstring("copying movetoreal to 0x2000\n\r");
  copymem((void *)0x20000,(void *)(UINT64)&movetoreal,(UINT64)&vmxstartup_end-(UINT64)&movetoreal);


  sendstring("Calling quickboot\n\r");

  if (skipAPTerminationWait==0xcedead) //PSOD
    *(unsigned char *)0x7c0e=0xff;
  else
    *(unsigned char *)0x7c0e=bootdisk;



  quickboot();
  sendstring("WTF?\n\r");
}


#ifdef DEBUG
#define SHOWFIRSTMENU 1
int showfirstmenu=1;
#else
#define SHOWFIRSTMENU 0
int showfirstmenu=0;
#endif

void menu2(void)
{
  unsigned char key;

  if (isAMD)
    vmcall_instr=vmcall_amd;
  else
    vmcall_instr=vmcall_intel;



  sendstringf("loadedOS=%6\n",loadedOS);


  //*(BYTE *)0x7c0e=0x80;
  bootdisk=0x80;
  while (1)
  {
    clearScreen();
    currentdisplayline=0;

    {
    	vmcb x;
    	int offset;

    	offset=(QWORD)&x.DR6-(QWORD)&x;


        	displayline("DR6=%x\n", offset);


    }

    displayline("Welcome to the DBVM interactive menu\n\n");
    displayline("These are your options:\n");
    displayline("0: Start virtualization\n");
    displayline("1: Keyboard test\n");
    displayline("2: Set disk to startup from (currently %2)\n",bootdisk);
    displayline("3: Disassembler test\n");
    displayline("4: Interrupt test\n");
    displayline("5: Breakpoint test\n");
    displayline("6: Set Redirects with dbvm (only if dbvm is already loaded)\n");
    displayline("7: cr3 fuck test\n");
    displayline("8: PCI enum test (finds db's serial port)\n");
    displayline("9: test input\n");
    displayline("a: test branch profiling\n");
    displayline("b: boot without vm (test state vm would set)\n");
    displayline("c: PSOD test\n");
    displayline("v: control register test\n");
    displayline("e: efer test\n");
    displayline("o: out of memory test\n");

    key=0;
    while (!key)
    {
      if ((!loadedOS) || (showfirstmenu))
      {
#ifdef DELAYEDSERIAL
        if (!useserial)
          key='0';
        else
#endif
        if (loadedOS)
          key=waitforchar();
        else
          key=kbd_getchar();
      }
      else
        key='0';

      while (IntHandlerDebug) ;

      if (key)
      {
        char temps[16];
        displayline("%c\n", key);

        switch (key)
        {
          case '0':
            clearScreen();
            menu();
            break;

          case '1':
            displayline("kdbstatus=%2\n",kbd_getstatus());
            displayline("kdb_getoutputport=%2\n",kdb_getoutputport());
            displayline("kdb_getinputport=%2\n",kdb_getinputport());
            displayline("kdb_getcommandbyte=%2\n",kdb_getcommandbyte());
            break;

          case '2':
            displayline("Set the new drive: ");
            readstringc(temps,2,16);
            temps[15]=0;

            bootdisk=atoi2(temps,16,NULL);
            displayline("\nNew drive=%2 \n",bootdisk);
            break;

          case '3': //disassemblerr
            {
              _DecodedInst disassembled[22];
              unsigned int i;
              unsigned int used=0;
              distorm_decode((UINT64)&menu2,(unsigned char*)menu2, 256, Decode64Bits, disassembled, 22, &used);

              if (used)
              {
                for (i=0; i<used; i++)
                {
                  displayline("%x : %s - %s %s\n",
                    disassembled[i].offset,
                    disassembled[i].instructionHex.p,
                    disassembled[i].mnemonic.p,
                    disassembled[i].operands.p);
                }

              }
              else
              {
                displayline("Failure...\n");
              }


            }
            break;

          case '4':
          {
            __asm("sti"); //enable interrupts
            break;
          }

          case '5':
          {
            UINT64 rflags;
            pcpuinfo i=getcpuinfo();



            try
            {
              displayline("Doing an int3 bp\n");
              int3bptest();
              displayline("Failure to int3 break\n");
            }
            except
            {
              displayline("caught level 1 int3 :%d\n", lastexception);
            }
            tryend

            try
            {
              displayline("Doing an int3 bp 2\n");
              int3bptest();
              displayline("Failure to int3 break 2\n");
            }
            except
            {
              displayline("caught level 1 int 3 2:%d\n", lastexception);
            }
            tryend


            displayline("testing multilevel try/except\n");
            try
            {
              displayline("inside try. Entering 2nd try\n");
              try
              {
                displayline("inside 2nd try. calling int3bptest\n");
                int3bptest();
                displayline("int3bptest in level 2 failed to get caught\n");
              }
              except
              {
                displayline("caught level 2 int3:%d\n", lastexception);
              }
              tryend

              int3bptest();
              displayline("Failure to int3 break\n");
            }
            except
            {
              displayline("caught level 1 int3:%d\n", lastexception);
            }
            tryend

            displayline("Setting the GD flag");

            i->OnInterrupt.RSP=getRSP();
            i->OnInterrupt.RBP=getRBP();
            i->OnInterrupt.RIP=(QWORD)((volatile void *)&&afterGDtest);
            asm volatile ("": : :"memory");
            setDR6(0xfffffff0);
            setDR7(getDR7() | (1<<13));
            asm volatile ("": : :"memory");
            setDR6(0xffff0ff0);

            sendstringf("Failure to break on GD");

            asm volatile ("": : :"memory");
afterGDtest:

            //RF


            displayline("Setting an execute breakpoint\n\r");
            setDR0((QWORD)getCR0);
            setDR6(0xffff0ff0);
            setDR7(getDR7() | (1<<0));
            displayline("Going to execute it\n");

            i->OnInterrupt.RSP=getRSP();
            i->OnInterrupt.RBP=getRBP();
            i->OnInterrupt.RIP=(QWORD)((volatile void *)&&afterEXBPtest);
            asm volatile ("": : :"memory");
            getCR0();

            sendstringf("Failure to break on execute\n");
            asm volatile ("": : :"memory");
afterEXBPtest:

            displayline("Setting a RW breakpoint\n\r");
            setDR0((QWORD)&isAP);
            setDR6(0xffff0ff0);
            setDR7(getDR7() | (3<<18) | (3<<16) | (1<<0));
            displayline("Going to write to that breakpoint\n");

            i->OnInterrupt.RSP=getRSP();
            i->OnInterrupt.RBP=getRBP();
            i->OnInterrupt.RIP=(QWORD)((volatile void *)&&afterWRBPtest);
            asm volatile ("": : :"memory");

            isAP++;
            asm volatile ("": : :"memory");
            sendstringf("Failure to break on write. %d\n", isAP);
afterWRBPtest:
            asm volatile ("": : :"memory");
            displayline("done writing\n");


            displayline("Setting the single step flag (this will give exceptions)\n\r");
            rflags=getRFLAGS(); //NO RF
            setRFLAGS(rflags | (1<<8));

            setRFLAGS(rflags & (~(1<<8))); //unset

            break;
          }

          case '6':
          {
            displayline("Setting the redirects. #UD Interrupt will fire if dbvm is not loaded (and crash)");
            vmcall_setintredirects();

            break;

          }

          case '7':
          {
            QWORD cr3=getCR3();
            displayline("CR3 was %6\n", cr3);

            cr3=cr3&0xfffffffffffff000ULL;
            setCR3(cr3);
            setCR4(getCR4() | CR4_PCIDE);

            cr3=cr3 | 2;
            setCR3(cr3);

            cr3=getCR3();
            displayline("CR3 is %6\n", cr3);

            cr3=cr3 | 0x8000000000000000ULL;
            setCR3(cr3);
            cr3=getCR3();
            displayline("CR3 is %6\n", cr3);

            break;
          }

          case '8':
          {
            //pci enum test
            pciConfigEnumPci();
            break;
          }

          case '9':
          {
            {
              char temps[17];
              UINT64 address;
              int size;
              int err2,err3;

              sendstring("\nAddress:");
              readstring(temps,16,16);
              address=atoi2(temps,16,&err2);

              sendstring("\nNumber of bytes:");
              readstring(temps,16,16);
              size=atoi2(temps,10,&err3);

              {
                _DecodedInst disassembled[22];
                unsigned int i;
                unsigned int used=0;
                distorm_decode((UINT64)address,(unsigned char*)address, size, Decode64Bits, disassembled, 22, &used);

                if (used)
                {
                  for (i=0; i<used; i++)
                  {
                    displayline("%x : %s - %s %s\n",
                      disassembled[i].offset,
                      disassembled[i].instructionHex.p,
                      disassembled[i].mnemonic.p,
                      disassembled[i].operands.p);
                  }

                }
                else
                {
                  displayline("Failure...\n");
                }
              }

            }

            break;
          }

          case 'a':
          {
            testBranchPrediction();
            break;
          }

          case 'b':
          {
            reboot(0);
            displayline("WTF?\n");
            break;
          }

          case 'c':
          {
            psod();
            displayline("WTF?\n");
            break;
          }

          case 'e':
          {
            QWORD old=readMSR(EFER_MSR);
            QWORD new;

            sendstringf("old=%6\n", old);

            new=old ^ (1<<11);
            new=new & (~(1<<10));
            sendstringf("new1=%6\n", new);
            writeMSR(EFER_MSR, new);

            new=readMSR(EFER_MSR);
            sendstringf("new2=%6\n", new);
            break;
          }

          case 'o':
          {
            int count;
            void *mem;

            while (1)
            {
              mem=malloc2(4096);
              if (mem==NULL)
              {
                sendstringf("alloc fail\n");
                while (1);
              }
              count++;
              if (count%10==0)
              {
                sendstringf("count=%d\n", count);
              }

            }


            break;
          }

          case 'v':
          {
            QWORD cr0=getCR0();
            sendstringf("CR0=%x\n", cr0);

            sendstring("Flipping WP\n");

            cr0=cr0 ^ CR0_WP;
            setCR0(cr0);
            cr0=getCR0();
            sendstringf("CR0=%x\n", cr0);

            sendstring("Flipping NE\n");
            cr0=cr0 ^ CR0_NE;
            setCR0(cr0);
            cr0=getCR0();
            sendstringf("CR0=%x\n", cr0);

            sendstring("Flipping NE again \n");
            cr0=cr0 ^ CR0_NE;
            setCR0(cr0);
            cr0=getCR0();
            sendstringf("CR0=%x\n", cr0);

            QWORD cr4=getCR4();
            sendstringf("CR4=%x\n", cr4);

            sendstring("Flipping CR4_OSXSAVE\n");

            cr4=cr4 ^ CR4_OSXSAVE;
            setCR4(cr4);
            cr4=getCR4();
            sendstringf("CR4=%x\n", cr4);

            sendstring("Flipping CR4_VMXE\n");

            cr4=cr4 ^ CR4_VMXE;
            setCR4(cr4);
            cr4=getCR4();
            sendstringf("CR4=%x\n", cr4);

            sendstring("Flipping CR4_VMXE again\n");

            cr4=cr4 ^ CR4_VMXE;
            setCR4(cr4);
            cr4=getCR4();
            sendstringf("CR4=%x\n", cr4);



            break;
          }

          default:
            key=0;
            break;
        }



        if (key)
        {
          displayline("Press any key to return to the menu\n");
          if (loadedOS)
            key=waitforchar();
          else
            key=kbd_getchar();

        }
      }
    }

		resync();
	}
}


#if (defined SERIALPORT) && (SERIALPORT != 0)
//obsolete, part of lauxlib again
void *lalloc (void *ud, void *ptr, size_t osize, size_t nsize) {
  (void)ud;
  (void)osize;
  if (nsize == 0) {
    free(ptr);
    return NULL;
  }
  else
    return realloc(ptr, nsize);
}
#endif


void menu(void)
{
  //outportb(0x80,0x11);
  displayline("menu\n\r"); //debug to find out why the vm completely freezes when SERIALPORT==0

  sendstring("menu\n\r");

  displayline("After sendstring\n");

  int i,j;

  nosendchar[getAPICID()]=0; //force that it gets send

  while (1)
  {
    char command;
    QWORD mem;
    QWORD pages;
    mem=getTotalFreeMemory(&pages);
    sendstring("\n\r\n\rWelcome to Dark Byte\'s virtual machine monitor\n\r");


    sendstringf("Memory free: %d Bytes (Pages: %d) ", (int)mem, (int)pages);
    sendstring("\n\r^^^^^^^^^^^^^^^^^^^^^^^Menu 1^^^^^^^^^^^^^^^^^^\n\r");
    sendstring("Press 0 to run the VM\n\r");
    sendstring("Press 1 to display the fake memory map\n\r");
    sendstring("Press 2 to display the virtual memory of the VMM\n\r");
    sendstring("Press 3 to display the physical memory of this system\n\r");
    sendstring("Press 4 to display the virtual memory of the Virtual Machine\n\r");
    sendstring("Press 5 to raise int 1 by software\n\r");
    sendstring("Press 6 to run some testcode in the 2nd core (assuming there is one)\n\r");
    sendstring("Press 7 to test some crap\n\r");
    sendstring("Press 8 to execute testcode()\n\r");
    sendstring("Press 9 to restart\n\r");
    sendstring("Press M to test the memorymanager\n\r");
#if (defined SERIALPORT) && (SERIALPORT != 0)
    sendstring("Press L for Lua\n\r");
#endif
    sendstring("Your command:");

#ifndef DEBUG
    if (autostart || loadedOS)
    {
      autostart=0;
      command='0';
    }
    else
#endif
    {
      displayline("Waiting for serial port command:\n");
      sendstring("waiting for command:");

#ifdef DELAYEDSERIAL
      if (!useserial)
        command='0';
      else
#endif
      if (loadedOS)
      {
//        command='0';
        command=waitforchar();

      }
      else
      {
#if (defined SERIALPORT) && (SERIALPORT != 0)
        command=waitforchar();
#else
        command='0';
#endif

      }
    }



    displayline("Checking command %d ",command);

    sendchar(command);

    displayline("After sendchar\n");
    sendstring("\n\r");

    displayline("...");

    switch (command)
    {

      case  '0' : //run virtual machine
      {
        displayline("Starting the virtual machine\n");

        if ((!loadedOS) || (needtospawnApplicationProcessors))
        {

          if (cpucount>0) //!isAMD for now during tests
          {
            displayline("Sending other CPU cores into VMX wait mode\n");
            sendstring("BootCPU: Sending all AP's the command to start the VMX code\n\r");

            AP_Launch=1;
            int allsetup=0;
            pcpuinfo c;
            while (allsetup==0)
            {
              c=firstcpuinfo->next;
              allsetup=1;
              while (c)
              {
                if (c->vmxsetup==0)
                {
                  allsetup=0;
                  resync();
                  break;
                }

                c=c->next;
              }
            }
            //wait till the other cpu's are started
            sendstring("BOOT CORE: Other cpu's finished setting up, now start the boot cpu\n\r");
          }


          displayline("Calling startvmx for main core\n");
        }

        //while (1) _pause(); //debug so I only see AP cpu's
        //outportb(0x80,0x12);

        startvmx(getcpuinfo());
        sendstring("BootCPU: Back from startvmx\n\r");
        break;
      }

      case  '1' : //display fake mem map
        sendARD();
        break;


      case  '2' : //display vmm mem
        {
          char temps[17];
          unsigned long long StartAddress;
          unsigned int nrofbytes;

          sendstring("Startaddress:");
          readstring(temps,16,16);
          sendstring("\n\r");
          sendstringf("temps=%s \n\r",temps);
          StartAddress=atoi2(temps,16,NULL);


          sendstring("Number of bytes:");
          readstring(temps,8,8);
          sendstring("\n\r");
          nrofbytes=atoi2(temps,10,NULL);

          sendstringf("Going to show the memory region %6 to %6 (physical=%6)\n\r",StartAddress,StartAddress+nrofbytes,VirtualToPhysical((void *)StartAddress));

          for (i=0; (unsigned int)i<nrofbytes; i+=16)
          {
            sendstringf("%8 : ",StartAddress+i);
            for (j=i; (j<(i+16)) && ((unsigned)j<nrofbytes); j++)
              sendstringf("%2 ",(unsigned char)*(unsigned char *)(StartAddress+j));

            if ((unsigned)(i+16)>nrofbytes)
            {
              // Get the cursor to the right spot
              int currentcol=11+3*(nrofbytes-i);
              int wantedcol=11+3*16;
              for (j=0; j<(wantedcol-currentcol); j++)
                sendstring(" ");
            }

            for (j=i; j<(i+16) && ((unsigned)j<nrofbytes); j++)
            {
              unsigned char tempc=*(unsigned char *)(StartAddress+j);
              if (tempc<32)
                tempc='.';

              sendstringf("%c",tempc);
            }

            sendstring("\n\r");
          }

          break;
        }



      case  '3' : //display physical memory
        //use one page (mapped at 0x80000000, 4mb) to display the memory
        {
          displayPhysicalMemory();
        }
        break;


      case  '4' : //display vm memory (virtual)
				{
				  sendstringf("obsolete\n");

				}
        break;


      case  '5' :
        asm("int $5\n");
        break;


      case  '6' : //run 2nd core testapp
        {
          /*
          unsigned int a,b,c,d;
          a=1;
          _cpuid(&a,&b,&c,&d);
          sendstringf("cpuid:1: eax=%8, ebx=%8, ecx=%8, edx=%8, \n\r",a,b,c,d);
          if ((d & (1<<28))>0)
          {
            sendstring("Multi processor supported\n\r");
            sendstringf("logical processor per package: %d \n\r",(d >> 16) & 0xff);

            BOOT_ID=apic_getBootID();
            sendstringf("BOOT_ID=%8\n\r",BOOT_ID);

            sendstringf("APIC_SVR=%8\n\r",APIC_SVR);


            cpucount=1;
            apic_enableSVR();
            while (cpucount==1)
            {
              a=1;
              _cpuid(&a,&b,&c,&d); //serializing instruction
            }

          }
          else
            sendstring("No multi processor support\n");


          */

          break;
        }

      case  '7':
        {
          int error;
          UINT64 pf;

          void *address=mapVMmemory(getcpuinfo(), 0xc0000ULL, 16, &error, &pf);
          sendstringf("address=%6\n", address);

          if (error==0)
          {
            sendstringf("*address=%2\n", *(char *)address);
            unmapPhysicalMemory(address,16);
          }
          else
            sendstringf("error=%d  (pf=%6)\n", error, pf);






          break;
          //



        }

			case  '8':
				{
          break;
        }

        case	'9':
        {
          reboot(0);

          break;
				}

        case 'm':
        {
          mmtest();
          break;
        }


#if (defined SERIALPORT) && (SERIALPORT != 0)
        case 'l':
        {
          sendstring("Entering lua console:");
          //enterLuaConsole();


          break;
        }
#endif



        case 'v':
        {
          sendstring("Trying vmcall\n");
          vmcalltest();
          break;
        }

       /*
      case  'i':
        showstate();
        break;

      case  't':
        {
          ULONG xxx=MapPhysicalMemory(0xfb000000, 0xfb000000);
          sendstringf("xxx=%8\n\r",xxx);
          testcode();
          break;
        }

      case  'c' :
      {
        unsigned int crc=generateCRC((unsigned char *)(e), 0x007fffff-(e));
        unsigned int idtcrc=generateCRC((unsigned char *)0, 0x400);
        unsigned int vmmcrc=generateCRC((unsigned char *)0x00400000, getGDTbase() - 0x00400000);
        sendstringf("stack crc=%8\n\r",crc);
        sendstringf("idt crc=%8\n\r",idtcrc);
        sendstringf("vmm crc=%8\n\r",vmmcrc);
        break;
      }
      */

      default :
        sendstring("That is not a valid option\n\r");
        break;

    }


  }

}


void startvmx(pcpuinfo currentcpuinfo)
{
#ifdef DEBUG
  UINT64 entryrsp=getRSP();
#endif

  UINT64 a,b,c,d;
 // outportb(0x80,0x13);


  displayline("cpu %d: startvmx:\n",currentcpuinfo->cpunr);
#ifdef DEBUG
  sendstringf("currentcpuinfo=%6  (cpunr=%d)\n\r",(UINT64)currentcpuinfo, currentcpuinfo->cpunr);
  sendstringf("ESP=%6\n\r",entryrsp);
  sendstringf("APICID=%d\n\r",getAPICID());
#endif




  // setup the vmx environment and run it
  if (hascpuid())
  {

    unsigned char ext_fam_id;
    unsigned char ext_model_id;
    unsigned char proc_type;
    unsigned char family_id;
    unsigned char model;
    unsigned char stepping_id;

    a=1;
    _cpuid(&a,&b,&d,&c);


    stepping_id= a & 0xf; //4
    model=(a >> 4) & 0xf; //4
    family_id=(a >> 8) & 0xf; //4
    proc_type=(a >> 12) & 0x3; //2
    //2
    ext_model_id=(a >> 16 ) & 0xf; //4
    ext_fam_id=(a >> 20 ) & 0xff;

    sendstringf("Version Information=%x : \n\r",a);
    sendstringf("\tstepping_id=%d\n\r",stepping_id);
    sendstringf("\tmodel=%d\n\r",model);
    sendstringf("\tfamily_id=%d\n\r",family_id);
    sendstringf("\tproc_type=%d\n\r",proc_type);
    sendstringf("\text_model_id=%d\n\r",ext_model_id);
    sendstringf("\text_fam_id=%d\n\r",ext_fam_id);



    sendstringf("Brand Index/CLFLUSH/Maxnrcores/Init APIC=%x :\n",b);
    {
      unsigned char brand_index;
      unsigned char CLFLUSH_line_size;
      unsigned char max_logical_cpu;
      unsigned char initial_APIC;

      brand_index=b & 0xff; //4
      CLFLUSH_line_size=(b >> 8) & 0xff;
      max_logical_cpu=(b >> 16) & 0xff;
      initial_APIC=(b >> 24) & 0xff;

      sendstringf("\tBrand Index=%d\n\r",brand_index);
      sendstringf("\tCLFLUSH line size=%d\n\r",CLFLUSH_line_size);
      sendstringf("\tMaximum logical cpu\'s=%d\n\r",max_logical_cpu);
      sendstringf("\tinitial APIC=%d\n\r",initial_APIC);
    }

   // PSOD("Line 2149: PSOD Test");

    if (isAMD)
    {
      sendstring("AMD virtualization handling\n\r");



      UINT64 a=0x80000001;
      UINT64 b,c,d;

      _cpuid(&a,&b,&c,&d);

      if (c & (1<<2)) //SVM bit in cpuid
      {
    	  sendstring("SVM supported\n");

    	  a=0x8000000a;
    	  _cpuid(&a,&b,&c,&d);

    	  displayline("cpuid: 0x8000000a:\n");
    	  displayline("EAX=%8\n", a);
    	  displayline("EBX=%8\n", b);
    	  displayline("ECX=%8\n", c);
    	  displayline("EDX=%8\n", d);


    	  AMD_hasDecodeAssists=(d & (1<<7))>0;
    	  AMD_hasNRIPS=(d & (1<<3))>0;
    	  sendstringf("AMD_hasDecodeAssists=%d\n", AMD_hasDecodeAssists);

    	  UINT64 VM_CR=readMSR(0xc0010114); //VM_CR MSR
    	  sendstringf("VM_CR=%6\n", VM_CR);

    	  if ((VM_CR & (1<<4))==0)
    	  {
    		  UINT64 efer;
    		  sendstring("SVM is available\n");



    		  currentcpuinfo->vmcb=malloc(4096);
    		  //SetPageToWriteThrough((UINT64)currentcpuinfo->vmcb); //test. I doubt it's needed since no cpu touches another's vmcb

    		  sendstringf("Have set vmcb at %x to WriteThrough caching protection\n", currentcpuinfo->vmcb);

    		  zeromemory(currentcpuinfo->vmcb, 4096);

    		  currentcpuinfo->vmcb_PA=(UINT64)VirtualToPhysical((void *)currentcpuinfo->vmcb);

          sendstring("Setting SVME bit in EFER\n");
          efer=readMSR(EFER_MSR);

          sendstringf("EFER was %6\n", efer);
          efer=efer | (1 << 12);
          sendstringf("EFER will become %6\n", efer);


          writeMSR(EFER_MSR, efer);




          UINT64 VM_HSAVE_PA_MSR=readMSR(0xc0010117); //VM_HSAVE_PA MSR
          sendstringf("VM_HSAVE_PA_MSR was %6\n", VM_HSAVE_PA_MSR);

          currentcpuinfo->vmcb_host=malloc(4096);
        //  bochsbp();
          writeMSR(0xc0010117, (UINT64)VirtualToPhysical(currentcpuinfo->vmcb_host));



    		  setupVMX(currentcpuinfo);


          /*if (!isAP)
            clearScreen();*/




          launchVMX(currentcpuinfo);

          sendstring("launchVMX returned\n");
          while (1) outportb(0x80,0xc9);


    	  }
    	  else
    	  {
    		  sendstring("SVM has been disabled\n");
    	  }


      }
      else
      {
    	  sendstring("This cpu does not support SVM\n");
    	  sendstringf("cpuid: 0x80000001:\n");
    	  sendstringf("EAX=%8\n", a);
    	  sendstringf("EBX=%8\n", b);
    	  sendstringf("ECX=%8\n", c);
    	  sendstringf("EDX=%8\n", d);

      }



    }
    else
    {

      if ((d >> 5) & 1)
      {

        volatile UINT64 IA32_FEATURE_CONTROL;


        displayline("%d:System check successful. INTEL-VT is supported\n", currentcpuinfo->cpunr);
        sendstring("!!!!!!!!!!!!!!This system supports VMX!!!!!!!!!!!!!!\n\r");

        displayline("Going to call IA32_FEATURE_CONTROL=readMSR(0x3a)\n");
        IA32_FEATURE_CONTROL=readMSR(IA32_FEATURE_CONTROL_MSR);
        displayline("IA32_FEATURE_CONTROL=%6\n\r",IA32_FEATURE_CONTROL);


        if (IA32_FEATURE_CONTROL & FEATURE_CONTROL_LOCK)
        {
          displayline("IA32_FEATURE_CONTROL is locked (value=%6). (Disabled in bios?)\n\r",IA32_FEATURE_CONTROL);
          if (!(IA32_FEATURE_CONTROL & FEATURE_CONTROL_VMXON ))
          {
            displayline("Bit 2 (VMX) is also disabled. VMX is not possible. Reboot!\n");
            return;
          }
          else
            sendstring("VMXON was already enabled in the feature control MSR\n");
        }
        else
        {
          sendstring("Not locked yet\n");

          IA32_FEATURE_CONTROL=IA32_FEATURE_CONTROL | FEATURE_CONTROL_VMXON | FEATURE_CONTROL_LOCK;

          displayline("setting IA32_FEATURE_CONTROL to %6\n\r",IA32_FEATURE_CONTROL);

          writeMSR(IA32_FEATURE_CONTROL_MSR,IA32_FEATURE_CONTROL);
          IA32_FEATURE_CONTROL=readMSR(IA32_FEATURE_CONTROL_MSR);
          displayline("IA32_FEATURE_CONTROL is now %6\n\r",IA32_FEATURE_CONTROL);
        }


        sendstring("Gathering VMX info\n\r");
  			//gather info
        IA32_VMX_BASIC.IA32_VMX_BASIC=readMSR(0x480);

        IA32_VMX_CR0_FIXED0=readMSR(IA32_VMX_CR0_FIXED0_MSR);
        IA32_VMX_CR0_FIXED1=readMSR(IA32_VMX_CR0_FIXED1_MSR);
        IA32_VMX_CR4_FIXED0=readMSR(IA32_VMX_CR4_FIXED0_MSR);
        IA32_VMX_CR4_FIXED1=readMSR(IA32_VMX_CR4_FIXED1_MSR);


        sendstring("Setting CR4\n\r");


  			setCR4(getCR4() | CR4_VMXE);

        if (currentcpuinfo->vmxon_region==NULL)
          currentcpuinfo->vmxon_region=malloc(4096);

        sendstringf("Allocated vmxon_region at %6 (%6)\n\r",(UINT64)currentcpuinfo->vmxon_region,(UINT64)VirtualToPhysical(currentcpuinfo->vmxon_region));

        if (currentcpuinfo->vmxon_region==NULL)
        {
          sendstringf(">>>>>>>>>>>>>>>>>>>>vmxon allocation has failed<<<<<<<<<<<<<<<<<<<<<<<<<<\n");
          ddDrawRectangle(0,DDVerticalResolution-100,100,100,0xff0000);
          while (1) outportb(0x80,0xca);
        }

        zeromemory(currentcpuinfo->vmxon_region,4096);
  			*(ULONG *)currentcpuinfo->vmxon_region=IA32_VMX_BASIC.rev_id;

        if (currentcpuinfo->vmcs_region==NULL)
          currentcpuinfo->vmcs_region=malloc(4096);

        sendstringf("Allocated vmcs_region at %6 (%6)\n\r",currentcpuinfo->vmcs_region,VirtualToPhysical(currentcpuinfo->vmcs_region));

        if (currentcpuinfo->vmcs_region==NULL)
        {
          ddDrawRectangle(0,DDVerticalResolution-100,100,100,0xff0000);
          sendstringf(">>>>>>>>>>>>>>>>>>>>vmcs_region allocation has failed<<<<<<<<<<<<<<<<<<<<<<<<<<\n");
          while (1) outportb(0x80,0xcb);
        }



        zeromemory(currentcpuinfo->vmcs_region,4096);
        *(ULONG *)currentcpuinfo->vmcs_region=IA32_VMX_BASIC.rev_id;

        currentcpuinfo->vmcs_regionPA=VirtualToPhysical(currentcpuinfo->vmcs_region);

        displayline("revision id=%d\n\r",IA32_VMX_BASIC.rev_id);

        displayline("IA32_FEATURE_CONTROL=%6\n\r",IA32_FEATURE_CONTROL);
        displayline("IA32_VMX_CR0_FIXED0=%6 IA32_VMX_CR0_FIXED1=%6\n\r",IA32_VMX_CR0_FIXED0,IA32_VMX_CR0_FIXED1);
        displayline("IA32_VMX_CR4_FIXED0=%6 IA32_VMX_CR4_FIXED1=%6\n\r",IA32_VMX_CR4_FIXED0,IA32_VMX_CR4_FIXED1);


        displayline("CR0=%6  (Should be %6)\n\r",(UINT64)getCR0(),((UINT64)getCR0() | (UINT64)IA32_VMX_CR0_FIXED0) & (UINT64)IA32_VMX_CR0_FIXED1);
        displayline("CR4=%6  (Should be %6)\n\r",(UINT64)getCR4(),((UINT64)getCR4() | (UINT64)IA32_VMX_CR4_FIXED0) & (UINT64)IA32_VMX_CR4_FIXED1);

        setCR0(((UINT64)getCR0() | (UINT64)IA32_VMX_CR0_FIXED0) & (UINT64)IA32_VMX_CR0_FIXED1);
        setCR4(((UINT64)getCR4() | (UINT64)IA32_VMX_CR4_FIXED0) & (UINT64)IA32_VMX_CR4_FIXED1);


        displayline("vmxon_region=%6\n\r",VirtualToPhysical(currentcpuinfo->vmxon_region));

        displayline("%d:Checks successfull. Going to call vmxon\n",currentcpuinfo->cpunr);

  		  if (vmxon(VirtualToPhysical(currentcpuinfo->vmxon_region))==0)
  	  	{
  		    sendstring("vmxon success\n\r");
          displayline("%d: vmxon success\n",currentcpuinfo->cpunr);

          displayline("%d: calling vmclear\n",currentcpuinfo->cpunr);

          if (vmclear(VirtualToPhysical(currentcpuinfo->vmcs_region))==0)
          {
            displayline("%d: calling vmptrld\n",currentcpuinfo->cpunr);

            if (vmptrld(VirtualToPhysical(currentcpuinfo->vmcs_region))==0)
            {

              displayline("%d: vmptrld successful. Calling setupVMX\n", currentcpuinfo->cpunr);

              sendstringf("%d: Calling setupVMX with currentcpuinfo %6\n\r",currentcpuinfo->cpunr ,(UINT64)currentcpuinfo);
              setupVMX(currentcpuinfo);

              displayline("%d: Virtual Machine configuration successful. Launching...\n",currentcpuinfo->cpunr);


              /*if (currentcpuinfo->cpunr==0)
                while (1);*/

              if (!isAP)
                clearScreen();

              //vmptrld(VirtualToPhysical(currentcpuinfo->vmcs_region));
             // vmptrld(VirtualToPhysical(currentcpuinfo->vmcs_region));
              //vmptrld(VirtualToPhysical(currentcpuinfo->vmcs_region));

              launchVMX(currentcpuinfo);

              ddDrawRectangle(0,DDVerticalResolution-100,100,100,0xff0000);

              displayline("Exit from launchVMX, if you see this, something horrible has happened\n");
              sendstring("Exit from launchVMX\n\r");

            }
            else
            {
              sendstring("vmptrld failed\n\r");

              displayline("vmptrld failure:");
              displayline(getVMInstructionErrorString());
              displayline(" \n");


            }
          }
          else
          {
            sendstring("vmclear failed\n\r");


            displayline("vmclear failure:");
            displayline(getVMInstructionErrorString());
            displayline(" \n");

          }
          sendstringf("Calling vmxoff() (rsp=%x)\n\r", getRSP());

  				vmxoff();
  				sendstring("still here so vmxoff succeeded\n\r");

  			}
        else
        {
          displayline("vmxon failure\n");
          sendstring("vmxon failed\n\r");
        }



      }
      else
      {
        displayline("Fatal error: Your system does not support intel-VT!!!!\n");
        displayline("Remove the disk, reboot, and go look for a better cpu\n");
        sendstring("!!!!!!!!!!!!!!Your system is crap, it does NOT support VMX!!!!!!!!!!!!!!\n\r");
        sendstringf("cpuid(1):\n");
        sendstringf("EAX=%8\n", a);
        sendstringf("EBX=%8\n", b);
        sendstringf("ECX=%8\n", c);
        sendstringf("EDX=%8\n", d);

      }
    }


  }
  else
  {
    sendstring("This fucking retarded system doesn'\t even have cpuid. Should I fry it ?\n\r");
    displayline("Your system doesn\'t even support CPUID, therefore it probably won\'t support Virtualization either\n");
  }

#ifdef DEBUG
  sendstringf("End of startvmx (entryrsp=%6, returnrsp=%6)\n\r",entryrsp,getRSP());
#endif

  ddDrawRectangle(0,DDVerticalResolution-100,100,100,0x00ffff);

  if (currentcpuinfo->cpunr==0)
    displayline("bye...\n");
}
