/*
   TMInit.c, Copyright  (c) by Christian Conrad,
   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/Threads/TMInit.c,v $
	Author(s):             Christian Conrad
	Affiliation:           ETH Zuerich, TIK
	Version:               $Revision: 3041 $
	Creation Date:         
	Last Date of Change:   $Date: 2007-06-19 22:29:34 +0200 (Die, 19 Jun 2007) $      by: $Author: sryffel $
 
 */

#include "Topsy.h"
#include "TMHal.h"
#include "TMInit.h"
#include "TMClock.h"
#include "HashList.h"
#include "List.h"
#include "Support.h"
#include "Threads.h"
#include "TMIPC.h"
#include "TMScheduler.h"
#include "TMThread.h"
#include "TMProtectionDomain.h"
#include "TMError.h"
#include "vm_main.h"
#include "MemoryLayout.h"

/* Global variables */
List threadList;                  /* Contains all threads (user & kernel) */
extern HashList threadHashList;   /* Hash list of all threads */
extern Scheduler scheduler;       /* Scheduler structure */

// defined in TMMain along with mainUserPD
extern pdom_t		mainKernelPD;			/* the PD containing the kernel init stuff */

/* Statically allocated thread structures for tm and mm */
/* Memory Management Thread */
Thread*      mmThreadPtr;
/* Thread Management Thread */
Thread*      tmThreadPtr;
int          tmThreadPdbr;
/* Idle Thread */
Thread*      idThreadPtr;

static void dummyExceptionHandler(ThreadId id)
{
  WARNING("No exception handler has been set yet");
}

static void dummyInterruptHandler(void* arg)
{
  WARNING("No interrupt handler has been set yet");
}

void initBasicExceptions(void)
{
  int i = 0;

  /* setup the exception and interrupt handling at its correct place.
   * this is highly machine dependent (vectorized exceptions vs. software
   * dispatch (as on the mips).
   */
  tmInstallExceptionCode();

  //-CJ	ioConsolePutString("\ntmInstallExceptionCode returned");

  /* Initialization of default dummy exception handlers */
  for (i = 0; i < MAXNBOFEXCEPTIONS; i++)
  {
    tmSetExceptionHandler(i, dummyExceptionHandler);
  }

  for (i = 0; i < MAXNBOFINTERRUPTS; i++)
  {
    tmSetInterruptHandler(i, dummyInterruptHandler, NULL);
  }
}

extern void DEBUGhlt(void);

void tmInit( Address mmStack, Address tmStack,
             Address userInit)
{
  int err;
  Address mmStack2, tmStack2; // XXX: ignore the values from mm_main() in favor of a more generic solution
  size_t mmStackSize, tmStackSize;

  /* Initialisation of the scheduler data structures.
   * The first thread is passed as argument.
   */

  schedulerInit(tmThreadPtr);

  // init the thread hash & list and their lock
  lockInit(threadLock);
  threadHashList = hashListNew();
  threadList = listNew();

  // init PD
  pdInit();

  /* add the kernelPD to the list (is created in vm_domain.c boot up) */
  if ( pdAdd(mainKernelPD) != PDOK )
  {
    PANIC("tmInit: could not add kernel Protection Domain to the list.");
  }

  /* initialize the architecture dependent Thread Structures */
  if (tmHalInit() != TM_OK)
  {
    PANIC("tmInit: tmHalInit() failed.");
  }

  /* initialize ProcContext of the mmThread */
  if (tmHalInitThread(&mmThreadPtr, mainKernelPD->pdId, mainKernelPD->pdId, 
                      &mmStack2, &mmStack2, &mmStackSize, TM_INIT | TM_KERNEL) != TM_OK)
  {
    PANIC("tmInit: Initializing mmContext failed");
  }
 
  /* build mm and tm threads */
  err = threadBuild( MMTHREADID, mainKernelPD->pdId /* destPD */, 0, "mmThread",
                     mmThreadPtr->contextPtr,	mmStack2, mmStack2, (unsigned int)mmStackSize,
                     vm_main, (ThreadArg)0, KERNEL, mmThreadPtr,
                     KERNEL_PRIORITY_HIGH, KERNEL_PERM_MSK);
  
  if (err != TM_THREADBUILDOK)
    PANIC("threadBuild for mmThread failed");  
  // DisplayContext("threadBuild mmContext:", &mmContext);
  
  /* initialize ProcContext of the tmThread */
  if (tmHalInitThread(&tmThreadPtr, mainKernelPD->pdId, mainKernelPD->pdId, 
                      &tmStack2, &tmStack2, &tmStackSize, TM_INIT | TM_KERNEL) != TM_OK)
  {
    PANIC("tmInit: Initializing tmContext failed");
  }

  err = threadBuild( TMTHREADID, mainKernelPD->pdId /* destPD */, 0, "tmThread",
                     tmThreadPtr->contextPtr, tmStack2, tmStack2, (unsigned int)tmStackSize,
                     tmMain, (ThreadArg)userInit, KERNEL, tmThreadPtr,
                     KERNEL_PRIORITY_HIGH, KERNEL_PERM_MSK);

  if (err != TM_THREADBUILDOK)
    PANIC("threadBuild for tmThread failed");
  // DisplayContext("threadBuild mmContext:", &mmContext);
  
  tmThreadPdbr =  tmThreadPtr->pdPtr->vm_space.vm_pmap.tf_pdbr; // set the global Variable


  //-LR   threadBuild( IDLETHREADID, TMTHREADID, "idThread", &idContext,
  //-LR       idStack, TM_DEFAULTTHREADSTACKSIZE,
  //-LR       tmIdleMain, (ThreadArg)0, KERNEL, &idThread, IDLE_PRIORITY);
  //-LR

  //-LR   if ((idleThreadId = threadStart( (ThreadMainFunction)tmIdleMain,
  //-LR           NULL, KERNEL, "idleThread", TMTHREADID, IDLE_PRIORITY, 0L))
  //-LR       == TM_THREADSTARTFAILED) {
  //-LR     PANIC("idleThread could not be started");
  //-LR   }
  //-LR
  /* Store pointer to mm and tm in threadHashList hash table */
  hashListAdd(threadHashList, mmThreadPtr, mmThreadPtr->id);
  hashListAdd(threadHashList, tmThreadPtr, tmThreadPtr->id);

  //-LR	hashListAdd(threadHashList, &idThread, idThread.id);

  /* add mm and tm to threadList */
  listAddInFront(threadList, mmThreadPtr, NULL);
  listAddInFront(threadList, tmThreadPtr, NULL);

  //-LR	listAddInFront(threadList, &idThread, NULL);

  /* Setting of default handlers for SYSCALL and Hardware exceptions */
  tmInstallErrorHandlers();

  /* Initialize the Threads Locks */
  lockInit(&mmThreadPtr->lock);
  lockInit(&tmThreadPtr->lock);

  /* Get Locks */
  lock(&mmThreadPtr->lock);
  lock(&tmThreadPtr->lock);

  /* Threads mm and tm are set to ready status, scheduling decision */
  schedulerSetReady(mmThreadPtr);
  schedulerSetReady(tmThreadPtr);

  /* Clean Up */
  tmHalCleanupAfterThreadBuild(mmThreadPtr, mmStack2, TM_INIT | TM_KERNEL);
  tmHalCleanupAfterThreadBuild(tmThreadPtr, tmStack2, TM_INIT | TM_KERNEL);

  /* release locks */
  unlock(&mmThreadPtr->lock);
  unlock(&tmThreadPtr->lock);


  //-LR     TimerInit();                   /* Enable Working Interrupts 0x00 Timer 0 */

  /* Clock configuration */
  if (PREEMPTIVE_MODE)
  {
    if (setClockValue(CLOCK0, TIMESLICE, RATEGENERATOR) != TM_OK)
    {
      PANIC("Clock could not be properly configured");
    }
    printf ("Clock configured!\n");
  }

  //-LR	schedulerSetReady(&idThread);
  schedule();					/* first thread is picked */

  //-LR  enableAllInterruptsInContext(scheduler.running->contextPtr);
  //-CJ  printf("\n\n\n\n\n\n\n");
   printf("RestoreContext Name: %s\n", scheduler.running->name);
   printf("RestoreContext StackStart: %p\n", scheduler.running->stackStart);
   printf("RestoreContext StackEnd: %p\n", scheduler.running->stackEnd);
   printf("RestoreContext ContextPtr: %p\n", scheduler.running->contextPtr);
   DisplayContext("RestoreContext Context:", scheduler.running->contextPtr);
   //asm("hlt");

  printf("... going to the real thing(tm)\n");
  restoreContext(scheduler.running->contextPtr);
  /*** initialization of the kernel ends here, from this point
   *** we run in kernel mode with interrupts enabled.
   *** now this is the real thing (tm)...
   ***/
}

