Index: Makefile.in
===================================================================
RCS file: /cvsroot-freemware/plex86/Makefile.in,v
retrieving revision 1.5
diff -u -r1.5 Makefile.in
--- Makefile.in	2000/04/08 22:19:46	1.5
+++ Makefile.in	2000/04/20 05:58:53
@@ -46,6 +46,8 @@
 	$(MAKE) -C docs dist-clean
 	/bin/rm -f config.status config.cache config.log
 	/bin/rm -f Makefile config.h
+	/bin/rm -f `find . -name .#\*`
+	/bin/rm -f `find . -name \*~`
 
 Makefile:	Makefile.in config.status
 		CONFIG_FILES=Makefile CONFIG_HEADERS= $(SHELL) config.status
Index: plex86.h
===================================================================
RCS file: /cvsroot-freemware/plex86/plex86.h,v
retrieving revision 1.2
diff -u -r1.2 plex86.h
--- plex86.h	2000/03/26 18:17:48	1.2
+++ plex86.h	2000/04/20 05:58:54
@@ -156,10 +156,13 @@
 #define RET_BECAUSE_MON_ERROR 12
 #define RET_BECAUSE_REMAP     13
 #define RET_BECAUSE_USEREMU   14
+#define RET_BECAUSE_KERNELEMU 15
 
 #define MON_ACTION_RAISE_INT  0x80
 #define MON_ACTION_SET_VIP    0x81
+#define MON_ACTION_GOT_PAGE   0x82
 
+/* instructions for emulation in host's user space */
 #define EMU_INSTR_OUT_8          1
 #define EMU_INSTR_OUT_16         2
 #define EMU_INSTR_OUT_32         3
@@ -185,5 +188,9 @@
 
 #define EMU_INSTR_INT           20
 #define EMU_INSTR_STI           21
+
+
+/* for host's kernel space */
+#define NEED_PAGE                1
 
 #endif  // #ifndef __PLEX86_H__
Index: kernel/Makefile.in
===================================================================
RCS file: /cvsroot-freemware/plex86/kernel/Makefile.in,v
retrieving revision 1.4
diff -u -r1.4 Makefile.in
--- kernel/Makefile.in	2000/03/26 17:30:27	1.4
+++ kernel/Makefile.in	2000/04/20 05:58:54
@@ -50,7 +50,7 @@
 
 
 $(KERNEL_TARGET): $(HOST_O) monitor.o nexus.o fault.o emulation.o \
-		prescan.o fetchdecode.o
+		prescan.o fetchdecode.o page-fault.o trap.o
 	$(LD) $(KLDFLAGS) $^ -o $@
 
 clean:
Index: kernel/emulation.c
===================================================================
RCS file: /cvsroot-freemware/plex86/kernel/emulation.c,v
retrieving revision 1.4
diff -u -r1.4 emulation.c
--- kernel/emulation.c	2000/04/11 19:04:05	1.4
+++ kernel/emulation.c	2000/04/20 05:58:54
@@ -1380,17 +1736,111 @@
     Bit32u new_eip;
     Bit32u eip = context->eip + 1;
 
-    new_eip = read_guest_dword(vm, &eip);
-    new_cs = read_guest_word(vm, &eip);
+    new_eip = read_guest_dword (vm, &eip);
+    new_cs = read_guest_word (vm, &eip);
 
-    read_guest_descriptor(vm, &descr, new_cs);
+    read_guest_descriptor (vm, &descr, new_cs);
     if (!(descr.type & D_CODE))
-        return 0;
+	return 0;
 
-    codeseg_activate(vm,  context->cs, 0 );
+    codeseg_activate (vm, context->cs, 0);
     context->cs = new_cs | 3;
     context->eip = new_eip;
-    codeseg_activate(vm,  new_cs, 1 );
+    codeseg_activate (vm, new_cs, 1);
+
+    return 1;
+}
 
+int
+emulate_set_pdbr (vm_t * vm, guest_context_t * context, Bit32u cr3)
+{
+    Bit32u dummy, addr;
+    int   i;
+    pageEntry_t *pte;
+
+    addr = cr3;
+    pte = (pageEntry_t *) & dummy;
+
+    /* cr3 points to a page directory
+     * read all entries of this page and unmap page tables
+     */
+    for (i = 0; i < 1024; i++)
+    {
+	dummy = read_guest_dword (vm, &addr);
+	if (pte->P == 1)
+	{
+	    unmap_page_from_guest (vm, vm->common.pages.guest[pte->base]);
+	}
+    }
+
+    /* unmap the page directory itself */
+    if(!unmap_page_from_guest (vm, vm->common.pages.guest[cr3 >> 12]))
+      return 0;
+
+#if 0
+    context->event_info = EMU_INSTR_HLT | (RET_BECAUSE_USEREMU << 8);
+    context->error = cr3 - vm->common.addr->nexus->mon_base;
+    return 4;
+#endif
+
     return 1;
+}
+
+
+int
+unmap_page_from_guest (vm_t * vm, Bit32u page)
+{
+    Bit32u pdi, pti;
+    pageEntry_t pte;
+    Bit32u cr3;
+
+    for (pdi = 0; pdi < (vm->common.pages.guest_n_megs >> 2); pdi++)
+    {
+	/* browse through all entries in page directory */
+
+	for (pti = 0; pti < 1024; pti++)
+	{
+	    /* look into all entries of the page table */
+	    pte = vm->common.addr->page_tbl[pdi].u.pte[pti];
+	    if (pte.base == page)
+	    {
+		vm->common.addr->page_tbl[pdi].u.pte[pti].US = 0;	// mark as not present in page table;
+
+		asm volatile ("invlpg %0"::"m" (page << 12));	// remove from TLB;
+		/* above doesn't work - why? 
+		* flush whole TLB :( */
+		asm volatile ("movl %%cr3, %0" : "=r" (cr3));
+		asm volatile ("movl %0, %%cr3" : : "r" (cr3));
+
+
+		return 1;
+	    }
+	}
+    }
+    return 0;
+}
+
+int
+map_page_into_guest (vm_t * vm, Bit32u page)
+{
+    Bit32u pdi, pti;
+    pageEntry_t pte;
+
+    for (pdi = 0; pdi < (vm->common.pages.guest_n_megs >> 2); pdi++)
+    {
+	/* browse through all entries in page directory */
+
+	for (pti = 0; pti < 1024; pti++)
+	{
+	    /* look into all entries of the page table */
+	    pte = vm->common.addr->page_tbl[pdi].u.pte[pti];
+	    if (pte.base == page)
+	    {
+		vm->common.addr->page_tbl[pdi].u.pte[pti].US = 1;	// mark as not present in page table;
+		
+		return 1;
+	    }
+	}
+    }
+    return 0;
 }
Index: kernel/fault.c
===================================================================
RCS file: /cvsroot-freemware/plex86/kernel/fault.c,v
retrieving revision 1.3
diff -u -r1.3 fault.c
--- kernel/fault.c	2000/03/26 16:52:47	1.3
+++ kernel/fault.c	2000/04/20 05:58:54
@@ -23,6 +23,9 @@
 #include "plex86.h"
 #include "monitor.h"
 
+extern int emulate_trap(vm_t *, guest_context_t *);
+extern int emulate_page_fault(vm_t *, guest_context_t *);
+
 /*
  * Access to nexus page of current VM from monitor space.  I moved
  * this macro to this file from the header files, so it would not
@@ -78,6 +81,7 @@
     if ( context != expected_context )
     {
         expected_context->event_info = RET_BECAUSE_MON_ERROR<<8;
+	expected_context->error = 0x0001;
         return 0;
     }
 
@@ -85,10 +89,18 @@
     {
     /* FIXME */
 
+    case 1:
+      action = emulate_trap(vm, context);
+      break;
+
     case 13:  /* GPF */
         action = emulate(vm, context );
         break;
 
+    case 14: /* Page Fault */
+        action = emulate_page_fault(vm, context);
+	break;
+
     default: 
         action = emulate_int(vm,  context,
                              context->event_info & 0xff, context->eip );
@@ -109,12 +121,12 @@
     case 2:   /* emulation successful, need to remap monitor */
         context->event_info = RET_BECAUSE_REMAP << 8;
         return 0;
-    case 3:   /* emulation in user space */
+    case 3:   /* emulation on host side */
         return 0;
     }
 }
 
-void
+int
 monitor_action( guest_context_t *context )
 /*
  *  monitor_action():  monitor action handler
@@ -148,8 +160,10 @@
         nexus->mon_eflags |= FLG_VIP;
         break;
 
+
     default:
         /* Hmmm. */
         break;
     }
+    return 1;
 }
Index: kernel/host-linux.c
===================================================================
RCS file: /cvsroot-freemware/plex86/kernel/host-linux.c,v
retrieving revision 1.6
diff -u -r1.6 host-linux.c
--- kernel/host-linux.c	2000/04/08 22:15:59	1.6
+++ kernel/host-linux.c	2000/04/20 05:58:55
@@ -21,16 +21,16 @@
 #include "plex86.h"
 #include "monitor.h"
 
-#include <linux/config.h>
+//#include <linux/config.h>
 #include <linux/module.h>
-#include <linux/types.h>
-#include <linux/kernel.h>
-#include <linux/fs.h>
-#include <linux/mm.h>
+//#include <linux/types.h>
+//#include <linux/kernel.h>
+//#include <linux/fs.h>
+//#include <linux/mm.h>
 #include <linux/proc_fs.h>
 #include <linux/wrapper.h>
-#include <linux/version.h>
-#include <asm/irq.h>
+//#include <linux/version.h>
+//#include <asm/irq.h>
 
 #ifndef VERSION_CODE
 #  define VERSION_CODE(vers,rel,seq) ( ((vers)<<16) | ((rel)<<8) | (seq) )
@@ -127,7 +127,9 @@
 static int run_guest_loop(vm_t *vm);
 
 
+int fill_page_cache(vm_t *, page_t *);
 
+
 /************************************************************************/
 /* Structures / Variables                                               */
 /************************************************************************/
@@ -529,15 +531,19 @@
         save_flags(eflags);
         restore_flags(eflags & ~0x00004300); // clear NT/IF/TF
 
+	/* go to guest land ... */
         host2guest(vm);
 
+	/* ... back from guest land */
+
         vector = vm->host.guest_context->event_info & 0xff;
         event  = (vm->host.guest_context->event_info >> 8) & 0xff;
 
-        if ( event == RET_BECAUSE_REDIR )
-        {
+        switch ( event )
+	  {
+	  case RET_BECAUSE_REDIR:
             restore_flags(eflags & ~0x00000200); // restore all but IF
-            soft_int(vector);
+	    soft_int(vector);
             redir_cnt[vector]++;
 
             // Check whether we have any more CPU time
@@ -547,15 +553,32 @@
             // Unless we need to return to handle pending signals, restart VM
             if (!current_got_fatal_signal())
                 continue;
-        }
-        else
-        {
-            restore_flags(eflags);
-        }
+	    break;
+
+	  case RET_BECAUSE_KERNELEMU:
+	    printk( KERN_WARNING "plex86: emulation in host kernel context [0x%x]\n",vector);
+	    switch ( vector )
+	      {
+	      case NEED_PAGE:
+		{
+		  Bit32u page;
+		  page = get_free_page(GFP_KERNEL);
+		  //printk(KERN_WARNING "plex86: try to get a page for guest - got 0x%x\n",MAP_NR(page));
+		  /* FIXME: failes if page frame address > 256 MB */
+		  vm->host.guest_context->event_info = (MON_ACTION_GOT_PAGE << 8) | (MAP_NR(page) << 16);
+		  vm->host.guest_context->error = 0x5555;
+		  continue;
+		  break;
+		}
+		
+	      default:
+		printk(KERN_WARNING "plex86: error: unknown kernel emu function 0x%x\n",vector);
+	      }
+
+	    break;
 
+	  case RET_BECAUSE_EMERR:
 
-        // Display monitor emulation error messages
-        if ( event == RET_BECAUSE_EMERR )
             switch (vm->host.addr.nexus->debug_msg.msg_code) 
             {
             case EMU_CLI_MSG:
@@ -581,6 +604,11 @@
               break;
             }
 
+
+	  default:
+            restore_flags(eflags);
+	  }
+
         return 0;
     }
 }
@@ -697,6 +725,9 @@
     // Transition page table
     if (ad->transition_PT) free_page((u32)ad->transition_PT);
 
+    // Page cache
+    if (ad->page_cache) free_page((u32)ad->page_cache);
+
     // Nexus page
     if (ad->nexus) free_page((u32)ad->nexus);
 
@@ -790,16 +821,22 @@
     pg->transition_PT = MAP_NR(ad->transition_PT);
     if (!ad->transition_PT) goto error;
 
+    // Page cache
+    ad->page_cache = (page_t *)get_free_page(GFP_KERNEL);
+    pg->page_cache = MAP_NR(ad->page_cache);
+    if(!ad->page_cache) goto error;
+    fill_page_cache(vm, ad->page_cache);
+
     // Nexus page
     ad->nexus = (nexus_t *)get_free_page(GFP_KERNEL);
     pg->nexus = MAP_NR(ad->nexus);
     if (!ad->nexus) goto error;
 
-memset(&vm->common.code_cache, 0, sizeof(vm->common.code_cache));
-// Pages used for the code cache for scan-before-execute technique.
-// No need to zero out these pages.  When a new meta page is used, it
-// has to be zeroed on demand.  The virtualized page has to be copied
-// from the unvirtualized page. +++
+    memset(&vm->common.code_cache, 0, sizeof(vm->common.code_cache));
+    // Pages used for the code cache for scan-before-execute technique.
+    // No need to zero out these pages.  When a new meta page is used, it
+    // has to be zeroed on demand.  The virtualized page has to be copied
+    // from the unvirtualized page. +++
 
     // Virtualized code pages for prescan technique
     ad->icache = vmalloc(1 * 4096); // for now
@@ -882,6 +919,27 @@
  error:
     unalloc_vm_pages( vm );
     return -ENOMEM;
+}
+
+int
+fill_page_cache(vm_t *vm, page_t *pc)
+     /* fill the page cache */
+{
+  int i;
+  page_t *pp;
+
+  vm->common.page_cache_level = 0;
+  /* FIXME: take a proper value for number of pages in cache, 10 for now */
+  for(i=0; i<10; i++)
+    {
+      pp = (page_t *)get_free_page(GFP_KERNEL);
+      pc->u.pte[i].base = MAP_NR(pp);
+      pc->u.pte[i].P = 1;
+      vm->common.page_cache_level++;
+      //printk(KERN_WARNING "plex86: fill cache with page 0x%x\n",pc->u.pte[i].base);
+    }
+
+  return 1;
 }
 
 /* The end */
Index: kernel/monitor.c
===================================================================
RCS file: /cvsroot-freemware/plex86/kernel/monitor.c,v
retrieving revision 1.6
diff -u -r1.6 monitor.c
--- kernel/monitor.c	2000/04/11 19:04:05	1.6
+++ kernel/monitor.c	2000/04/20 05:58:55
@@ -252,10 +265,17 @@
      *  by the scan-before-execute (prescan) logic, as we run code
      *  from private virtualized pages.
      */
     vm->guest.addr.icache = (page_t *) (laddr - base);
     map_nexus_pages(vm->common.pages.icache, 1, &laddr, pageTable);
 
+    /* Page cache */
+    vm->guest.addr.page_cache = (page_t *) (laddr - base);
+    map_nexus_pages(&vm->common.pages.page_cache, 1, &laddr, pageTable);
 
+
     /*
      *  =====================
      *  Transition Page Table
@@ -652,6 +672,7 @@
              *  or an internal monitor error occurred previously.
              */
             vm->host.guest_context->event_info = RET_BECAUSE_MON_ERROR<<8;
+            vm->host.guest_context->error = 0x0002;
             return;
         }
 
@@ -693,6 +714,7 @@
         case RET_BECAUSE_REDIR:
         case RET_BECAUSE_EMERR:
         case RET_BECAUSE_USEREMU:
+	case RET_BECAUSE_KERNELEMU:
             /* Host needs to handle this situation */
             return;
         }
@@ -815,7 +839,7 @@
      */
 
     page_index = 0;
-    for (pdi=0; pdi<(vm->common.pages.guest_n_megs>>2); pdi++) 
+    for (pdi=0; pdi<(vm->common.pages.guest_n_megs>>2) + 1; pdi++) 
     {
         /* Fill in the PDE flags */
 
Index: kernel/nexus.S
===================================================================
RCS file: /cvsroot-freemware/plex86/kernel/nexus.S,v
retrieving revision 1.3
diff -u -r1.3 nexus.S
--- kernel/nexus.S	2000/03/26 16:52:47	1.3
+++ kernel/nexus.S	2000/04/20 05:58:55
@@ -61,7 +61,7 @@
 __mon_base:        ;.skip 4, 0
 __mon_cr3:         ;.skip 4, 0
 __mon_eflags:      ;.skip 4, 0
-
+	
 __guest_gdt_info:  ;.skip 6, 0
 __guest_idt_info:  ;.skip 6, 0
 __guest_ldt_sel:   ;.skip 2, 0
@@ -88,6 +88,8 @@
 
 __vm:                ;.skip 4, 0
 
+__callback_fun:      ;.skip 4, 0
+	
 __debug_msg:       ;.skip 12, 0
 
 
@@ -239,6 +241,8 @@
   movl $monitor_action, %eax 
   call *%eax                // Note: 'call monitor_action' doesn't work!
   addl $4, %esp
+  orl %eax, %eax       // If the monitor-side handler was unable to handle
+  je fault_to_host     // the fault, return to the host side
 
 back_to_guest:
 
Index: kernel/include/monitor.h
===================================================================
RCS file: /cvsroot-freemware/plex86/kernel/include/monitor.h,v
retrieving revision 1.4
diff -u -r1.4 monitor.h
--- kernel/include/monitor.h	2000/03/26 18:17:48	1.4
+++ kernel/include/monitor.h	2000/04/20 05:58:55
@@ -157,6 +157,8 @@
 
   void          *vm; // guest pointer to vm_t structure.
 
+  Bit32u         callback_fun;
+
   // Debug messages returned from monitor-side fault handlers
   // to the host-side code.
   debug_msg_t    debug_msg;
@@ -172,6 +174,7 @@
   Bit32u cr1;
   Bit32u cr2;
   Bit32u cr3;
+  Bit32u cr4;
   } guest_cpu_state_t;
 
 
@@ -218,7 +221,9 @@
 #define MON_TSS_PAGES       BYTES2PAGES(MON_TSS_SIZE)
 
 #define MON_GUEST_PAGES    (PLEX86_MAX_PHY_MEGS * 256)
-#define MON_PAGE_TABLES    ((PLEX86_MAX_PHY_MEGS+3) >> 2)
+
+// +1 for dynamic system structure extension
+#define MON_PAGE_TABLES    ((PLEX86_MAX_PHY_MEGS+3) >> 2) + 1 
 
 #define MAX_VM_STRUCT_PAGES 6
 
@@ -256,6 +261,9 @@
     // between host and monitor spaces.
     Bit32u transition_PT;
 
+    // A page cache is a pool for pages ;)
+    Bit32u page_cache;
+
     // Physical addresses of host pages which comprise the actual
     // monitor structures.  These will be mapped into the current
     // guest task's linear address space as well.
@@ -284,6 +292,7 @@
     page_t       *page_tbl;
     page_t       *nexus_page_tbl;
     page_t       *transition_PT;
+    page_t       *page_cache;
 
     nexus_t      *nexus;
     gate_t       *idt;
@@ -366,6 +375,7 @@
       code_cache_entry_t code_cache[1]; // only 1 for now
       vm_pages_t         pages;  // memory pages allocated by the host
       guest_cpu_state_t  guest_cpu_state;
+      Bit32u             page_cache_level; //how many pages are in page cache?
 
       // We need to keep track of what each of the guest's physical
       // pages contains, and maintain some additional attributes.
Index: user/emulation.c
===================================================================
RCS file: /cvsroot-freemware/plex86/user/emulation.c,v
retrieving revision 1.2
diff -u -r1.2 emulation.c
--- user/emulation.c	2000/03/26 15:52:14	1.2
+++ user/emulation.c	2000/04/20 05:58:55
@@ -261,6 +261,8 @@
     case EMU_INSTR_HLT:
 	/* FIXME: should only shut down if interrupts are off ("cli; hlt") */
 	fprintf (stderr, "HLT -- shutting down virtual machine\n");
+	fprintf (stderr, "   *** 0x%x *** \n",context.error);
+	vm_debug_exception();
 	vm_abort ();
 	break;
 
Index: user/plex86.conf
===================================================================
RCS file: /cvsroot-freemware/plex86/user/plex86.conf,v
retrieving revision 1.4
diff -u -r1.4 plex86.conf
--- user/plex86.conf	2000/04/12 07:42:18	1.4
+++ user/plex86.conf	2000/04/20 05:58:55
@@ -12,7 +12,7 @@
 #plugin = ./plugins/ice/plugin-ice.so 3141
 
 #plugin = ./plugins/bios/plugin-bios.so ../guest/test/kernel
-#plugin = ./plugins/bios/plugin-bios.so ../guest/virtcode/kernel
+#plugin = ./plugins/bios/plugin-bios.so ../guest/virtcode/virtcode
 #plugin = ./plugins/bios/plugin-bios.so ../guest/cooperative/kernel
 plugin = ./plugins/bios/plugin-bios.so ../guest/preemptive/kernel
 #plugin = ./plugins/bios/plugin-bios.so ../guest/paging/kernel
Index: user/user.c
===================================================================
RCS file: /cvsroot-freemware/plex86/user/user.c,v
retrieving revision 1.5
diff -u -r1.5 user.c
--- user/user.c	2000/04/08 23:20:24	1.5
+++ user/user.c	2000/04/20 05:58:56
@@ -257,6 +257,7 @@
 
 	switch ( (context.event_info >> 8) & 0xff )
 	{
+	case RET_BECAUSE_KERNELEMU:
 	case RET_BECAUSE_REDIR:
 	    break;
 
@@ -266,6 +267,7 @@
 
 	case RET_BECAUSE_MON_ERROR:
 	    fprintf(stderr, "Fatal monitor error\n");
+	    fprintf(stderr, " *** 0x%x ***\n",context.error);
 	    if (vm_debug_exception())
 		vm_abort();
 	    break;


-------------------------------------------------------
trap.c:

/*
 *  plex86: run multiple x86 operating systems concurrently
 *  Copyright (C) 1999, 2000 The plex86 Team
 *
 *  emulation.c:  This file contains functions to emulate IA32 instructions
 *                
 *  This library is free software; you can redistribute it and/or
 *  modify it under the terms of the GNU Lesser General Public
 *  License as published by the Free Software Foundation; either
 *  version 2 of the License, or (at your option) any later version.
 *
 *  This library is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 *  Lesser General Public License for more details.
 *
 *  You should have received a copy of the GNU Lesser General Public
 *  License along with this library; if not, write to the Free Software
 *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
 */

#include "plex86.h"
#include "monitor.h"

extern int map_page_into_guest (vm_t *, Bit32u);
extern int unmap_page_from_guest (vm_t *, Bit32u);

int
emulate_trap(vm_t *vm, guest_context_t *context)
{
  Bit32u addr;

  /* FIXME: for now, we assume the trap comes from virtual page table emulation */

  addr = vm->common.addr->nexus->debug_msg.para1;

  /* ok, guest has read or written... */
  if(!unmap_page_from_guest(vm, vm->common.pages.guest[(addr & 0xfffff000) >> 12]))
    {
      context->event_info = EMU_INSTR_HLT | (RET_BECAUSE_USEREMU << 8);
      context->error = 0x4112;
      return 3;
    }

  return 1;
}


------------------------------------------------------------------
page-fault.c:

/*
 *  plex86: run multiple x86 operating systems concurrently
 *  Copyright (C) 1999, 2000 The plex86 Team
 *
 *  emulation.c:  This file contains functions to emulate IA32 instructions
 *                
 *  This library is free software; you can redistribute it and/or
 *  modify it under the terms of the GNU Lesser General Public
 *  License as published by the Free Software Foundation; either
 *  version 2 of the License, or (at your option) any later version.
 *
 *  This library is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 *  Lesser General Public License for more details.
 *
 *  You should have received a copy of the GNU Lesser General Public
 *  License along with this library; if not, write to the Free Software
 *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
 */

#include "plex86.h"
#include "monitor.h"

int virtual_page_table_fault(vm_t *, guest_context_t *, Bit32u);
int pager_handle_page_fault(vm_t *, guest_context_t *, Bit32u);

extern int map_page_into_guest (vm_t *, Bit32u);
extern int unmap_page_from_guest (vm_t *, Bit32u);

static inline Bit32u 
read_guest_dword (vm_t * vm, Bit32u * addr)
{
    Bit32u *ptr = (Bit32u *) (*addr - vm->common.addr->nexus->mon_base);

    *addr += 4;
    return *ptr;
}

static inline void 
write_guest_dword (vm_t * vm, Bit32u * addr, Bit32u val)
{
    Bit32u *ptr = (Bit32u *) (*addr - vm->common.addr->nexus->mon_base);

    *addr += 4;
    *ptr = val;
}

int
emulate_page_fault(vm_t *vm, guest_context_t *context)

{

  Bit32u cr2,d;

  asm volatile("movl %%cr2, %0" : "=r" (cr2)); // linear fault address;

  /* FIXME: do some checks:
   * comes the page fault from guest os kernel?
   * if so, is it a page table emulation page fault?
   * ...
   */

  if(vm->common.addr->nexus->guest_cpl > 0)
    {
      /* guest application causes page fault 
       * we should redirect page fault to guest kernel,
       * but this isn't implemented yet
       */
      context->event_info = EMU_INSTR_HLT | (RET_BECAUSE_USEREMU << 8);
      return 4;
    }


  if(virtual_page_table_fault(vm, context, cr2))
    return 1;

  /* let some other checks follow (i.e. for virtual GDT, IDT...) */

  /* cause wasn't a virtual system structure,
   * assume it's a page fault for our monitor pager
   * let the pager give the requested page to guest
   */

  if((d=pager_handle_page_fault(vm, context, cr2)))
    return d;

  /* ui, unknown page fault cause
   * that's a hard problem, let's stop guest
   */
  context->event_info = EMU_INSTR_HLT | (RET_BECAUSE_USEREMU << 8);
  context->error = cr2;
  return 3;
}

int
virtual_page_table_fault(vm_t *vm, guest_context_t *context, Bit32u addr)
     /* check whether it is a page fault caused by virtual page tables
      * addr is the linear page fault address
      * return 1 if so
      * return 0 if not
      */
{

  if( (addr & 0xfffff000) == vm->common.guest_cpu_state.cr3)
    /* fault while accessing the page directory */
    {

      /* now we have to emulate the read or write 
       * therefore, set the trap flag to execute the single faulting instruction
       * set the A and D bit correctly in virtual page table
       * and let the cpu execute the instruction
       * if it was a read, guest read the right bits
       * if it was a write, we have to update the entry in real page table
       */

      /* make page accesseble for guest */
      if(!map_page_into_guest(vm, vm->common.pages.guest[(addr & 0xfffff000) >> 12]))
	{
	  context->event_info = EMU_INSTR_HLT | (RET_BECAUSE_USEREMU << 8);
	  context->error = 0x4111;
	  return 3;
	}

      /* FIXME: save fault address */
      vm->common.addr->nexus->debug_msg.para1 = addr;


      /* FIXME: we should set A and D bit here */

      context->eflags |= 0x100; // set trap flag;
      return 1;

    }

  return 0; // not caused by virtual page tables;
}


int 
pager_handle_page_fault(vm_t *vm, guest_context_t *context, Bit32u addr)
     /* a pager for guest
      * called from general page fault handle routine
      * resolves page fault by browsing through virtual page tables to find the right entry,
      * translates the virtual page frame to a real page frame and
      * enters the real page frame in the real used page table
      */
{
  Bit32u pd_offset, pd_addr, pd_entry, real_page_frame, pdi, pti, emulated_pdi, emulated_pti;
  Bit32u d;
  pageEntry_t *pde, newEntry;
  Bit32u pageTable;
  

  pd_offset = (addr >> 22) * 4;
  pd_addr = pd_offset+vm->common.guest_cpu_state.cr3;
  pd_entry = read_guest_dword(vm, &pd_addr);

  if(pd_entry & 0x80)
    /* this is a superpage */
    {
      /* calculate page frame nr and
       * get real page frame from virtual page frame
       *                                         super page nr   * 2^10  +    page table offset  = virtual page frame */
      real_page_frame = vm->common.pages.guest[((pd_entry >> 22) * 1024) + ((addr >> 12) & 0x3ff)];

      /* map real page frame into monitor's address space at address "addr & 0xfffff000" 
       * may be that there is the nexus - remap nexus if so 
       */
      emulated_pdi = (addr >> 22);
      emulated_pti = (addr >> 12) & 0x3ff;
      pde = vm->common.addr->page_dir;
      if(pde[emulated_pdi].P == 0)
	{
	  /* uh, we have a problem - there is no page table where we need one
	   * try to get another page from host which we can use as page table
	   */
	  if(vm->common.page_cache_level == 0)
	    {
	      context->event_info = NEED_PAGE | (RET_BECAUSE_KERNELEMU << 8);
	      context->error = 0x4113;
	      return 3;
	    }
	  else
	    {
	      /* there is an empty page table entry, but we have a page in page cache
	       * go on with this page...
	       * we have to insert this page in our (monitor) address space to have access
	       * ourself and
	       * insert the page as page table at the (from guest) expected place
	       */

	      /* search a free entry in monitor's page tables for the new page */
	      for (pdi = 0; pdi < (vm->common.pages.guest_n_megs >> 2) + 1; pdi++)
		{
		  for(pti=0;pti<1024;pti++)
		    {
		      if(vm->common.addr->page_tbl[pdi].u.pte[pti].P == 0)
			{
			  /* ok, found a free place in monitors address space, now insert the page
			   * we want to use as emulated page table there so we can access it ourself
			   */
			  d = vm->common.addr->page_tbl[pdi].u.pte[pti].base = 
			    vm->common.addr->page_cache->u.pte[--vm->common.page_cache_level].base;
			  vm->common.addr->page_tbl[pdi].u.pte[pti].D = 0;
			  vm->common.addr->page_tbl[pdi].u.pte[pti].A = 0;
			  vm->common.addr->page_tbl[pdi].u.pte[pti].PCD = 0;
			  vm->common.addr->page_tbl[pdi].u.pte[pti].PWT = 0;
			  vm->common.addr->page_tbl[pdi].u.pte[pti].US = 0;
			  vm->common.addr->page_tbl[pdi].u.pte[pti].RW = 1;
			  vm->common.addr->page_tbl[pdi].u.pte[pti].P = 1;

			  /* virtual address of the page we just inserted */
			  pageTable = (pdi << 22) + (pti << 12); 
			  goto entry_found;
			}
		    }
		}
	      /* no free entry in monitor's page tables :( */
	      context->event_info = EMU_INSTR_HLT | (RET_BECAUSE_USEREMU << 8);
	      context->error = 0x4121;
	      return 3;
	    } /* if ... else (page_cache_level > 0) */

	entry_found:

	  /* prepare new page table for use as emulation page table for guest */
	  newEntry.base = real_page_frame;
	  newEntry.D = 0;
	  newEntry.A = 0;
	  newEntry.PCD = 0;
	  newEntry.PWT = 0;
	  newEntry.US = 1;
	  newEntry.RW = 1;
	  newEntry.P = 1;
	  pageTable += (addr >> 12) & 0x3ff; //offset in this page;
	  write_guest_dword(vm,&pageTable,*(Bit32u *)&newEntry);


	  /* insert the new emulation page table into monitor's page directory */
	  pde[emulated_pdi].base = d;
	  pde[emulated_pdi].D = 0;
	  pde[emulated_pdi].A = 0;
	  pde[emulated_pdi].PCD = 0;
	  pde[emulated_pdi].PWT = 0;
	  pde[emulated_pdi].US = 0;
	  pde[emulated_pdi].RW = 1;
	  pde[emulated_pdi].P = 1;

	}
      else
	{
	  /* there is a page table for this already, fine!
	   * go on with this page table
	   */


	  /* ehm... in theory... */
#if 0
	  vm->common.addr->page_tbl[emulated_pdi].u.pte[emulated_pti].base = real_page_frame;
	  vm->common.addr->page_tbl[emulated_pdi].u.pte[emulated_pti].D = 0;
	  vm->common.addr->page_tbl[emulated_pdi].u.pte[emulated_pti].A = 0;
	  vm->common.addr->page_tbl[emulated_pdi].u.pte[emulated_pti].PCD = 0;
	  vm->common.addr->page_tbl[emulated_pdi].u.pte[emulated_pti].PWT = 0;
	  vm->common.addr->page_tbl[emulated_pdi].u.pte[emulated_pti].US = 1;
	  vm->common.addr->page_tbl[emulated_pdi].u.pte[emulated_pti].RW = 1;
	  vm->common.addr->page_tbl[emulated_pdi].u.pte[emulated_pti].P = 1;
#endif
	  context->event_info = EMU_INSTR_HLT | (RET_BECAUSE_USEREMU << 8);
	  context->error = emulated_pdi;
	  return 3;
	}


      //context->event_info = EMU_INSTR_HLT | (RET_BECAUSE_USEREMU << 8);
      //context->error = 0x4115;
      //return 3;

    } /* end if(pd_entry & 0x80) (superpage?) */
  else
    {

      /* it's no superpage */
      context->event_info = EMU_INSTR_HLT | (RET_BECAUSE_USEREMU << 8);
      context->error = 0x4120;
      return 3;
    }

  return 3;
}
