/*
    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/sa1100/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 abort_prefetch(ThreadId currentThread, Register status, Register BadVAddr);
static void abort_data(ThreadId currentThread, Register status, Register BadVAddr);

void vm_install_exception_handlers(void)
{
  tmSetExceptionHandler(ABORT_PREFETCH, abort_prefetch);
  tmSetExceptionHandler(ABORT_DATA, abort_data);
}

inline static void lookup(ThreadId currentThread,
                          Register status,
                          Register BadVAddr,
                          char* errorString)
{
  Message msg;
  struct vm_pmap *pmap;
  ThreadPtr thread;

  /* then get the faulting thread */
  thread = schedulerRunning();
  ASSERT(thread != NULL && thread->pdPtr != NULL);

  pmap = &thread->pdPtr->vm_space.vm_pmap;

  //-CJ	printf("exc. lookup: %s BadVAddr %p\n", errorString, (address_t)BadVAddr);

  /* 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 ( currentThread == 0 )
    PANIC("vm_pmap_lookup failed and there is no VM Manager running");
  if ( currentThread == MMTHREADID )
    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 = currentThread;
  msg.enveloppe = NULL;
  msg.msg.vm_lookup.pdId = thread->pdPtr->pdId;
  msg.msg.vm_lookup.badVAddr = (address_t)BadVAddr;
  msg.msg.vm_lookup.type = vm_pmap_lookup_type(pmap, (address_t)BadVAddr);
  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.msg.tmKill.id = currentThread;

  ERROR(errorString);
  printRegisters();

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

static void abort_prefetch(ThreadId currentThread,
                           Register status, Register BadVAddr)
{
  /* there are three possibilities:
  	 - page miss. The requested instruction produced a miss.
  	 - misaligned access. The instruction was not word aligned.
  	 - break. Instruction was a break instruction.
  	 The problem is there are no cause flags so we need to guess 
  	 what happend. For the moment only page misses are allowed.
   */
  lookup(currentThread, status, BadVAddr, "abort prefetch");
}

static void abort_data(ThreadId currentThread,
                       Register status, Register BadVAddr)
{
  lookup(currentThread, status, BadVAddr, "abort data");
}
