/*
  TMHal.c, Copyright  (c) by Lukas Ruf, lr@lpr.ch,
  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/ia32/TMHal.c,v $
 	Author(s):             Lukas Ruf, lr@lpr.ch
 	Affiliation:           ETH Zuerich, TIK
 	Version:               $Revision: 3048 $
 	Creation Date:         
 	Last Date of Change:   $Date: 2007-07-20 18:11:54 +0200 (Fre, 20 Jul 2007) $      by: $Author: sryffel $
  */
#include "IOHal.h"
#include "MMHal.h"
#include "Support.h"
#include "TMHal.h"
#include "TMHalAsm.h"
#include "TMClock.h"
#include "TMError.h"
#include "TMScheduler.h"
#include "TMIPC.h"
#include "MemoryLayout.h"
#include "Debug.h"
#include "System.h"
#include "Assert.h"

#include "vm.h"
#include "vm_calls.h"
#include "vm_extern.h"
#include "vm_pmap.h"
#include "vm_pmap_hal.h"
#include "vm_pmap_asm.h"

#include "Exception.h"
#include "tm-include.h"
#include "TMProtectionDomain.h"

#include "Video.h"
#include "SupportAsm.h"
#include "Tools.h"

#include "cpu.h"

#ifdef APIC
#include "TMHalApicAsm.h"
#include "TMHalApic.h"
#endif

/* simplifications */
extern Scheduler scheduler;
// extern char *ExMsg[];

extern Thread* tmThreadPtr;

/* Segment Selectors are all defined in MMHal.c */
// extern unsigned long KCSEL;  /* KERNEL Code Segment Selector */
// extern unsigned long KDSEL;  /* KERNEL Data Segment Selector */
// extern unsigned long UCSEL;  /* USER   Code Segment Selector */
// extern unsigned long UDSEL;  /* USER   Data Segment Selector */

static int m_NoCalled = 0;

static volatile unsigned int *preemptedPtr;   /* Points to the preempted counter of the currently running thread */

static char *ExMsg[] = {                   /* Exception no. */
                         "Division by zero",                     /* Ex_00 */
                         "Debug",                                /* Ex_01 */
                         "NMI",                                  /* Ex_02 */
                         "Breakpoint",                           /* Ex_03 */
                         "INTO",                                 /* Ex_04 */
                         "BOUNDS",                               /* Ex_05 */
                         "Invalid Opcode",                       /* Ex_06 */
                         "CoPro not Available",                  /* Ex_07 */
                         "Double-Fault",                         /* Ex_08 */
                         "CoPro segment overrrun",               /* Ex_09 */
                         "Invalid TSS",                          /* Ex_0A */
                         "Segment Not Present",                  /* Ex_0B */
                         "Stack Exception",                      /* Ex_0C */
                         "General Protection",                   /* Ex_0D */
                         "Page Fault",                           /* Ex_0E */
                         "Reserved",                             /* Ex_0F */
                         "FPU error",                            /* Ex_10 */
                         "Alignment check (>=i486)",             /* Ex_11 */
                         "Machine check (>=Pentium)"             /* Ex_12 */
                       };

static char *IntMsg[] = {
                          "Timer Wrapper",                        /* IRQ_00 -> 20 */
                          "Keyboard",                             /* IRQ_01 -> 21 */
                          "Slave IC",                             /* IRQ_02 -> 22 */
                          "Serial Port II",                       /* IRQ_03 -> 23 */
                          "Serial Port I",                        /* IRQ_04 -> 24 */
                          "Parallel Port II",                     /* IRQ_05 -> 25 */
                          "Floppy",                               /* IRQ_06 -> 26 */
                          "Parallel Port I",                      /* IRQ_07 -> 27 */
                          "RTC",                                  /* IRQ_08 -> 28 */
                          "avail.",                               /* IRQ_09 -> 29 */
                          "avail.",                               /* IRQ_0A -> 2A */
                          "avail.",                               /* IRQ_0B -> 2B */
                          "avail.",                               /* IRQ_0C -> 2C */
                          "CoProcessor",                          /* IRQ_0D -> 2D */
                          "Harddisk Controller",                  /* IRQ_0E -> 2E */
                          "not yet known IRQ",                    /* IRQ_0F -> 2F */
                          "Software Interrupt for SYSCALL"        /* IRQ_10 -> 30 */
                        };


/* Exception service branch table (exception handlers).
 * - 32 exception handlers,
 */
ExceptionHandler exceptionHandlerTable[MAXNBOFEXCEPTIONS];

/* Hardware interrupt service branch table (interrupt handlers) and
 * corresponding arguments that may be passed to the interrupt handler.
 * - 16 hw interrupts
 * -  1 sw interrupts 
 */
InterruptHandler interruptHandlerTable[MAXNBOFINTERRUPTS];
void* interruptHandlerArgTable[MAXNBOFINTERRUPTS];

void tmInstallExceptionCode()
{
  int i = 0;
  for (i = 0; i < MAXNBOFINTERRUPTS; i++)
  {
    interruptHandlerArgTable[i] = NULL;
    interruptHandlerTable[i] = NULL;
  }
  for (i = 0; i < MAXNBOFEXCEPTIONS; i++)
    exceptionHandlerTable[i] = NULL;

  /* Set Clock Handler: Vital for correct functionality :-) */
  tmSetInterruptHandler(AIR_00,(InterruptHandler)tmClockHandler,NULL);
}


/* InterruptHandler are enumareted conforming to their IDT-Slot.
   So, not to waste space, the interruptHandlerTable is an array of
   MAXNBOFINTERRUPTS addresses. To make the Interrupt No. fit into this array
   the Interrupt IDs are modulo MAXNBOFINTERRUPTS.
 */
InterruptHandler tmSetInterruptHandler( InterruptId intId, InterruptHandler intHdler, void* arg)
{
  InterruptHandler oldIntHdler = interruptHandlerTable[intId % MAXNBOFINTERRUPTS];

  interruptHandlerTable[intId % MAXNBOFINTERRUPTS]    = intHdler;
  interruptHandlerArgTable[intId % MAXNBOFINTERRUPTS] = arg;

  return oldIntHdler;
}

/*
     ExceptionHandlers are enumareted conforming to their IDT-Slot. As Intel did
     preserve the 32 first slots the ExceptionHandlers' ID resembles the Address
     position in the array.
 */
ExceptionHandler tmSetExceptionHandler( ExceptionId excId, ExceptionHandler excHdler)
{
  ExceptionHandler oldExcHdler = exceptionHandlerTable[excId];

  exceptionHandlerTable[excId] = excHdler;
  return oldExcHdler;
}

/*
   The intDispatcher is called by the General Interrupt Handler. The ProcContext
   includes the executed IDT-Slot ID. So the Interrupt ID is retrieved by IDT-Slot
   ID modulo MAXNBOFINTERRUPTS.
   
   Please note: If an exception occurs in any case tmError() is correctly called
      by default even if no exception handlers are installed. I decided to provide
      this functionality as there is no sense in calling a single exception handler
      that executes nothing than a string deliverance. I collected all possible
      Exception Codes in Interrupt.c. Therefore I includeded the "extern *ExMsg[]"
      statement above.
      Implementation of specialized Exception Handlers is provided through normal
      function calls -- as used before :-)  
 */
void intDispatcher(ProcStackFramePtr frame)
{
  /* we call here the handler for interrupt offset with
   * its (optionally) defined argument
   */
  int i = frame->tf_trapno;


  if (i < T_IR_00)
  {
    i %= MAXNBOFEXCEPTIONS; /* Make sure no array access-overflow will occur */
    if (exceptionHandlerTable[i])
      (exceptionHandlerTable[i])(scheduler.running->id);
    else
      tmError(scheduler.running->id,ExMsg[i]);
  }
  else
  {
    i %= MAXNBOFINTERRUPTS;

    if (interruptHandlerTable[i])
      (interruptHandlerTable[i])(interruptHandlerArgTable[i]);
    else
      tmError(scheduler.running->id,"no appropriate IRQ-Handler installed.");
  }
}
/******************************************************************************/
void enableInterruptInContext( InterruptId intId, ProcContextPtr contextPtr)
{}

void disableInterruptInContext( InterruptId intId, ProcContextPtr contextPtr)
{}

void enableAllInterruptsInContext( ProcContextPtr contextPtr)
{
  contextPtr->sf->tf_eflags |= PSL_I;
}

/******************************************************************************/

void tmSetMachineDependentRegisters(ProcContextPtr contextPtr, pdom_t	destPD)
{
  /* Set Segment Selectors */ 
  contextPtr->sf->tf_cs = destPD->vm_space.vm_pmap.code_sel;
  contextPtr->sf->tf_ds = destPD->vm_space.vm_pmap.data_sel;
  contextPtr->sf->tf_es = destPD->vm_space.vm_pmap.data_sel;
  contextPtr->sf->tf_fs = destPD->vm_space.vm_pmap.data_sel;
  contextPtr->sf->tf_gs = destPD->vm_space.vm_pmap.data_sel;
  contextPtr->sf->tf_ss = destPD->vm_space.vm_pmap.data_sel;

  contextPtr->sf->tf_trapno = 0x0;  /* make sure, no error code is set -> else
                                     an interrupt reset will be performed */

  contextPtr->sf->tf_last = 0;      /* set pointer to the last Stack Frame initially to 0 */

  contextPtr->sf->tf_priority = IDLE_PRIORITY;    /* XXX */
  contextPtr->sf->tf_schedFlags = 0;              /* XXX */

  /* set the PDBR */
  contextPtr->sf->tf_pdbr = destPD->vm_space.vm_pmap.tf_pdbr;

}

void tmAdjustMachineDependentRegisters(ProcContextPtr contextPtr, AddressSpace space)
{
#if 0
  if (space == USER)
  {
    /* The automaticThreadExit "start" address must be adjusted too */
    *(int*)contextPtr->tf_esp = K_TO_U(*(int*)contextPtr->tf_esp);

    /* Now: directly adjust the top of stack pointer */
    contextPtr->tf_esp = K_TO_U(contextPtr->tf_esp);

    /* Please note: Now, NO MORE DIRECT STACK MANIPULATION !!!! */
  }
#endif
}

void tmSetReturnValue(ProcContextPtr contextPtr, Register value)
{
  contextPtr->sf->tf_eax = value;
}

void tmSetStackPointer(ProcContextPtr contextPtr, Register value)
{
  if (GET_THREAD(contextPtr) == GET_THREAD(value))
  {
    // sp points to the kernel mode stack -> kernel thread
    // kernel threads have no tf_esp -> nothing to do
    // check stack pointer
    ASSERT((int)value == (int)contextPtr->sf + sizeof(ProcStackFrame) - 2*sizeof(int));
  }
  else
  {
    /* Execution speed is extended by factor 2 if all memory access is
      to locations residing at multiples of four */
    contextPtr->sf->tf_esp = (Register)(((unsigned long)value >> 2)<<2);
  }
  //-LR   printf("tmSetStackPointer: 0x%8x\n", (int)contextPtr->tf_esp);
}

void tmSetProgramCounter(ProcContextPtr contextPtr, Register value)
{
  contextPtr->sf->tf_eip = value;
}

void tmSetStatusRegister(ProcContextPtr contextPtr, Register value)
{
  contextPtr->sf->tf_eflags = value;
}

void tmSetFramePointer(ProcContextPtr contextPtr, Register value)
{
  contextPtr->sf->tf_ebp = value;
}

void tmSetReturnAddress(ProcContextPtr contextPtr, Register value, Address stackPointer, Address stackPageBase)
{
  Register *threadLocalStack;
  threadLocalStack     = (Register *)(PAGEREMAINDER(stackPointer) + stackPageBase);
  threadLocalStack[0] = value;
}

void tmSetArgument0(ProcContextPtr contextPtr, Register value, Address stackPointer, Address stackPageBase)
{
  Register *threadLocalStack;
  threadLocalStack    = (Register*)(PAGEREMAINDER(stackPointer) + stackPageBase);
  threadLocalStack[1] = value;
}

void tmSetArgument1(ProcContextPtr contextPtr, Register value, Address stackPointer, Address stackPageBase)
{
  Register *threadLocalStack;
  threadLocalStack    = (Register *)(PAGEREMAINDER(stackPointer) + stackPageBase);
  threadLocalStack[2] = value;
}


/*Low Level Interrupt Handling and Message Adjustment*************************/

void CascStart()
{
  __enable_IRQ(0x22);           //  enable CASCADE
  __endOf_IRQ(0x22);
  return;
}


/**********************************************************/
/* 8259A reprogrammed in CoreLoad to map IRQs to 20h..2Fh */
/**********************************************************/
void InterruptInit()
{
  int i = 0;
  Set_Def_IDTEntry(0x20,(unsigned long)__NIR_00, INTGATE); // Timer Wrapper called in timer.S
  Set_Def_IDTEntry(0x21,(unsigned long)__NIR_01, INTGATE); // Keyboard
  Set_Def_IDTEntry(0x22,(unsigned long)__NIR_02, INTGATE); // Slave IC
  Set_Def_IDTEntry(0x23,(unsigned long)__NIR_03, INTGATE); // Serial Port II
  Set_Def_IDTEntry(0x24,(unsigned long)__NIR_04, INTGATE); // Serial Port I
  Set_Def_IDTEntry(0x25,(unsigned long)__NIR_05, INTGATE); // Parallel Port II
  Set_Def_IDTEntry(0x26,(unsigned long)__NIR_06, INTGATE); // Floppy
  Set_Def_IDTEntry(0x27,(unsigned long)__NIR_07, INTGATE); // Parallel Port I
  Set_Def_IDTEntry(0x28,(unsigned long)__NIR_08, INTGATE); // RTC
  Set_Def_IDTEntry(0x29,(unsigned long)__NIR_09, INTGATE); // avail.
  Set_Def_IDTEntry(0x2A,(unsigned long)__NIR_0A, INTGATE); // avail.
  Set_Def_IDTEntry(0x2B,(unsigned long)__NIR_0B, INTGATE); // avail.
  Set_Def_IDTEntry(0x2C,(unsigned long)__NIR_0C, INTGATE); // avail.
  Set_Def_IDTEntry(0x2D,(unsigned long)__NIR_0D, INTGATE); // CoProcessor
  Set_Def_IDTEntry(0x2E,(unsigned long)__NIR_0E, INTGATE); // Harddisk Controller
  Set_Def_IDTEntry(0x2F,(unsigned long)__NIR_0F, INTGATE); // reserved
  printf("Peripheral IRQ Handler : IRQs 0x00..0x0F (mapped to 0x20..0x2F)\n");
  Set_Def_IDTEntry(0x30,(unsigned long)__INT_30, SYSGATE); // Software Interrupt for User INT Gates
  printf("Internal   INT Handler : Installed at 0x30\n");
  printf("Blocking all unregistered Interrupts...");
  for (i = 0x31; (i < 256); i++)
    Set_Def_IDTEntry((char)i,(unsigned long)__INT_31, INTGATE);   /* Block all other gates */
  printf("Blocked !!\n");
#ifndef APIC
  CascStart();
#endif
  printf("Interrupt Request Line 0x02 (Cascade) started\n");
  return;
}

/* Exception Handling is included in Interrupt.c too as it makes use of the
   same functionality.
 */
void ExceptionInit()
{

  Set_Def_IDTEntry(0x00,(unsigned long)__NEx_00, TRAPGATE); /* Fault : Divide by 0  */
  Set_Def_IDTEntry(0x01,(unsigned long)__NEx_01, TRAPGATE); /* Fault : Debug        */
  Set_Def_IDTEntry(0x02,(unsigned long)__NEx_02, TRAPGATE); /* Trap  : NMI          */
  Set_Def_IDTEntry(0x03,(unsigned long)__NEx_03, TRAPGATE); /* Trap  : Breakpoint   */
  Set_Def_IDTEntry(0x04,(unsigned long)__NEx_04, TRAPGATE); /* Trap  : INTO         */
  Set_Def_IDTEntry(0x05,(unsigned long)__NEx_05, TRAPGATE); /* Fault : BOUNDS       */
  Set_Def_IDTEntry(0x06,(unsigned long)__NEx_06, TRAPGATE); /* Fault : Invalid Opcode */
  Set_Def_IDTEntry(0x07,(unsigned long)__NEx_07, TRAPGATE); /* Fault : CoPro not Available */
  Set_Def_IDTEntry(0x08,(unsigned long)__NEx_08, TRAPGATE); /* Abort : Double-Fault */
  Set_Def_IDTEntry(0x09,(unsigned long)__NEx_09, TRAPGATE); /* Abort : CoPro segment overrrun */
  Set_Def_IDTEntry(0x0A,(unsigned long)__NEx_0A, TRAPGATE); /* Fault : Invalid TSS */
  Set_Def_IDTEntry(0x0B,(unsigned long)__NEx_0B, TRAPGATE); /* Fault : Segment Not Present */
  Set_Def_IDTEntry(0x0C,(unsigned long)__NEx_0C, TRAPGATE); /* Fault : Stack Exception */
  Set_Def_IDTEntry(0x0D,(unsigned long)__NEx_0D, TRAPGATE); /* Fault : General Protection */
  Set_Def_IDTEntry(0x0E,(unsigned long)__NEx_0E, TRAPGATE); /* Fault : Page Fault */
  Set_Def_IDTEntry(0x0F,(unsigned long)__NEx_0F, TRAPGATE); /* Fault : Reserved */
  Set_Def_IDTEntry(0x10,(unsigned long)__NEx_10, TRAPGATE); /* Fault : FPU error */
  Set_Def_IDTEntry(0x11,(unsigned long)__NEx_11, TRAPGATE); /* Fault : Alignment check (i486 + ff) */
  Set_Def_IDTEntry(0x12,(unsigned long)__NEx_12, TRAPGATE); /* Abort : Machine check (Pentium + ff) */

  Set_Def_IDTEntry(0x13,(unsigned long)__NEx_13, TRAPGATE); /* Fault : Reserved */
  Set_Def_IDTEntry(0x14,(unsigned long)__NEx_14, TRAPGATE); /* Fault : Reserved */
  Set_Def_IDTEntry(0x15,(unsigned long)__NEx_15, TRAPGATE); /* Fault : Reserved */
  Set_Def_IDTEntry(0x16,(unsigned long)__NEx_16, TRAPGATE); /* Fault : Reserved */
  Set_Def_IDTEntry(0x17,(unsigned long)__NEx_17, TRAPGATE); /* Fault : Reserved */
  Set_Def_IDTEntry(0x18,(unsigned long)__NEx_18, TRAPGATE); /* Fault : Reserved */
  Set_Def_IDTEntry(0x19,(unsigned long)__NEx_19, TRAPGATE); /* Fault : Reserved */
  Set_Def_IDTEntry(0x1A,(unsigned long)__NEx_1A, TRAPGATE); /* Fault : Reserved */
  Set_Def_IDTEntry(0x1B,(unsigned long)__NEx_1B, TRAPGATE); /* Fault : Reserved */
  Set_Def_IDTEntry(0x1C,(unsigned long)__NEx_1C, TRAPGATE); /* Fault : Reserved */
  Set_Def_IDTEntry(0x1D,(unsigned long)__NEx_1D, TRAPGATE); /* Fault : Reserved */
  Set_Def_IDTEntry(0x1E,(unsigned long)__NEx_1E, TRAPGATE); /* Fault : Reserved */
  Set_Def_IDTEntry(0x1F,(unsigned long)__NEx_1F, TRAPGATE); /* Fault : Reserved */
  printf("Processor Exceptions   : IDT Vectors 0x00..0x1F\n");
  return;
}


/******************************************************************************/
#if 0
/* Cases to differenciate:
    Presumption: All threads have a completed initialize Thread Context !!!
      From    ->  To        Action taken
      **************        ************
      User    ->  User        Nothing
      Kernel  ->  Kernel      Nothing
      User    ->  Kernel      Increment all Addresses
      Kernel  ->  User        Decrement all Addresses 
 */

/*  Alte Version führte zum Fehler: Ungültiger L-Wert in Zuweisung
 *   #define ADJUST(x) x = (long)((long)(x) + (long)(adjust));
 */
#define ADJUST(x) x = (void *)((long)(x) + (long)(adjust));

void msgAdjust(ThreadPtr pFromOrToThread, Message *msg, Boolean ToKernel)
{
  /* Note: Defining this Variable as signed long limits MM to 2GB !!! */
  long  adjust = UADDR_BASE;
  ProcContextPtr ctx = pFromOrToThread->contextPtr;
  Boolean MsgLocIsKernel = (ctx->tf_cs == KCSEL);

  if   (MsgLocIsKernel)
    return;  /* no adjustments required */
  /* This means the sending or the receiving thread expects the message to be
     copied in or from a location in the Kernel Data Space.
     A Message from a User Thread would have MsgLocIsKernel == false and would
     be adjusted on the way to the Kernel.
     A Message to a User Thread needs adjustment as MsgLocIsKernel == false. 
   */
  //-LR   printf("WE ARE IN MSG ADJUST\n");
  //-LR   STP();

  //-LR   printf("msg->id: %i\n", msg->id);


  if (!ToKernel)  /* if we are on MsgRecv direction */
    adjust = -adjust;   /* we go to User Space */
  switch  (msg->id)
  {

  case VM_UNALLOC :
    //-LR       printf("msg->msg.vmFree.address         = 0x%8x --> ",
    //-LR         (unsigned int)msg->msg.vmFree.address        );
    ADJUST(msg->msg.vmFree.address);
    //-LR       printf("0x%8x\n",
    //-LR         (unsigned int)msg->msg.vmFree.address        );
    break;
  case VM_MOVE :
    //-LR       printf("msg->msg.vmMove.address         = 0x%8x --> ",
    //-LR         (unsigned int)msg->msg.vmMove.address);
    ADJUST(msg->msg.vmMove.address);
    //-LR       printf("0x%8x\n",
    //-LR         (unsigned int)msg->msg.vmMove.address);
    break;
  case VM_PROTECT :
    //-LR       printf("msg->msg.vmProtect.startAddress = 0x%8x --> ",
    //-LR         (unsigned int)msg->msg.vmProtect.startAddress);
    ADJUST(msg->msg.vmProtect.startAddress);
    //-LR       printf("0x%8x\n",
    //-LR         (unsigned int)msg->msg.vmProtect.startAddress);
    break;
  case VM_ALLOCREPLY :
    //-LR       printf("msg->msg.vmAllocReply.address   = 0x%8x --> ",
    //-LR         (unsigned int)msg->msg.vmAllocReply.address);
    ADJUST(msg->msg.vmAllocReply.address);
    //-LR       printf("0x%8x\n",
    //-LR         (unsigned int)msg->msg.vmAllocReply.address);
    break;
  case VM_MOVEREPLY :
    //-LR       printf("msg->msg.vmMoveReply.address    = 0x%8x --> ",
    //-LR           (unsigned int)msg->msg.vmMoveReply.address);
    ADJUST(msg->msg.vmMoveReply.address);
    //-LR       printf("0x%8x\n",
    //-LR           (unsigned int)msg->msg.vmMoveReply.address);
    break;

  case IO_READ :
    //-LR       printf("msg->msg.ioRead.buffer          = 0x%8x --> ", (unsigned int)(msg->msg.ioRead.buffer));
    //-BL       ADJUST(msg->msg.ioRead.buffer);
    //-LR       printf("0x%8x\n",
    //-LR           (unsigned int)(msg->msg.ioRead.buffer));
    //-LR       BRK();
    break;
  case IO_WRITE :
    //-LR       printf("msg->enveloppe         = 0x%8x --> ", (unsigned int)(msg->enveloppe));
    ADJUST(msg->enveloppe);
    //-LR        printf("0x%8x\n", (unsigned int)(msg->enveloppe));
    //-LR        printf("buffer=%s\n",msg->enveloppe);
    break;
  case TM_INFO :
    //-BL       ADJUST(msg->msg.tmInfo.infoPtr);

  case TM_START :
  case IO_INIT :
  case IO_OPENREPLY :
  case IO_CLOSEREPLY :
  case IO_READREPLY :
  case IO_WRITEREPLY :
  case IO_INITREPLY :
  case IO_OPEN :
  case IO_CLOSE :
  case VM_PROTECTREPLY :
  case VM_CLEANUP :
  case VM_FREEREPLY :
  case VM_ALLOC :
  case TM_EXIT :
  case TM_YIELD :
  case TM_KILL :
  case TM_STARTREPLY :
  case TM_KILLREPLY :
  case TM_INFOREPLY :

  case UNKNOWN_SYSCALL :
  case ANYMSGTYPE :

  default:
    /*rien a faire -- or shit happened*/
    break;
  }
}
#endif

/******************************************************************************/


void moveMessageToPD(Thread* threadPtr,   /* Thread in Destination PD                        */
                     Message* msgPtr,     /* Message to be copied (in Kernel PD)             */
                     Message* destPtr     /* Pointer to Destination in Thread of other PD    */
                    )
{ 

  /* We are in the kernel PD and have to copy the message to another PD.
   * Do this by temporarily mapping the destPtr to address map_pos_dest in the kernel PD.
   */
  int err;

  address_t map_pos_dest = vm_pmap_pagebuff_get_page();

  // map page to kernel PD
  err = vm_pmap_map_page_in_other_pmap(&threadPtr->pdPtr->vm_space.vm_pmap, destPtr, // from PD
                                       &vm->kernel->vm_pmap, map_pos_dest,           // to kernel PD
                                       PMAP_READ | PMAP_WRITE);                      // flags
  if (err != VM_OK)
    PANIC("Could not map page to Kernel PD");

  // adjust pointer to message in Kernel PD
  //  we mapped destPtr in our PD, adjust address to be in mapped page
  destPtr = (Message*)( (uint32)PAGEREMAINDER(destPtr) + map_pos_dest);
  
  // Make sure that the whole message is inside this page
  ASSERT(PAGEREMAINDER(destPtr) + sizeof(Message) < PAGESIZE);  
  
  // copy from kernel to destPtr
  byteCopy(destPtr, msgPtr, sizeof(Message));

  // remove mapping
  err = vm_pmap_page_remove(&vm->kernel->vm_pmap, map_pos_dest);
  vm_pmap_pagebuff_release_page(map_pos_dest);

  if (err != VM_OK)
    PANIC("Could not remove mapping for page");

}



/******************************************************************************/

/************************************/
/* HAL Level of Interrupt Handling. */
/************************************/

//-LR void _INTHandler(unsigned int esp, struct ProcContext_t frame);
void _INTHandler(struct ProcStackFrame_t frame);

extern int tmThreadPdbr;

//-LR void _INTHandler(unsigned int esp, struct ProcContext_t frame) {
void _INTHandler(volatile struct ProcStackFrame_t frame)
{
  volatile Thread *currentThread;
  Message *msg;
  int irq_to_ack = 0;
  int err, remap, rpl, flags;
  ProcStackFramePtr oldsf;
  ProcContextPtr ctx;
  address_t map_pos;

  /* change to the memory mapping of the tmThread */
  /* XXX: these data structures are not part of the vm of every thread... */
  if ( tmThreadPdbr != frame.tf_pdbr)
    __set_PDBR(tmThreadPdbr);
  
  SysInterrupted();

  /* SAVE PROCESS CONTEXT: REGISTER SET IS ONLY SAVED ON STACK */

  irq_to_ack = frame.tf_trapno;         /* Remember IRQ to acknowledge before exit */

  currentThread = __get_thread_from_esp();

  ASSERT(currentThread ==  schedulerRunning()); // just to make sure

  /* complete the stack frame */
  oldsf = currentThread->contextPtr->sf;
  currentThread->contextPtr->sf = &frame;                     // set the new Stack Frame
  currentThread->contextPtr->sf->tf_last = (int) oldsf;       // save the old Stack Frame
  currentThread->contextPtr->sf->tf_priority = currentThread->schedInfo.priority;   // original priority
  currentThread->contextPtr->sf->tf_schedFlags = 0;                                 // clear SchedFlags
  currentThread->contextPtr->sf->tf_schedFlags |= SCHEDFLAGS_INISR;                 // handling Interrupt

  /* Take new Priority */
  lock(&currentThread->lock);
  {
    schedulerChangePriority(currentThread, INTHANDLER_PRIORITY);    // we do not need the lock for this 
  }
  unlock(&currentThread->lock);

  /* Reenable Interrupts since the Stack Frame is complete */ 
  sti;

  /* check for nexted exception/interrupt */
  if (currentThread->contextPtr->sf->tf_last != 0) {
    int this = irq_to_ack;
    int last = ((ProcStackFramePtr)currentThread->contextPtr->sf->tf_last)->tf_trapno;
    SysNestedInterrupted(this, last);
    /*
    ioConsolePutString("\nNested Interrupt 0x");
    ioConsolePutHexInt(irq_to_ack);
    ioConsolePutString(" thread ");
    ioConsolePutInt(currentThread->id);
    ioConsolePutString("\n");
    */
  }

  if (frame.tf_trapno <  T_IR_00)
  { /* Processor Exception occured */
    //-LR     printf("esp: 0x%8x \n",(unsigned int)esp);
    // printf("WARNING: %s occured: \n", ExMsg[frame.tf_trapno]);
    // DisplayContext("",currentThread->contextPtr);
  }
  if (frame.tf_trapno < T_IS_30)
  {
    /* This is a shorthand -- no, not really a hack -- to get the ia32 clock
     * running and displaying the progress.
     */
    if (frame.tf_trapno == T_IR_00)  /* The clock is irq0. */
      _TimerISR(); /* this functions shows correct operation: time & rotator */
    else
      intDispatcher(&frame);
    LOCK_THREAD(currentThread, flags);
    {
      tmHalThreadSetResched(currentThread, ACTIVE);
    }
    UNLOCK_THREAD(currentThread, flags);
  }
  else
    if (frame.tf_trapno == T_IS_30)
    {         /* This is our cruxious SYSCALL :-) */
      msg = (Message *)frame.tf_esi;          /* Get Address of Message */

#if 0   // old Memory
      // This Adjustments are not needed anymore
      if (currentThread->contextPtr->tf_ds != KDSEL)
      {  /* is it not a kernel thread ? */
        msg = (Message *)U_TO_K(msg);                   /* if USER thread, adjust address */
        //-LR         printf("Address of msg: %p\n", (unsigned long)msg);
        //-LR         MemoryDump("User Message:", (unsigned int)msg);
        //-LR         DisplayContext("User:", &frame);
        //-LR         STP();
      }

      /* We are on the way to the Message handler */

      msgAdjust(currentThread,msg,TRUE);
#endif

      /* We are in the kernel PD. Therefore it is neccesary to move the page
    	 *   into the kernel PD. We do this by mapping the page containing the
       *   message to map_pos.
       */

      // only need to remapp, if the two Page Directories are not identical
      remap = ( currentThread->contextPtr->sf->tf_pdbr != tmThreadPdbr);
      if (remap)
      {
        map_pos = vm_pmap_pagebuff_get_page();
        
        // map page to kernel PD
        err = vm_pmap_map_page_in_other_pmap(&currentThread->pdPtr->vm_space.vm_pmap, msg, // from PD
                                       &vm->kernel->vm_pmap, map_pos,                      // to kernel PD
                                       PMAP_READ | PMAP_WRITE);                            // flags
        if (err != VM_OK)
        {
          WARNING("Could not map page containing the Message to Kernel PD");
          tmError(currentThread->id, "Invalid Message supplied");
        }

        // adjust pointer to message in Kernel PD
        msg = (Message*)( (uint32)PAGEREMAINDER(msg) + map_pos);
  
        // Make sure that the whole message is inside this page
        ASSERT(PAGEREMAINDER(msg) + sizeof(Message) < PAGESIZE);  
      }
      
      msgDispatcher(currentThread->id,               /* Sender is current thread */
                    msg,                             /* Address of Message (in PD of tmThread) */
                    (unsigned long int)frame.tf_edx, /* timeout in milliseconds (if receiving) */
                    (MsgOpCode)frame.tf_eax );       /* Message OpCode */

      if (remap) 
      {
        // remove mapping
        err = vm_pmap_page_remove(&vm->kernel->vm_pmap, map_pos);
        vm_pmap_pagebuff_release_page(map_pos);
        if (err != VM_OK)
          PANIC("Could not remove mapping for page");
      }
    }
    else
    {
      printf("Unknown \"random\" interrupt occured : %i :-?\n",frame.tf_trapno);
      DisplayContext("  currentThread: ", currentThread->contextPtr);
    }
  m_NoCalled++;

  /* 
   * Disable Interrupts
   */
  cli; 

#ifdef APIC
  if ((irq_to_ack >= T_IR_00) && (irq_to_ack <= T_IR_0F) && (irq_to_ack != APIC_IR_SPIV))
    __endOf_APIC_IRQ();   /* As soon as we reenable interrupts, the APIC sends us the next. 
                           * We do this after cli, because we want to finish this now
                           */
#else
  if ((irq_to_ack >= T_IR_00) && (irq_to_ack <= T_IR_0F))
    __endOf_IRQ(irq_to_ack);
#endif
   
  /* Aquire write Lock of Thread */
  lock(&currentThread->lock);
  {
 
    /* Take old priority */
    schedulerChangePriority(currentThread, (ThreadPriority)currentThread->contextPtr->sf->tf_priority);

    /* Take old status */
    if (currentThread->contextPtr->sf->tf_schedFlags & SCHEDFLAGS_BLOCKED)
      schedulerSetBlocked(currentThread);

    currentThread->contextPtr->sf->tf_schedFlags &= ~SCHEDFLAGS_INISR;                 // ISR finished
  
  }
  /* release Lock */
  unlock(&currentThread->lock);

  /* Make scheduling decision if necessary */
  if (currentThread->contextPtr->sf->tf_schedFlags & SCHEDFLAGS_RESCHED) 
    schedule();

  /* Always execute a restore context to surely process set context. */
  ASSERT_REDEF(schedulerRunning() != NULL);
  restoreContext(schedulerRunning()->contextPtr);
  
  /* This should never be executed. */
  return;
}


void powerSaver(void)
{
  asm("hlt");             /* stop working until next IRQ: save power */
}

/* */
void busyIdle(void)
{
  unsigned int i;

  while (1)
    i += i*23;  // do something

}

/******************************************************************************/

/************************************/
/* Initialize Thread Management     */
/************************************/

int tmHalInit(void)
{
  int cpu_count;

  cpu_count = tmGetCpuCount();

  // Allocate Buffer for other TSSs  

  // XXX initialize other CPUs

  return TM_OK;
}


/************************************/
/* tmHal helper functions           */
/************************************/

// Finds a free region of the specified size near near
inline int tmHalFindNear(ThreadId destPDId, RegionPtr_t regionPtr, address_t near, int flags)
{
  int err;

  if (flags & TM_INIT)
  {
    pdom_t destPD = getPDbyId(destPDId);
    struct vm_region* prev;
    struct vm_map* vm_map = &destPD->vm_space.vm_map;
    err = vm_map_find_near(&vm_map, near, regionPtr->end - regionPtr->start + 1,
                            regionPtr, &prev, 1);
    if (err != VM_OK)
      return TM_FAILED;
  }
  else
  {
    Message message, reply;
    reply.id = VM_REPLY;
    ThreadId to = MMTHREADID;
    int size = regionPtr->end - regionPtr->start + 1;
    int direction = 1;

    // Attach enveloppe to message and init push/pop state
    if (attachEnvMsg(&message, VM_PP_INIT) != 0)
    {
      printf("VM_MAP_FIND_NEAR: failed to attach enveloppe to message.\n");
      return TM_FAILED;
    }
    message.id = VM_MAP_FIND_NEAR;
    pushEnvMsg(&message, &destPDId, sizeof(destPDId)); 
    pushEnvMsg(&message, &near, sizeof(address_t));
    pushEnvMsg(&message, &size, sizeof(size_t));
    pushEnvMsg(&message, regionPtr, sizeof(Region_t));
    pushEnvMsg(&message, &direction, sizeof(int));
    
    if (tmMsgSend(to, &message)!= TM_MSGSENDOK)
    {
      printf("VM_MAP_FIND_NEAR: tmMsgSend failed\n");
      return TM_FAILED;
    }

    tmMsgRecv(&to, ANYMSGTYPE, &reply, INFINITY);

    if (reply.msg.vm_reply.err != VM_OK || reply.id != VM_REPLY)
    {
      printf("VM_MAP_FIND_NEAR failed with err %i\n   ptr1 %p  ptr2 %p\n", reply.msg.vm_reply.err, reply.msg.vm_reply.ptr1, reply.msg.vm_reply.ptr2);
      return TM_FAILED;
    }
    regionPtr->start = reply.msg.vm_reply.ptr1;
    regionPtr->end   = reply.msg.vm_reply.ptr2;
  
  }
  // printf("tmHalFindNear: start %p  end %p  near %p\n", regionPtr->start, regionPtr->end, near);
  return TM_OK;
}


inline int tmHalFreeRegion(ThreadId destPDId, address_t start, address_t end, int flags)
{
  int err;
  
  if (flags & TM_INIT)
  {
    pdom_t destPD = getPDbyId(destPDId);
    Region_t region;
    region.start = start;
    region.end = end;
    err = vm_region_free(&destPD->vm_space.vm_map,
                         &destPD->vm_space.vm_pmap, &region);
  }
  else
  {
    err = vmFreeHint(destPDId, start);
  }
  if (err != VM_OK)
    return TM_FAILED;
  
  return TM_OK;
}

// enters the Region into the pmap
inline int tmHalRegionEnter(ThreadId destPDId, RegionPtr_t regionPtr, int flags)
{
  int err, addr;

  // kernel mode stack -> enter Region into pmap-Layer of destPD to prevent a recursive page exception in INTHandler
    if (flags & TM_INIT)
    {
      // call vm function directly
      pdom_t destPD = getPDbyId(destPDId);
      for (addr=(int)regionPtr->start; addr < (int)regionPtr->end; addr+= PAGESIZE)
      {
        if (VM_OK != vm_region_lookup(&destPD->vm_space.vm_map,&destPD->vm_space.vm_pmap,
                                      addr, VM_ACCESS_READ))
           return TM_FAILED;
      }
    }
    else
    {
      // use Messages
      Message message, reply;
      reply.id = VM_REPLY;
      message.id = VM_REGION_ENTER;
      message.msg.vm_get.pdId = destPDId;
      message.msg.vm_get.hint = regionPtr->start;
      
      genericSyscall(MMTHREADID, &message, &reply);

      if (reply.msg.vm_reply.err != VM_OK)
        return TM_FAILED;

    }
    return TM_OK;

}
// adds a region to destPDId
inline int tmHalAddRegion(ThreadId destPDId, RegionPtr_t regionPtr, address_t near, int flags)
{
  int err; 
  // printf("tmHalAddRegion1: region.start %p region.end %p near %p\n", regionPtr->start, regionPtr->end, near);

  if (flags & TM_INIT)
  {
    // the mmThread is not yet running => call the vm functions directly
    address_t hint;
    pdom_t destPD = getPDbyId(destPDId);

    // Add Stack to the tmPD
    err = vm_region_add_near(&destPD->vm_space.vm_map,
                             &destPD->vm_space.vm_pmap, regionPtr,
                             near, 1);
    if (err != VM_OK)
      return TM_FAILED;
   }
   else
   {
    // the mmThread is running => use Syscall (IPC)
    Message message, reply;
    reply.id = VM_REPLY;
    ThreadId to = MMTHREADID;

    // Add Stack to tmPD
    // Attach enveloppe to message and init push/pop state
    if (attachEnvMsg(&message, VM_PP_INIT) != 0)
    {
      printf("VM_REGION_ADD_NEAR: failed to attach enveloppe to message.\n");
      return TM_FAILED;
    }
    message.id = VM_REGION_ADD_NEAR;
    pushEnvMsg(&message, &destPDId, sizeof(ThreadId));
    pushEnvMsg(&message, regionPtr, sizeof(Region_t));
    pushEnvMsg(&message, &near, sizeof(address_t));
    tmMsgSend(to, &message);

    tmMsgRecv(&to, VM_REPLY, &reply, INFINITY);

    if (reply.msg.vm_reply.err != VM_OK)
      return TM_FAILED;

    regionPtr->start = reply.msg.vm_reply.ptr1;
    regionPtr->end   = reply.msg.vm_reply.ptr2;
  }
  // printf("tmHalAddRegion2: region.start %p region.end %p near %p\n", regionPtr->start, regionPtr->end, near);
  
  return TM_OK;
}

inline int tmHalShareRegion(ThreadId fromPDId, ThreadId toPDId, RegionPtr_t regionPtr, address_t* nearPtr, int flags)
{
  int err, size = regionPtr->end - regionPtr->start;
 
  if (flags & TM_INIT)
  {
    pdom_t fromPD = getPDbyId(fromPDId);
    pdom_t toPD   = getPDbyId(toPDId);

    // the mmThread is not yet running => call the vm functions directly
    // add Stack to destPD
    err = vm_region_share_near(&fromPD->vm_space.vm_map, &fromPD->vm_space.vm_pmap, regionPtr,  // source: fomPD
                               &toPD->vm_space.vm_map,   &toPD->vm_space.vm_pmap,               // destination: toPD
                               nearPtr, VM_OBJ_SHARE_RW, 1);

    regionPtr->start = (int)*nearPtr;
    regionPtr->end   = regionPtr->start + size;

    if (err != VM_OK)
    {
      return TM_FAILED;
    }
  }
  else
  { 
    // the mmThread is running => use Syscall (IPC)
    Message message, reply;
    reply.id = VM_REPLY;
    ThreadId to = MMTHREADID;
    int direction=1, flags=VM_OBJ_SHARE_RW;

    // printf("VM_REGION_SHARE: try to share region: start %p  end %p\n", regionPtr->start, regionPtr->end);
  
    // Add Stack to tmPD
    // Attach enveloppe to message and init push/pop state
    if (attachEnvMsg(&message, VM_PP_INIT) != 0)
    {
      printf("VM_REGION_SHARE: failed to attach enveloppe to message.\n");
      return TM_FAILED;
    }
    message.id = VM_REGION_SHARE;
    pushEnvMsg(&message, &fromPDId, sizeof(ThreadId));
    pushEnvMsg(&message, &toPDId, sizeof(ThreadId));
    pushEnvMsg(&message, regionPtr, sizeof(Region_t));
    pushEnvMsg(&message, nearPtr, sizeof(address_t));
    pushEnvMsg(&message, &flags, sizeof(int));
    pushEnvMsg(&message, &direction, sizeof(int));
    tmMsgSend(to, &message);

    tmMsgRecv(&to, VM_REPLY, &reply, INFINITY);

    if (reply.msg.vm_reply.err != VM_OK)
      return TM_FAILED;

    regionPtr->start = *nearPtr = reply.msg.vm_reply.ptr1;
    regionPtr->end   = regionPtr->start + size;

    // printf("  succeeded: start %p  end %p\n", regionPtr->start, regionPtr->end);
  }

  return TM_OK;
}

// allocates a new User Thread Stack
int tmHalAllocUserThreadStack(ThreadId destPDId, ThreadId tmPDId,  address_t* stackBaseAddress, address_t* stackBaseMappedAt, int flags)
{
  int err;
  address_t hint=0;

  // User Thread => Allocate new User Stack
  Region_t  region;
  // setup Stack Region
  region.obj_arg.argsize = 0;
  region.obj_arg.arg[0] = 0;
  region.start = 0;
  region.end = (address_t)(TM_DEFAULTTHREADSTACKSIZE - 1);
  region.cur = VM_ACCESS_READ|VM_ACCESS_WRITE;
  region.max = VM_ACCESS_READ|VM_ACCESS_WRITE;
  region.type = (unsigned short)VM_MANAGER_ANON;

  // Alloc Stack in destination Thread
  err = tmHalAddRegion(destPDId, &region, TM_DEFAULTTHREADSTACKNEAR, flags);
  // printf("tmHalAllocUserThreadStack: pd %i  %p-%p\n", destPDId, region.start, region.end);
  if (err != TM_OK)
    return TM_FAILED;

  // save stack base address
  *stackBaseAddress = region.start;

  // Add Stack Region to the tmPD
  err = tmHalShareRegion(destPDId, tmPDId, &region, &hint, flags);
  if (err != TM_OK)
  {
    tmHalFreeRegion(destPDId, region.start, region.end, flags);
    return TM_FAILED;
  }
  // printf("tmHalAllocUserThreadStack: pd %i  %p-%p  hint %p\n", tmPDId, region.start, region.end, hint);
  *stackBaseMappedAt = hint;

  return TM_OK;
}

int tmHalAllocKernelModeStack(ThreadId destPDId, ThreadId tmPDId, address_t* stackBaseAddress, address_t* stackBaseMappedAt, int flags)
{
  int err, addr;
  address_t near;
  Region_t  region;
  pdom_t destPD = getPDbyId(destPDId);

  // setup Stack Region for Kernel Mode Stack
  region.obj_arg.argsize = 0;
  region.obj_arg.arg[0] = 0;
  region.start = 0;
  region.end = (address_t)(TM_KERNEL_MODE_STACK_SIZE - 1);
  region.cur = VM_ACCESS_READ|VM_ACCESS_WRITE;
  region.max = VM_ACCESS_READ|VM_ACCESS_WRITE;
  region.type = (unsigned short)VM_MANAGER_ANON;

  // find free space for region, which is alligned to the TM_KERNEL_MODE_STACK_SIZE boundary
  near = (address_t)TM_KERNEL_MODE_STACK_NEAR;
  while (1)
  {
    // search
    // printf("tmHalAllocKernelModeStack(): trying near %p\n", near);
    err = tmHalFindNear(tmPDId, &region, near, flags);
    if (err != TM_OK)
    {
      printf("tmHalFindNear() failed tmPDId %i  start %p  end %p  \n      near %p  flags 0x%x  err %i \n", tmPDId, region.start, region.end, near, flags, err);
      return TM_FAILED; // eg no more space 
    }

    // check, whether region.start is aligned
    if ( ((unsigned long)region.start & (TM_KERNEL_MODE_STACK_SIZE - 1)) == 0)
    {
      // aligned => use region.start
      near = region.start;
      break; // end while(1)
    }
    else
    {
      // not yet aligned => round near up to next boundary
      near = (address_t)(((unsigned long)region.start + TM_KERNEL_MODE_STACK_SIZE - 1) & ~(TM_KERNEL_MODE_STACK_SIZE - 1));
    }
  }
  // printf("tmHalAllocKernelModeStack(): found near %p\n", near);

  // add Kernel Mode Stack to tmPD first
  err = tmHalAddRegion(tmPDId, &region, near, flags | TM_KERNEL);
  if (err != TM_OK)
    return err;

  // add Kernel Mode Stack to destPD
  if (destPDId != tmPDId)
  {
    address_t near = region.start;
    err = tmHalShareRegion(tmPDId, destPDId, &region, &near, flags | TM_KERNEL);

    if (err != TM_OK)
    {
      tmHalFreeRegion(tmPDId, region.start, region.end, flags);
      return TM_FAILED;
    }
    // make sure, the stack is mapped to exactly the same position in both PDs
    ASSERT(near == region.start);
  }

  *stackBaseAddress = region.start;
  *stackBaseMappedAt = region.start;

  // enter the Kernel Mode Stack into the pmap of destPD
  err = tmHalRegionEnter(destPDId, &region, flags | TM_KERNEL);
  if (err != TM_OK)
  {
    tmHalFreeRegion(tmPDId, region.start, region.end, flags);
    if (destPDId != tmPDId)
      tmHalFreeRegion(destPDId, region.start, region.end, flags);
    return TM_FAILED;
  }

  // make sure that the Stack is aligned to the TM_KERNEL_MODE_STACK_SIZE boudary
  // this should be the case as long as we only allocate stack regions in this memory area
  // printf("tmHalAllocKernelModeStack(): stackBaseAddress %p \n", *stackBaseAddress);
  ASSERT(((unsigned long)*stackBaseAddress & (TM_KERNEL_MODE_STACK_SIZE-1)) == 0);

  return TM_OK;
}


/************************************/
/* Thread Management                */
/************************************/

/*
 * Initialize the Thread: allocate Stacks, Thread and Context structures
 *  We have to:
 *   1. Allocate Kernel Mode Stack in the destination PD and the PD of the tmThread
 *        Its important, that the stack is mapped to the same virtual address in both PDs.
 *   2. Set the threadPtr to the bottom of the Kernel Mode Stack and the ProcContextPtr above it
 *   3. Set the ss0 and the esp0 of the Threads Context to the newly allocated stack
 *   4. Set the Frame Pointer to esp0
 *   5. If the target thread is a User Thread, create a second User Mode Stack
 *      Set stackBaseAddress, stackBaseMappedAt and stackSize
 *
 *  Kernel Mode Stack:
 *   +-----------------+
 *   | ProcStackFrame0 | top     : + TM_KERNEL_MODE_STACK_SIZE - 4
 *   | Stack0          |
 *   |  :              |
 *   | ProcContext     |
 *   | Thread          | bottom  : kmsBase / kmsMappedAt 
 *   +-----------------+ 
 *
 *  flags:
 *    TM_INIT     : during init, the mmThread is not yet running
 *    TM_KERNEL   : kernel thread
 *    TM_NOFLAGS  : nothing special
 */
int tmHalInitThread(Thread* *threadPtr, ThreadId destPDId, ThreadId tmPDId, Address* stackBaseAddress, Address* stackBaseMappedAt, size_t* stackSize, int flags)
{
  int err;
  address_t near, kmsBase, kmsMappedAt, utsBase, utsMappedAt;
  int exitCodeLength = endAutomaticThreadExit - automaticThreadExit; // length of the exit code

  /* STEP 1 */
  err = tmHalAllocKernelModeStack(destPDId, tmPDId, &kmsBase, &kmsMappedAt, flags);
  if (err != TM_OK)
  {
    printf("tmHalInitThread(): tmHalAllocKernelModeStack failed\n");
    return TM_FAILED;
  }

  // printf("tmHalAllocKernelModeStack: kmsBase %p  kmsMappedAt %p \n", kmsBase, kmsMappedAt);

  /* STEP 2 */
  (*threadPtr) = (Thread*)(kmsMappedAt);
  (*threadPtr)->contextPtr = (ProcContext*)((char*)kmsMappedAt + sizeof(Thread));

  /* STEP 3 */
  (*threadPtr)->contextPtr->proc_ss0 = GC_KDSEL;
  (*threadPtr)->contextPtr->proc_esp0 = (unsigned long)((char*)kmsBase + TM_KERNEL_MODE_STACK_SIZE - 4);
  
  /* STEP 4 */
  (*threadPtr)->contextPtr->sf = (ProcStackFramePtr) (
                                      (char*)((*threadPtr)->contextPtr->proc_esp0) - 
                                      sizeof(ProcStackFrame));
  if (flags & TM_KERNEL)
  {
    // make room on the Stack for exitMessage, exitCode, argument 1, argument 0 and return address
    // see threadBuild() for details
    (*threadPtr)->contextPtr->sf = (ProcStackFramePtr)(
                                      (char*)((*threadPtr)->contextPtr->sf) +
                                        2*sizeof(int)                       - /* ProcStackFrame has no tf_esp and tf_ss */
                                        sizeof(Message)                     - /* message      */
                                        exitCodeLength                      - /* exitCode     */
                                        sizeof(Register)                    - /* argument 1   */
                                        sizeof(Register)                    - /* argument 0   */
                                        sizeof(Register)                      /* return addr  */
                                   );
    // printf("tmHalInitThread(): esp0 %p  sf %p  ecl %p\n", (*threadPtr)->contextPtr->proc_esp0, (*threadPtr)->contextPtr->sf, exitCodeLength);
  }

  /* STEP 5 */
  if (flags & TM_KERNEL)
  {
    // Kernel Thread => Stack and Kernel Mode Stack are identical
    *stackBaseAddress = (address_t)((char*)kmsBase + sizeof(Thread) + sizeof(ProcContext));  // stack starts after Thread and ProcContext
    *stackBaseMappedAt = (address_t)((char*)kmsMappedAt + sizeof(Thread) + sizeof(ProcContext));
    *stackSize = (address_t)((char*)TM_KERNEL_MODE_STACK_SIZE - sizeof(Thread) - sizeof(ProcContext)); // stack is smaller
  }
  else
  {
    // User Thread => allocate new User Thread Stack
    err = tmHalAllocUserThreadStack(destPDId, tmPDId, &utsBase, &utsMappedAt, flags);
    if (err != TM_OK)
    {
      // free region of Kernel Mode Stack in the PD of the tmThread
      tmHalFreeRegion(tmPDId, kmsBase, kmsBase+TM_KERNEL_MODE_STACK_SIZE, flags);
      if (destPDId != tmPDId)
      {
        // free region in the PD of the destPD
        tmHalFreeRegion(destPDId, kmsMappedAt, kmsMappedAt + TM_KERNEL_MODE_STACK_SIZE, flags);
      }
      return TM_FAILED;
    }
    // printf("tmHalAllocUserThreadStack: utsBase %p  utsMappedAt %p \n", utsBase, utsMappedAt);
    // set stack base addresses
    *stackBaseAddress = utsBase;
    *stackBaseMappedAt = utsMappedAt;
    *stackSize = TM_DEFAULTTHREADSTACKSIZE;
  }

  return TM_OK;
}


/*
 * Destroys the Threads Stack, Thread and Context structures
 */
int tmHalDestroyThread(Thread* threadPtr, ThreadId destPDId, ThreadId tmPDId)
{
  int err1, err2=VM_OK;
  
  // copy the important information from the Thread structure
  int esp0 = threadPtr->contextPtr->proc_esp0;
  int esp  = threadPtr->stackStart;

  if (threadPtr->permission & KERNEL_PERM_MSK)
  {
    // This is a kernel Thread which has no extra User Mode Stack
  }
  else
  {
    // User Thread -> Free the User Mode Stack
    if (vmFreeHint(destPDId, esp) != VM_OK)
      printf("Failed to free region (%p)\n", esp);
  }

  if (destPDId != tmPDId)
  {
    // Free Kernel Mode Stack in destPD
    err1 = vmFreeHint(destPDId, esp0);  
  }

  // Free Kernel Mode Stack in tmPD
  //  this frees the thread and the context structures too
  err2 = vmFreeHint(tmPDId, esp0);  
 

  if (err1 == VM_OK && err2 == VM_OK) 
    return TM_OK;

  Debug("Failed to free Kernel Mode Stack Region");
  return TM_FAILED;
}


/*
 * Cleans up at the end of ThreadBuild
 */
int tmHalCleanupAfterThreadBuild(Thread* threadPtr, Address stackBaseMappedAt, int flags)
{
  int err;

  if (tmThreadPtr->pdPtr != threadPtr->pdPtr)
  {
    // remove mapped stack from pd of tmThread
    err = tmHalFreeRegion(tmThreadPtr->pdPtr->pdId, stackBaseMappedAt, stackBaseMappedAt + TM_DEFAULTTHREADSTACKSIZE, flags);
  
    if (err != TM_OK)
    {
      WARNING("Failed to free mapped stack");
      return TM_FAILED;
    }
  }
  

  return TM_OK;
}


/*
 * Performs a Task Switch to the context next
 */
void restoreContext(ProcContextPtr next)
{

  SysRestored();

  // XXX: check Threads...

  // change TSS of the CPU
  next->cpu_tss = tmGetTSSPtr(tmGetCpuId());
  next->cpu_tss->tss_ss0 = next->proc_ss0;
  next->cpu_tss->tss_esp0 = next->proc_esp0;

  // restore Context of next
  __restoreContext(next); 
  
  // restore Context should not return!
  asm("hlt");

}

/* Finds the Stack Frame which meets the given criterion
 *  returns NULL otherwise
 */
ProcStackFramePtr tmHalThreadGetSF(Thread* threadPtr, BlockCause hint)
{
  ProcStackFramePtr sf = threadPtr->contextPtr->sf;
  ASSERT((int)sf != 0);   // must not be called when thread is not interrupted
  // check the sf
  while ((int)sf != 0)
  {
    // check sf pointer
    ASSERT((unsigned long)threadPtr < (unsigned long)sf);
    ASSERT((unsigned long)sf < (unsigned long)(threadPtr + TM_DEFAULTKERNELTHREADSTACKSIZE));

    // check whether this is the right sf
    switch (hint)
    {
      case TIMER_INT:
        if (sf->tf_trapno == T_IR_00)
          return sf;
        break;
      case PAGE_FAULT:
        if (sf->tf_trapno == T_EX_0C || sf->tf_trapno == T_EX_0E) 
          return sf;
        break;
      case MSG_SEND:
      case MSG_RECV:
        if (sf->tf_trapno == T_IS_30)
          return sf;
        break;
      case ACTIVE:
        return sf;  // return first (active) sf
        break;
      default:
        PANIC("UNKNOWN BlockCause");
        break;
    }
    
    // try next
    sf = (ProcStackFramePtr) sf->tf_last;
  } // end of while
  // no sf found
  return NULL;
}

/* This function looks at the given Thread and decides on
 * what to do:
 *  a) Thread is handling an Interrupt: 
 *     Set the status of the right Stack Frame to be ready
 *  b) otherwise:
 *     move the Thread to the schedulers ready list
 * ! This function may run in an exception handler.
 */
int tmHalThreadSetReady(ThreadPtr threadPtr, BlockCause hint)
{
  int err;
  ProcStackFramePtr sf;

  ASSERT(!lockTry(&threadPtr->lock));
  
  // find the right Stack Frame
  sf = tmHalThreadGetSF(threadPtr, hint);
  if (sf == NULL)  
    PANIC("This Stack Frame does not exist.");

  // set thread to ready
  if (sf->tf_schedFlags & SCHEDFLAGS_INISR)
  {
    // a) Thread is handling an Interrupt
    sf->tf_schedFlags &= ~SCHEDFLAGS_BLOCKED; // clear blocked flag
    err = TM_OK;
  }
  else
  {
    // b) not in the Interrupt handler
    err = schedulerSetReady(threadPtr);
  }

  return err;
}

/* This function looks at the given Thread and decides on
 * what to do:
 *  a) Thread is handling an Interrupt: 
 *     Set the status of the right Stack Frame to be blocked
 *  b) otherwise:
 *     move the Thread to the schedulers blocked list
 * ! This function may run in an exception handler.
 */
int tmHalThreadSetBlocked(ThreadPtr threadPtr, BlockCause hint)
{
  int err;
  ProcStackFramePtr sf;

  ASSERT(!lockTry(&threadPtr->lock));
  
  // find the right Stack Frame
  sf = tmHalThreadGetSF(threadPtr, hint);
  if (sf == NULL) 
    PANIC("This Stack Frame does not exist.");

  // set thread to blocked
  if (sf->tf_schedFlags & SCHEDFLAGS_INISR)
  {
    // a) Thread is handling an Interrupt
    sf->tf_schedFlags |= SCHEDFLAGS_BLOCKED; // set blocked flag
    err = TM_OK;
  }
  else
  {
    // b) not in the Interrupt handler
    err = schedulerSetBlocked(threadPtr);
  }

  return err;
}

/* This function looks at the given Thread and 
 *   sets the reschedule flag of the right StackFrame 
 * ! This function may run in an exception handler.
 */
void tmHalThreadSetResched(ThreadPtr threadPtr, BlockCause hint)
{
  int err;
  ProcStackFramePtr sf;

  ASSERT(!lockTry(&threadPtr->lock));
  
  // find the right Stack Frame
  sf = tmHalThreadGetSF(threadPtr, hint);
  if (sf == NULL) 
    PANIC("This Stack Frame does not exist.");

  // make thread to be rescheduled
  if (sf->tf_schedFlags & SCHEDFLAGS_INISR)
  {
    // a) Thread is handling an Interrupt
    sf->tf_schedFlags |= SCHEDFLAGS_RESCHED; // set schedule flag
    err = TM_OK;
  }
  else
  {
    // b) not in the Interrupt handler => function has no effect
    err = TM_OK;
  }

  return err;
}

/* This function looks at the given Thread and decides on
 * what to do:
 *  a) Thread is handling an Interrupt: 
 *     Set the reschedule flag of the right StackFrame 
 *  b) otherwise:
 *     call the threadYield() function
 * ! This function may run in an exception handler.
 */
void tmHalThreadYield(ThreadPtr threadPtr, BlockCause hint)
{
  int flags, err;
  ProcStackFramePtr sf;

  // thread must not be locked, since we call threadYield() below
  ATOMIC_START(flags);
  {
    ASSERT(lockTry(&threadPtr->lock));
    unlock(&threadPtr->lock);
  }
  ATOMIC_END(flags);

  // find the right Stack Frame
  sf = tmHalThreadGetSF(threadPtr, hint);
  if (sf == NULL) 
    PANIC("This Stack Frame does not exist.");

  if (sf->tf_schedFlags & SCHEDFLAGS_INISR)
  {
    // a) Thread is handling an Interrupt
    sf->tf_schedFlags |= SCHEDFLAGS_RESCHED; // set schedule flag
    err = TM_OK;
  }
  else
  {
    // b) not in the Interrupt handler
    threadYield();
    err = TM_OK;
  }

  return err;
}

/* Returns the Scheduling status of the right Stack Frame. 
 */
SchedStatus tmHalThreadGetSchedStatus(ThreadPtr threadPtr, BlockCause hint)
{
  SchedStatus status;
  ProcStackFramePtr sf;

  // thread has to be locked
  ASSERT(!lockTry(&threadPtr->lock));
  
  // find the right Stack Frame
  sf = tmHalThreadGetSF(threadPtr, hint);

  // find SchedStatus
  if (sf != 0 && sf->tf_schedFlags & SCHEDFLAGS_INISR)
  {
    // a) Thread is handling an Interrupt
    if (sf->tf_schedFlags & SCHEDFLAGS_BLOCKED)
    {
      status = BLOCKED;
    }
    else
    {
      status = threadPtr->schedInfo.status; // READY or RUNNING
    }
  }
  else
  {
    // b) not in the Interrupt handler
    status = threadPtr->schedInfo.status; 
  }
  return status;

}


/*
 * Check if the pending flag can be removed.
 * This procedure is only invoked by the scheduler before setting a new
 * thread as the next to be scheduled. If the pending flag for the new
 * thread is set, then it can be removed, as it is guaranteed that the
 * new thread will then have access to its message.
 */
void tmHalIpcResetPendingFlag(Thread* threadPtr)
{
  ASSERT(!lockTry(&threadPtr->lock));
  // next Stack Frame is the one that executed tmMsgRecv
  if (tmHalThreadGetSF(threadPtr, MSG_RECV) == tmHalThreadGetSF(threadPtr, ACTIVE))
    threadPtr->msgQueue.msgPendingStatus = NOT_WAITING;
}

