/*
    Copyright  (c) by Claudio Jeker,
    Swiss Federal Institute of Technology,
    Computer Engineering and Networks Laboratory.
 
    TOPSY -- A Teachable Operating System.
             Implementation of a tiny and simple
             micro kernel for teaching purposes.
 
    For further information, please visit http://www.tik.ee.ethz.ch/~topsy
 
    This software is provided under the terms of the GNU General Public Licence.
 
    A full copy of the GNU GPL is provided in the file COPYING found in the 
    development root of Topsy.
*/
/*
	
	File:                  $Source: /home/ruf/cvs_Topsy/cvs/Topsy/VMemory/mips/Attic/vm_pmap_exc.c,v $
 	Author(s):             George Fankhauser, Claudio Jeker
 	Affiliation:           ETH Zuerich, TIK
 	Version:               $Revision: 2922 $
 	Creation Date:         
 	Last Date of Change:   $Date: 2007-03-19 17:48:52 +0100 (Mon, 19 Mär 2007) $      by: $Author: baderma $
		
*/

#include "Threads.h"
#include "Messages.h"
#include "TMHal.h"
#include "TMScheduler.h"
#include "TMProtectionDomain.h"
#include "Assert.h"
#include "Support.h"

#include "vm.h"

static void TLBModifiedError(ThreadPtr currentThread);
static void TLBLoadError(ThreadPtr currentThread);
static void TLBStoreError(ThreadPtr currentThread);
static void AddressLoadError(ThreadPtr currentThread);
static void AddressStoreError(ThreadPtr currentThread);
static void BusError(ThreadPtr currentThread);


void vm_install_exception_handlers(void)
{
  tmSetExceptionHandler(TLB_MOD, TLBModifiedError);
  tmSetExceptionHandler(TLB_LOAD, TLBLoadError);
  tmSetExceptionHandler(TLB_STORE, TLBStoreError);
  tmSetExceptionHandler(ADDR_ERR_LOAD, AddressLoadError);
  tmSetExceptionHandler(ADDR_ERR_STORE, AddressStoreError);
  tmSetExceptionHandler(BUSERR_INSTR, BusError);
  tmSetExceptionHandler(BUSERR_DATA, BusError);
}

inline static void lookup(ThreadPtr thread, int type, char* errorString)
{
  Message msg;
  struct vm_pmap *pmap;
  Register BadVAddr;
  Register epc; // acctually not needed
  extern int exceptionContextFlag;
  extern struct protection_domain kernel;

  epc = getEPC(); // not yet needed
  BadVAddr = getBADVM();

  if ( exceptionContextFlag == 1 )
  {
    ASSERT(thread != NULL && thread->pdPtr != NULL);
    pmap = &thread->pdPtr->vm_space.vm_pmap;
  }
  else
  {
    /* nested exception current PD is the kernel */
    pmap = &kernel.vm_space.vm_pmap;
  }

  //-CJ	printf("vm_pmap_lookup: %s EPC %p BadVAddr %p\n",
  //-CJ	errorString, (address_t)epc, (address_t)BadVAddr);
  switch ( vm_pmap_lookup(pmap, (address_t)BadVAddr, type) )
  {
  case VM_OK:
    return; /* all ok */
  default:
    /* the bootup thread has id 0 as soon the scheduler is running this
    	 thread is no longer existant and there is will be no thread with id
    	 0 again.
     */
    if ( thread->id == 0 )
      PANIC("vm_pmap_lookup failed and there is no VM Manager running");
    if ( thread->id == MMTHREADID )
    {
      printf("vm_pmap_lookup: %s EPC %p BadVAddr %p\n",
             errorString, (address_t)epc, (address_t)BadVAddr);
      PANIC("VM Manager caused a fault and vm_pmap_lookup failed");
    }
    /* Set us asleep. Until MMTHRED has done the lookup */
    schedulerSetBlocked(thread);

    /* message to MMTHREAD */
    msg.id = VM_LOOKUP;
    msg.from = thread->id;
    msg.enveloppe = NULL;
    msg.msg.vm_lookup.pdId = thread->pdPtr->pdId;
    msg.msg.vm_lookup.badVAddr = (address_t)BadVAddr;
    msg.msg.vm_lookup.type = type;
    kSend(MMTHREADID, &msg);
    /* OK, now it's MMTHREADs turn to find the address.
    	 If it was a bad address MMTHREAD will kill us.
    	 If all was OK MMTHREAD will use schedulerSetReady() to wake us up
    	 then we will run just from where we did the TLB miss.
     */

    return;
  }
}

static void DefaultError(ThreadId currentThread, char* errorString)
{
  Message msg;

  msg.id = TM_KILL;
  msg.from = MMTHREADID;
  msg.enveloppe = NULL;
  msg.msg.tmKill.id = currentThread;

  ERROR(errorString);
  printRegisters();

  /* immediate in-kernel delivery without syscall
   * kSend does a schedule after the message arrived
   */
  kSend(TMTHREADID, &msg);
}

/* Assertion of the Bus Error input during a read operation, due
	 to such external events as bus timeout, backplane memory errors,
	 invalid physical address, or invalid access types.
 */
static void BusError(ThreadPtr currentThread)
{
  DefaultError(currentThread->id, "bus error");
}

/* Address Error: Attempt to load, fetch, store an unaligned
	 word: that is, a word or halfword at an address not
	 evenly divisible by four or two, respectively. Also
	 caused by reference to a virtual address with most significant
	 bit set while in User Mode.
 */
static void AddressStoreError(ThreadPtr currentThread)
{
  DefaultError(currentThread->id, "address store error");
}

static void AddressLoadError(ThreadPtr currentThread)
{
  DefaultError(currentThread->id, "address load error");
}

/* During a store instruction, the Valid bit is set but the dirty
	 bit is not set in a matching TLB entry.
 */
static void TLBModifiedError(ThreadPtr currentThread)
{
  //-CJ	DefaultError(currentThread->id, "tlb modify error");
  lookup(currentThread, PMAP_WRITE, "tlb modify error");
}

/* A refernced TLB entry's Valid bit isn't set, or there is a
	 reference to a K2SEG page that has no matching TLB entry,
   or a reference is made (in either kernel or user
	 mode) to a page in KUSEG that has no matching TLB entry.
	 The UTLB Miss is also handled via this functions because
	 there is no difference between these calls.
 */
static void TLBLoadError(ThreadPtr currentThread)
{
  //-CJ	DefaultError(currentThread->id, "tlb load error");
  lookup(currentThread, PMAP_READ, "tlb load error");
}

static void TLBStoreError(ThreadPtr currentThread)
{
  //-CJ	DefaultError(currentThread, "tlb store error");
  lookup(currentThread, PMAP_WRITE, "tlb store error");
}
