--- /dev/null
+#ifndef __VMM_EMULATOR_H__
+#define __VMM_EMULATOR_H__
+
+#ifdef __V3VEE__
+
+#include <palacios/vmm_list.h>
+#include <palacios/vmm_shadow_paging.h>
+#include <palacios/vmm_paging.h>
+
+
+
+
+
+struct emulated_page {
+  addr_t page_addr;
+  addr_t va;
+  pte32_t pte;
+  struct list_head page_list;
+};
+
+struct saved_page {
+  addr_t va;
+  pte32_t pte;
+  struct list_head page_list;
+};
+
+
+struct write_region {
+  void * write_data;
+  
+  uint_t length;
+  int (*write)(addr_t write_addr, void * src, uint_t length, void * priv_data);
+  addr_t write_addr;
+  void * private_data;
+  
+  struct list_head write_list;
+};
+
+
+struct emulation_state {
+  uint_t num_emulated_pages;
+  struct list_head emulated_pages;
+
+  uint_t num_saved_pages;
+  struct list_head saved_pages;
+
+  uint_t num_write_regions;
+  struct list_head write_regions;
+
+  uint_t running : 1;
+  uint_t instr_length;
+};
+
+
+int init_emulator(struct guest_info * info);
+
+
+int v3_emulation_exit_handler(struct guest_info * info);
+
+int v3_emulate_memory_write(struct guest_info * info, addr_t fault_gva,
+                           int (*write)(addr_t write_addr, void * src, uint_t length, void * priv_data), 
+                           addr_t write_addr, void * private_data);
+int v3_emulate_memory_read(struct guest_info * info, addr_t fault_gva, 
+                          int (*read)(addr_t read_addr, void * dst, uint_t length, void * priv_data), 
+                          addr_t read_addr, void * private_data);
+
+
+#endif // !__V3VEE__
+
+#endif
 
 int handle_shadow_pagefault(struct guest_info * info, addr_t fault_addr, pf_error_t error_code);
 int handle_shadow_invlpg(struct guest_info * info);
 
+
+
+
+int v3_replace_shdw_page(struct guest_info * info, addr_t location, void * new_page, void* old_page);
+int v3_replace_shdw_page32(struct guest_info * info, addr_t location, pte32_t * new_page, pte32_t * old_page); 
+
 #endif // ! __V3VEE__
 
 #endif
 
 #include <geekos/io.h>
 
 
-#define GENERIC_DEBUG 1
 
-#if GENERIC_DEBUG
-#define GENERIC_DEBUG_PRINT(first, rest...) PrintDebug(first, ##rest) 
-#else
-#define GENERIC_DEBUG_PRINT(first, rest...)
+#ifndef DEBUG_GENERIC
+#undef PrintDebug
+#define PrintDebug(fmt, args...)
 #endif
 
 
 
 int generic_reset_device(struct vm_device * dev)
 {
-  GENERIC_DEBUG_PRINT("generic: reset device\n");
+  PrintDebug("generic: reset device\n");
  
   return 0;
 
 
 int generic_start_device(struct vm_device *dev)
 {
-  GENERIC_DEBUG_PRINT("generic: start device\n");
+  PrintDebug("generic: start device\n");
   return 0;
 }
 
 
 int generic_stop_device(struct vm_device *dev)
 {
-  GENERIC_DEBUG_PRINT("generic: stop device\n");
+  PrintDebug("generic: stop device\n");
   return 0;
 }
 
 {
   uint_t i;
 
-  GENERIC_DEBUG_PRINT("generic: writing 0x");
+  PrintDebug("generic: writing 0x");
 
   for (i = 0; i < length; i++) { 
-    GENERIC_DEBUG_PRINT("%x", ((uchar_t*)src)[i]);
+    PrintDebug("%x", ((uchar_t*)src)[i]);
   }
   
-  GENERIC_DEBUG_PRINT(" to port 0x%x ... ", port);
+  PrintDebug(" to port 0x%x ... ", port);
 
   switch (length) {
   case 1:
     }
   }
 
-  GENERIC_DEBUG_PRINT(" done\n");
+  PrintDebug(" done\n");
   
   return length;
 }
 {
   uint_t i;
 
-  GENERIC_DEBUG_PRINT("generic: reading 0x%x bytes from port 0x%x ...", length, port);
+  PrintDebug("generic: reading 0x%x bytes from port 0x%x ...", length, port);
 
   switch (length) {
   case 1:
     }
   }
 
-  GENERIC_DEBUG_PRINT(" done ... read 0x");
+  PrintDebug(" done ... read 0x");
 
   for (i = 0; i < length; i++) { 
-    GENERIC_DEBUG_PRINT("%x", ((uchar_t*)src)[i]);
+    PrintDebug("%x", ((uchar_t*)src)[i]);
   }
 
-  GENERIC_DEBUG_PRINT("\n");
+  PrintDebug("\n");
 
   return length;
 }
 {
   uint_t i;
 
-  GENERIC_DEBUG_PRINT("generic: writing 0x");
+  PrintDebug("generic: writing 0x");
 
   for (i = 0; i < length; i++) { 
-    GENERIC_DEBUG_PRINT("%x", ((uchar_t*)src)[i]);
+    PrintDebug("%x", ((uchar_t*)src)[i]);
   }
   
-  GENERIC_DEBUG_PRINT(" to port 0x%x ... ", port);
+  PrintDebug(" to port 0x%x ... ", port);
 
-  GENERIC_DEBUG_PRINT(" ignored\n");
+  PrintDebug(" ignored\n");
   
   return length;
 }
                             struct vm_device * dev)
 {
 
-  GENERIC_DEBUG_PRINT("generic: reading 0x%x bytes from port 0x%x ...", length, port);
+  PrintDebug("generic: reading 0x%x bytes from port 0x%x ...", length, port);
 
   memset((char*)src,0,length);
-  GENERIC_DEBUG_PRINT(" ignored (return zeroed buffer)\n");
+  PrintDebug(" ignored (return zeroed buffer)\n");
 
   return length;
 }
   struct generic_internal *state = (struct generic_internal *)(dev->private_data);
   uint_t i, j;
 
-  GENERIC_DEBUG_PRINT("generic: init_device\n");
+  PrintDebug("generic: init_device\n");
 
   // Would read state here
 
   generic_reset_device(dev);
 
   for (i = 0; i < state->num_port_ranges; i++) { 
-    GENERIC_DEBUG_PRINT("generic: hooking ports 0x%x to 0x%x as %x\n", state->port_ranges[i][0], state->port_ranges[i][1], state->port_ranges[i][2]==GENERIC_PRINT_AND_PASSTHROUGH ? "print-and-passthrough" : "print-and-ignore");
+    PrintDebug("generic: hooking ports 0x%x to 0x%x as %x\n", state->port_ranges[i][0], state->port_ranges[i][1], state->port_ranges[i][2]==GENERIC_PRINT_AND_PASSTHROUGH ? "print-and-passthrough" : "print-and-ignore");
 
 #if PORT_HOOKS
     for (j = state->port_ranges[i][0]; j <= state->port_ranges[i][1]; j++) { 
       if (state->port_ranges[i][2]==GENERIC_PRINT_AND_PASSTHROUGH) { 
        if (dev_hook_io(dev, j, &generic_read_port_passthrough, &generic_write_port_passthrough)) { 
-         GENERIC_DEBUG_PRINT("generic: can't hook port 0x%x (already hooked?)\n", j);
+         PrintDebug("generic: can't hook port 0x%x (already hooked?)\n", j);
        }
       } else if (state->port_ranges[i][2]==GENERIC_PRINT_AND_IGNORE) { 
        if (dev_hook_io(dev, j, &generic_read_port_ignore, &generic_write_port_ignore)) { 
-         GENERIC_DEBUG_PRINT("generic: can't hook port 0x%x (already hooked?)\n", j);
+         PrintDebug("generic: can't hook port 0x%x (already hooked?)\n", j);
        }
       } 
     }
 #else
-    GENERIC_DEBUG_PRINT("generic: hooking ports not supported\n");
+    PrintDebug("generic: hooking ports not supported\n");
 #endif
 
   }
 
   for (i = 0; i < state->num_address_ranges; i++) { 
-    GENERIC_DEBUG_PRINT("generic: hooking addresses 0x%x to 0x%x\n",state->address_ranges[i][0],state->address_ranges[i][1]); 
+    PrintDebug("generic: hooking addresses 0x%x to 0x%x\n",state->address_ranges[i][0],state->address_ranges[i][1]); 
 
 #if MEM_HOOKS
     if (dev_hook_mem(dev, state->address_ranges[i][0], state->address_ranges[i][1])) {
-      GENERIC_DEBUG_PRINT("generic: Can't hook addresses 0x%x to 0x%x (already hooked?)\n",
+      PrintDebug("generic: Can't hook addresses 0x%x to 0x%x (already hooked?)\n",
                  state->address_ranges[i][0], state->address_ranges[i][1]); 
     }
 #else
-    GENERIC_DEBUG_PRINT("generic: hooking addresses not supported\n");
+    PrintDebug("generic: hooking addresses not supported\n");
 #endif
 
   }
 
   for (i = 0; i < state->num_irq_ranges; i++) { 
-    GENERIC_DEBUG_PRINT("generic: hooking irqs 0x%x to 0x%x\n",state->irq_ranges[i][0],state->irq_ranges[i][1]);
+    PrintDebug("generic: hooking irqs 0x%x to 0x%x\n",state->irq_ranges[i][0],state->irq_ranges[i][1]);
 
 #if IRQ_HOOKS
     for (j = state->irq_ranges[i][0]; j <= state->irq_ranges[i][1]; j++) { 
       if (dev_hook_irq(dev, j, &generic_interrupt)) { 
-       GENERIC_DEBUG_PRINT("generic: can't hook irq  0x%x (already hooked?)\n", j);
+       PrintDebug("generic: can't hook irq  0x%x (already hooked?)\n", j);
       }
     }
 #else
-    GENERIC_DEBUG_PRINT("generic: hooking irqs not supported\n");
+    PrintDebug("generic: hooking irqs not supported\n");
 #endif
 
   }
   struct generic_internal *state = (struct generic_internal *)(dev->private_data);
   uint_t i, j;
 
-  GENERIC_DEBUG_PRINT("generic: deinit_device\n");
+  PrintDebug("generic: deinit_device\n");
 
   for (i = 0; i < state->num_irq_ranges; i++) { 
-    GENERIC_DEBUG_PRINT("generic: unhooking irqs 0x%x to 0x%x\n", state->irq_ranges[i][0], state->irq_ranges[i][1]);
+    PrintDebug("generic: unhooking irqs 0x%x to 0x%x\n", state->irq_ranges[i][0], state->irq_ranges[i][1]);
 
 #if IRQ_HOOKS
     for (j = state->irq_ranges[i][0]; j <= state->irq_ranges[i][1]; j++) { 
       if (dev_unhook_irq(dev, j)) {
-       GENERIC_DEBUG_PRINT("generic: can't unhook irq 0x%x (already unhooked?)\n",j);
+       PrintDebug("generic: can't unhook irq 0x%x (already unhooked?)\n",j);
       }
     }
 #else
-    GENERIC_DEBUG_PRINT("generic: unhooking irqs not supported\n");
+    PrintDebug("generic: unhooking irqs not supported\n");
 #endif
 
   }
 
   for (i = 0; i < state->num_address_ranges; i++) { 
-    GENERIC_DEBUG_PRINT("generic: unhooking addresses 0x%x to 0x%x\n",state->address_ranges[i][0],state->address_ranges[i][1]); 
+    PrintDebug("generic: unhooking addresses 0x%x to 0x%x\n",state->address_ranges[i][0],state->address_ranges[i][1]); 
 
 #if MEM_HOOKS
     if (dev_unhook_mem(dev, state->address_ranges[i][0], state->address_ranges[i][1])) {
-      GENERIC_DEBUG_PRINT("generic: Can't unhook addresses 0x%x to 0x%x (already unhooked?)\n",
+      PrintDebug("generic: Can't unhook addresses 0x%x to 0x%x (already unhooked?)\n",
                  state->address_ranges[i][0], state->address_ranges[i][1]); 
     }
 #else
-    GENERIC_DEBUG_PRINT("generic: unhooking addresses not supported\n");
+    PrintDebug("generic: unhooking addresses not supported\n");
 #endif
 
   }
 
   for (i = 0; i < state->num_port_ranges; i++) { 
-    GENERIC_DEBUG_PRINT("generic: unhooking ports 0x%x to 0x%x\n",state->port_ranges[i][0],state->port_ranges[i][1]);
+    PrintDebug("generic: unhooking ports 0x%x to 0x%x\n",state->port_ranges[i][0],state->port_ranges[i][1]);
 
 #if PORT_HOOKS
     for (j = state->port_ranges[i][0]; j <= state->port_ranges[i][1]; j++) { 
       if (dev_unhook_io(dev, j)) {
-       GENERIC_DEBUG_PRINT("generic: can't unhook port 0x%x (already unhooked?)\n", j);
+       PrintDebug("generic: can't unhook port 0x%x (already unhooked?)\n", j);
       }
     }
 #else
-    GENERIC_DEBUG_PRINT("generic: unhooking ports not supported\n");
+    PrintDebug("generic: unhooking ports not supported\n");
 #endif
 
   }
 
 /*
  * Trap handlers
  * Copyright (c) 2001,2003,2004 David H. Hovemeyer <daveho@cs.umd.edu>
- * $Revision: 1.2 $
+ * $Revision: 1.3 $
  * 
  * This is free software.  You are permitted to use,
  * redistribute, and modify it as specified in the file "COPYING".
 static void GPF_Handler(struct Interrupt_State* state)
 {
     /* Send the thread to the reaper... */
-  SerialPrintLevel(1000,"Exception %d received, killing thread %p\n",state->intNum, g_currentThread);
+  SerialPrintLevel(1000,"VMM: Exception %d received, killing thread %p\n",state->intNum, g_currentThread);
   Dump_Interrupt_State(state);
   
   Exit(-1);
 
 }
 
 
-
+/*
 int passthrough_mem_read(void * guest_addr, void * dst, uint_t length, void * priv_data) {
   memcpy(dst, (void*)guest_addr, length);
   return length;
   memcpy((void*)guest_addr, src, length);
   return length;
 }
-
+*/
 
 
 /* We need a configuration mechanism, so we can wrap this completely inside the VMM code, 
 
   guest_state->efer |= EFER_MSR_svm_enable;
   guest_state->rflags = 0x00000002; // The reserved bit is always 1
   ctrl_area->svm_instrs.VMRUN = 1;
+  ctrl_area->svm_instrs.VMMCALL = 1;
+  ctrl_area->svm_instrs.VMLOAD = 1;
+  ctrl_area->svm_instrs.VMSAVE = 1;
+  ctrl_area->svm_instrs.STGI = 1;
+  ctrl_area->svm_instrs.CLGI = 1;
+  ctrl_area->svm_instrs.SKINIT = 1;
+  ctrl_area->svm_instrs.RDTSCP = 1;
+  ctrl_area->svm_instrs.ICEBP = 1;
+  ctrl_area->svm_instrs.WBINVD = 1;
+  ctrl_area->svm_instrs.MONITOR = 1;
+  ctrl_area->svm_instrs.MWAIT_always = 1;
+  ctrl_area->svm_instrs.MWAIT_if_armed = 1;
+
+
   ctrl_area->instrs.HLT = 1;
   // guest_state->cr0 = 0x00000001;    // PE 
   ctrl_area->guest_ASID = 1;
 
 #include <palacios/svm_halt.h>
 #include <palacios/svm_pause.h>
 #include <palacios/vmm_intr.h>
-
+#include <palacios/vmm_emulator.h>
 
 int handle_svm_exit(struct guest_info * info) {
   vmcb_ctrl_t * guest_ctrl = 0;
     if (handle_svm_pause(info) == -1) { 
       return -1;
     }
+  } else if (exit_code == VMEXIT_VMMCALL) {
+    PrintDebug("VMMCALL\n");
+    if (info->run_state == VM_EMULATING) {
+      if (v3_emulation_exit_handler(info) == -1) {
+       return -1;
+      }
+    } else {
+      PrintError("VMMCALL with not emulator...\n");
+      return -1;
+    }
+
   } else {
     addr_t rip_addr;
     char buf[15];
 
 #include <devices/generic.h>
 
 
+static int passthrough_mem_read(addr_t guest_addr, void * dst, uint_t length, void * priv_data) {
+  //  memcpy(dst, (void*)guest_addr, length);
+  int foo = 20;
+
+
+  memcpy(dst, &foo, length);
+
+  PrintDebug("Passthrough mem read returning: %d (length=%d)\n", foo + (guest_addr & 0xfff), length);
+  return length;
+}
+
+static int passthrough_mem_write(addr_t guest_addr, void * src, uint_t length, void * priv_data) {
+  memcpy((void*)guest_addr, src, length);
+  return length;
+}
+
+
 
 int config_guest(struct guest_info * info, void * config_ptr) {
 
   }
   
   
-  //add_shadow_region_passthrough(info, 0x100000, 0x2000000, (addr_t)Allocate_VMM_Pages(8192));
-  add_shadow_region_passthrough(info, 0x100000, 0x1000000, (addr_t)V3_AllocPages(4096));
-  
-  add_shadow_region_passthrough(info, 0x1000000, 0x8000000, (addr_t)V3_AllocPages(32768));
-  
+ 
+  //  add_shadow_region_passthrough(info, 0x100000, 0x1000000, (addr_t)V3_AllocPages(4096));
+  { 
+    /* MEMORY HOOK TEST */
+    add_shadow_region_passthrough(info, 0x100000, 0xa00000, (addr_t)V3_AllocPages(2304));
+    hook_guest_mem(info, 0xa00000, 0xa01000, passthrough_mem_read, passthrough_mem_write, NULL);
+    
+    add_shadow_region_passthrough(info, 0xa01000, 0x1000000, (addr_t)V3_AllocPages(1791));
+
+  }
+    add_shadow_region_passthrough(info, 0x1000000, 0x8000000, (addr_t)V3_AllocPages(32768));
+ 
   // test - give linux accesss to PCI space - PAD
   add_shadow_region_passthrough(info, 0xc0000000,0xffffffff,0xc0000000);
   
 #endif
       
 
-#if 1      
+#if 0      
       // Make the Serial ports invisible 
 
       {0x3f8, 0x3f8+7, GENERIC_PRINT_AND_IGNORE},      // COM 1
 
     ret = read_guest_va_memory(info, get_addr_linear(info, info->rip, &(info->segments.cs)), 15, instr);
   }
 
-  if (ret != 15) {
+  /* The IFetch will already have faulted in the necessary bytes for the full instruction
+    if (ret != 15) {
     // I think we should inject a GPF into the guest
     PrintError("Could not read instruction (ret=%d)\n", ret);
     return -1;
-  }
+    }
+  */
 
   if (v3_decode(info, (addr_t)instr, &dec_instr) == -1) {
     PrintError("Could not decode instruction\n");
     ret = read_guest_va_memory(info, get_addr_linear(info, info->rip, &(info->segments.cs)), 15, instr);
   }
 
-  if (ret != 15) {
-    // I think we should inject a GPF into the guest
-    PrintError("Could not read instruction (ret=%d)\n", ret);
-    return -1;
-  }
+  /* The IFetch will already have faulted in the necessary bytes for the full instruction
+     if (ret != 15) {
+     // I think we should inject a GPF into the guest
+     PrintError("Could not read instruction (ret=%d)\n", ret);
+     return -1;
+     }
+  */
 
   if (v3_decode(info, (addr_t)instr, &dec_instr) == -1) {
     PrintError("Could not decode instruction\n");
     ret = read_guest_va_memory(info, get_addr_linear(info, info->rip, &(info->segments.cs)), 15, instr);
   }
 
-  if (ret != 15) {
-    // I think we should inject a GPF into the guest
-    PrintError("Could not read instruction (ret=%d)\n", ret);
-    return -1;
-  }
+  /* The IFetch will already have faulted in the necessary bytes for the full instruction
+     if (ret != 15) {
+     // I think we should inject a GPF into the guest
+     PrintError("Could not read instruction (ret=%d)\n", ret);
+     return -1;
+     }
+  */
 
   if (v3_decode(info, (addr_t)instr, &dec_instr) == -1) {
     PrintError("Could not decode instruction\n");
     ret = read_guest_va_memory(info, get_addr_linear(info, info->rip, &(info->segments.cs)), 15, instr);
   }
 
-  if (ret != 15) {
-    // I think we should inject a GPF into the guest
-    PrintError("Could not read instruction (ret=%d)\n", ret);
-    return -1;
-  }
+  /* The IFetch will already have faulted in the necessary bytes for the full instruction
+     if (ret != 15) {
+     // I think we should inject a GPF into the guest
+     PrintError("Could not read instruction (ret=%d)\n", ret);
+     return -1;
+     }
+  */
 
   if (v3_decode(info, (addr_t)instr, &dec_instr) == -1) {
     PrintError("Could not decode instruction\n");
 
--- /dev/null
+#include <palacios/vmm.h>
+#include <palacios/vmm_emulator.h>
+#include <palacios/vm_guest_mem.h>
+#include <palacios/vmm_decoder.h>
+
+static const char VMMCALL[3] = {0x0f, 0x01, 0xd9};
+
+int init_emulator(struct guest_info * info) {
+  struct emulation_state * emulator = &(info->emulator);
+
+  emulator->num_emulated_pages = 0;
+  INIT_LIST_HEAD(&(emulator->emulated_pages));
+
+
+  emulator->num_saved_pages = 0;
+  INIT_LIST_HEAD(&(emulator->saved_pages));
+  
+  emulator->num_write_regions = 0;
+  INIT_LIST_HEAD(&(emulator->write_regions));
+
+  emulator->running = 0;
+  emulator->instr_length = 0;
+
+  return 0;
+}
+
+static addr_t get_new_page() {
+  void * page = V3_AllocPages(1);
+  memset(page, 0, PAGE_SIZE);
+
+  return (addr_t)page;
+}
+
+
+static int setup_code_page(struct guest_info * info, char * instr, struct basic_instr_info * instr_info ) {
+  addr_t code_page_offset = PT32_PAGE_OFFSET(info->rip);
+  addr_t code_page = get_new_page();
+  struct emulated_page * new_code_page = V3_Malloc(sizeof(struct emulated_page));
+  struct saved_page * saved_code_page = V3_Malloc(sizeof(struct saved_page));
+
+
+  saved_code_page->va = PT32_PAGE_ADDR(info->rip);
+
+  new_code_page->page_addr = code_page; 
+  new_code_page->va = PT32_PAGE_ADDR(info->rip);
+
+  new_code_page->pte.present = 1;
+  new_code_page->pte.writable = 0;
+  new_code_page->pte.user_page = 1;
+  new_code_page->pte.page_base_addr = PT32_BASE_ADDR(code_page);
+
+  memcpy((void *)(code_page + code_page_offset), instr, instr_info->instr_length);
+  memcpy((void *)(code_page + code_page_offset + instr_info->instr_length), VMMCALL, 3);
+
+  PrintDebug("New Instr Stream:\n");
+  PrintTraceMemDump((void *)(code_page + code_page_offset), 32);
+  PrintDebug("rip =%x\n", info->rip);
+
+
+
+
+  v3_replace_shdw_page32(info, new_code_page->va, &(new_code_page->pte), &(saved_code_page->pte));
+
+
+  list_add(&(new_code_page->page_list), &(info->emulator.emulated_pages));
+  info->emulator.num_emulated_pages++;
+
+  list_add(&(saved_code_page->page_list), &(info->emulator.saved_pages));
+  info->emulator.num_saved_pages++;
+
+  return 0;
+}
+
+
+// get the current instr
+// check if rep + remove
+// put into new page, vmexit after
+// replace new page with current eip page
+// 
+int v3_emulate_memory_read(struct guest_info * info, addr_t read_gva, 
+                          int (*read)(addr_t read_addr, void * dst, uint_t length, void * priv_data), 
+                          addr_t read_gpa, void * private_data) {
+  struct basic_instr_info instr_info;
+  char instr[15];
+  int ret;
+  struct emulated_page * data_page = V3_Malloc(sizeof(struct emulated_page));
+  addr_t data_addr_offset = PT32_PAGE_OFFSET(read_gva);
+  pte32_t saved_pte;
+
+  PrintDebug("Emulating Read\n");
+
+  if (info->mem_mode == PHYSICAL_MEM) { 
+    ret = read_guest_pa_memory(info, get_addr_linear(info, info->rip, &(info->segments.cs)), 15, instr);
+  } else { 
+    ret = read_guest_va_memory(info, get_addr_linear(info, info->rip, &(info->segments.cs)), 15, instr);
+  }
+
+  PrintDebug("Instr (15 bytes) at %x:\n", instr);
+  PrintTraceMemDump(instr, 15);
+  
+  if (v3_basic_mem_decode(info, (addr_t)instr, &instr_info) == -1) {
+    PrintError("Could not do a basic memory instruction decode\n");
+    V3_Free(data_page);
+    return -1;
+  }
+
+  if (instr_info.has_rep == 1) {
+    PrintError("We currently don't handle rep* instructions\n");
+    V3_Free(data_page);
+    return -1;
+  }
+
+
+  data_page->page_addr = get_new_page();
+  data_page->va = PT32_PAGE_ADDR(read_gva);
+  data_page->pte.present = 1;
+  data_page->pte.writable = 0;
+  data_page->pte.user_page = 1;
+  data_page->pte.page_base_addr = PT32_BASE_ADDR(data_page->page_addr);
+
+
+  // Read the data directly onto the emulated page
+  if (read(read_gpa, (void *)(data_page->page_addr + data_addr_offset), instr_info.op_size, private_data) != instr_info.op_size) {
+    PrintError("Read error in emulator\n");
+    V3_FreePage((void *)(data_page->page_addr));
+    V3_Free(data_page);
+    return -1;
+  }
+
+  v3_replace_shdw_page32(info, data_page->va, &(data_page->pte), &saved_pte);
+
+
+  list_add(&(data_page->page_list), &(info->emulator.emulated_pages));
+  info->emulator.num_emulated_pages++;
+
+  if (saved_pte.present == 1) {
+    struct saved_page * saved_data_page = V3_Malloc(sizeof(struct saved_page));
+    saved_data_page->pte = saved_pte;
+    saved_data_page->va = PT32_PAGE_ADDR(read_gva);
+
+    list_add(&(saved_data_page->page_list), &(info->emulator.saved_pages));
+    info->emulator.num_saved_pages++;
+  }
+
+
+  setup_code_page(info, instr, &instr_info);
+
+  info->emulator.running = 1;
+  info->run_state = VM_EMULATING;
+  info->emulator.instr_length = instr_info.instr_length;
+
+  return 0;
+}
+
+
+
+int v3_emulate_memory_write(struct guest_info * info, addr_t write_gva,
+                           int (*write)(addr_t write_addr, void * src, uint_t length, void * priv_data), 
+                           addr_t write_gpa, void * private_data) {
+
+  struct basic_instr_info instr_info;
+  char instr[15];
+  int ret;
+  struct write_region * write_op = V3_Malloc(sizeof(struct write_region ));
+  struct emulated_page * data_page = V3_Malloc(sizeof(struct emulated_page));
+  addr_t data_addr_offset = PT32_PAGE_OFFSET(write_gva);
+  pte32_t saved_pte;
+
+  PrintDebug("Emulating Write\n");
+
+  if (info->mem_mode == PHYSICAL_MEM) { 
+    ret = read_guest_pa_memory(info, get_addr_linear(info, info->rip, &(info->segments.cs)), 15, instr);
+  } else { 
+    ret = read_guest_va_memory(info, get_addr_linear(info, info->rip, &(info->segments.cs)), 15, instr);
+  }
+  
+  if (v3_basic_mem_decode(info, (addr_t)instr, &instr_info) == -1) {
+    PrintError("Could not do a basic memory instruction decode\n");
+    V3_Free(write_op);
+    V3_Free(data_page);
+    return -1;
+  }
+
+  if (instr_info.has_rep == 1) {
+    PrintError("We currently don't handle rep* instructions\n");
+    V3_Free(write_op);
+    V3_Free(data_page);
+    return -1;
+  }
+
+
+  data_page->page_addr = get_new_page();
+  data_page->va = PT32_PAGE_ADDR(write_gva);
+  data_page->pte.present = 1;
+  data_page->pte.writable = 1;
+  data_page->pte.user_page = 1;
+  data_page->pte.page_base_addr = PT32_BASE_ADDR(data_page->page_addr);
+
+
+
+  write_op->write = write;
+  write_op->write_addr = write_gpa;
+  write_op->length = instr_info.op_size;
+  write_op->private_data = private_data;
+
+  write_op->write_data = (void *)(data_page->page_addr + data_addr_offset);
+
+  list_add(&(write_op->write_list), &(info->emulator.write_regions));
+  info->emulator.num_write_regions--;
+
+  v3_replace_shdw_page32(info, data_page->va, &(data_page->pte), &saved_pte);
+
+
+  list_add(&(data_page->page_list), &(info->emulator.emulated_pages));
+  info->emulator.num_emulated_pages++;
+
+  if (saved_pte.present == 1) {
+    struct saved_page * saved_data_page = V3_Malloc(sizeof(struct saved_page));
+    saved_data_page->pte = saved_pte;
+    saved_data_page->va = PT32_PAGE_ADDR(write_gva);
+
+    list_add(&(saved_data_page->page_list), &(info->emulator.saved_pages));
+    info->emulator.num_saved_pages++;
+  }
+
+
+  if (info->emulator.running == 0) {
+    setup_code_page(info, instr, &instr_info);
+    info->emulator.running = 1;
+    info->run_state = VM_EMULATING;
+    info->emulator.instr_length = instr_info.instr_length;
+  }
+
+  return 0;
+}
+
+
+// end emulation
+int v3_emulation_exit_handler(struct guest_info * info) {
+  struct saved_page * svpg, * p_svpg;
+  struct emulated_page * empg, * p_empg;
+  struct write_region * wr_reg, * p_wr_reg;
+  pte32_t dummy_pte;
+
+  // Complete the writes
+  // delete writes
+  // swap out emulated pages with blank dummies
+  // swap in saved pages
+  // increment rip
+  
+  PrintDebug("V3 Emulation Exit Handler\n");
+
+  list_for_each_entry_safe(wr_reg, p_wr_reg, &(info->emulator.write_regions), write_list) {
+    wr_reg->write(wr_reg->write_addr, wr_reg->write_data, wr_reg->length, wr_reg->private_data);
+    PrintDebug("Writing \n");
+    
+    list_del(&(wr_reg->write_list));
+    V3_Free(wr_reg);
+
+  }
+  info->emulator.num_write_regions = 0;
+
+
+  *(uint_t *)&dummy_pte = 0;
+  
+  list_for_each_entry_safe(empg, p_empg, &(info->emulator.emulated_pages), page_list) {
+    pte32_t empte32_t;
+
+    PrintDebug("wiping page %x\n", empg->va); 
+
+    v3_replace_shdw_page32(info, empg->va, &dummy_pte, &empte32_t);
+    V3_FreePage((void *)(empg->page_addr));
+
+    list_del(&(empg->page_list));
+    V3_Free(empg);
+  }
+  info->emulator.num_emulated_pages = 0;
+
+  list_for_each_entry_safe(svpg, p_svpg, &(info->emulator.saved_pages), page_list) {
+
+    PrintDebug("Setting Saved page %x back\n", svpg->va); 
+    v3_replace_shdw_page32(info, empg->va, &(svpg->pte), &dummy_pte);
+    
+    list_del(&(svpg->page_list));
+    V3_Free(svpg);
+  }
+  info->emulator.num_saved_pages = 0;
+
+  info->run_state = VM_RUNNING;
+  info->emulator.running = 0;
+  //info->rip += info->emulator.instr_length;
+
+  info->emulator.instr_length = 0;
+
+  PrintDebug("returning from emulation\n");
+
+  return 0;
+}
 
 
 
 /* mem_addr is the guest physical memory address */
-static int mem_hook_dispatch(struct guest_info * info, addr_t fault_addr, addr_t guest_phys_page,  pf_error_t access_info, struct vmm_mem_hook * hook) {
+static int mem_hook_dispatch(struct guest_info * info, 
+                            addr_t fault_gva, addr_t fault_gpa,  
+                            pf_error_t access_info, struct vmm_mem_hook * hook) 
+{
 
   // emulate and then dispatch 
   // or dispatch and emulate
 
 
   if (access_info.write == 1) {
-    void * src = NULL;
-    uint_t length = 0;
-
-    PrintDebug("Memory hook write\n");
-    return -1;
-
-    if (hook->write(fault_addr, src, length, hook->priv_data) != length) {
+    if (v3_emulate_memory_write(info, fault_gva, hook->write, fault_gpa, hook->priv_data) == -1) {
+      PrintError("Memory write emulation failed\n");
       return -1;
     }
+    
   } else {
-    PrintDebug("Memory hook read\n");
-    return -1;
+    if (v3_emulate_memory_read(info, fault_gva, hook->read, fault_gpa, hook->priv_data) == -1) {
+      PrintError("Memory read emulation failed\n");
+      return -1;
+    }
   }    
 
-  return -1;
+  return 0;
 }
 
 
-int handle_special_page_fault(struct guest_info * info, addr_t fault_addr, addr_t guest_phys_page, pf_error_t access_info) {
-  struct shadow_region * reg = get_shadow_region_by_addr(&(info->mem_map), guest_phys_page);
+int handle_special_page_fault(struct guest_info * info, 
+                             addr_t fault_gva, addr_t fault_gpa, 
+                             pf_error_t access_info) 
+{
+  struct shadow_region * reg = get_shadow_region_by_addr(&(info->mem_map), fault_gpa);
 
   switch (reg->host_type) {
   case HOST_REGION_HOOK:
-    return mem_hook_dispatch(info, fault_addr, guest_phys_page, access_info, (struct vmm_mem_hook *)(reg->host_addr));
+    return mem_hook_dispatch(info, fault_gva, fault_gpa, access_info, (struct vmm_mem_hook *)(reg->host_addr));
   default:
     return -1;
   }
 
 
 
 
-
 static int handle_shadow_pte32_fault(struct guest_info* info, 
                                     addr_t fault_addr, 
                                     pf_error_t error_code,
   return 0;
 }
 
+
+
+
+
+
+int v3_replace_shdw_page32(struct guest_info * info, addr_t location, pte32_t * new_page, pte32_t * old_page) {
+  pde32_t * shadow_pd = (pde32_t *)CR3_TO_PDE32(info->shdw_pg_state.shadow_cr3);
+  pde32_t * shadow_pde =  (pde32_t *)&(shadow_pd[PDE32_INDEX(location)]);
+
+  if (shadow_pde->large_page == 0) {
+    pte32_t * shadow_pt = (pte32_t *)PDE32_T_ADDR((*shadow_pde));
+    pte32_t * shadow_pte = (pte32_t *)&(shadow_pt[PTE32_INDEX(location)]);
+
+    //if (shadow_pte->present == 1) {
+    *(uint_t *)old_page = *(uint_t *)shadow_pte;
+    //}
+
+    *(uint_t *)shadow_pte = *(uint_t *)new_page;
+
+  } else {
+    // currently unhandled
+    return -1;
+  }
+  
+  return 0;
+}
+
+
+
+
+
+
 int handle_shadow_pagefault(struct guest_info * info, addr_t fault_addr, pf_error_t error_code) {
   
   if (info->mem_mode == PHYSICAL_MEM) {
       
     } else {
       // Handle hooked pages as well as other special pages
-      if (handle_special_page_fault(info, fault_addr, PT32_PAGE_ADDR(guest_fault_pa), error_code) == -1) {
+      if (handle_special_page_fault(info, fault_addr, guest_fault_pa, error_code) == -1) {
        PrintError("Special Page Fault handler returned error for address: %x\n", fault_addr);
        return -1;
       }
 
   if (shadow_pte_access == PT_ENTRY_NOT_PRESENT) {
 
-    addr_t guest_pa = PTE32_T_ADDR((*guest_pte));
+    addr_t guest_pa = PTE32_T_ADDR((*guest_pte)) +  PT32_PAGE_OFFSET(fault_addr);
 
     // Page Table Entry Not Present