/*
  TMThread.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/TMThread.c,v $
	Author(s):             Christian Conrad
	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 "TMThread.h"
#include "TMScheduler.h"
#include "TMHal.h"
#include "TMIPC.h"
#include "TMMain.h"
#include "HashList.h"
#include "List.h"
#include "Lock.h"
#include "Syscall.h"
#include "Support.h"
#include "Limits.h"
#include "MMMapping.h"
#include "TMProtectionDomain.h"
#include "tailq.h"
#include "vm_hal.h"
//-CJ #include "Debug.h"

#ifdef MACHINE_IS_IXP
#include "lib1funcs.h"
#endif

//-CJ  static int xx_cnt = 0;

#define NO_RUNNING_THREAD_STRING "(in init)"

/* internal funtion prototypes */
Error threadDestroy( ThreadId id);


/* Global variables */
HashList threadHashList;            /* Hash list of all threads */
extern List threadList;             /* Contains all threads */
extern HashList	pdomHashList;				/* Hash list of all PDs */

extern pdom_t		mainKernelPD;			/* the PD containing the kernel init stuff, and especially the tmThread */
extern Thread*  tmThreadPtr;

LockDesc threadLockDesc;            /* lock to protect global thread lists */
Lock threadLock = &threadLockDesc;

extern ThreadId nextThreadId;   /* from TMMain */
int exceptionContextFlag = 1;       /* Set to 1 for exception context */


/*
 * Get a new thread / protectio-domain id
 * NOTE: if function fails we return 0. This does not mean that
 * ThreadId 0 is invalid, actually it is used by the Kernel PD.
 */
ThreadId getThPdomId(ThreadId desId)
{
        Thread*		threadPtr;
        Error 		retT,
                  retP;
        ThreadId	origId;

        if (desId != 0)
        {
                retT = hashListGet(threadHashList, (void**)(&threadPtr), desId);
                retP = hashListGet(pdomHashList, (void**)(&threadPtr), desId);
                if (retT == HASHNOTFOUND && retP == HASHNOTFOUND)
                {
                        return desId;
                }
                else
                {
                        return 0;
                }
        }
        origId = nextThreadId;
        do
        {
                retT = hashListGet(threadHashList, (void**)(&threadPtr), nextThreadId);
                retP = hashListGet(pdomHashList, (void**)(&threadPtr), nextThreadId);
                if (retT == HASHNOTFOUND && retP == HASHNOTFOUND)
                {
                        return nextThreadId++;
                }
                if (++nextThreadId == origId)
                {
                        PANIC("out of thread id's");
                }
        }
        while (TRUE);

        return 0;
}

#if 0
#define M(p)    printf("%s\n",p)
#define N(p,n)  printf("%s = 0x%8x\n", p, n)
#else
#define M(p)
#define N(p,n)
#endif

/* This procedure initializes a thread structure. resources are passed as
 * arguments so threadBuild doesn't need to know about memory allocation.
 */
int threadBuild( ThreadId id,
                ThreadId pdId,			/* the PD within which the thread should be created */
                ThreadId callerId,	/* who called me? (ThreadId) */
                char* name,
                ProcContext* contextPtr,
                Address stackBaseAddress, /* static address or as obtained via vm... */
                Address stackBaseMappedAt,/* mapped at */
                unsigned int stackSize,
                ThreadMainFunction mainFunction,
                ThreadArg parameter,
                AddressSpace space,
                Thread* threadPtr,
                ThreadPriority priority,
                ThreadPermission permission)
{
        Message threadExitMsg = {TMTHREADID, TM_EXIT, 0, NULL, {{NULL, NULL, NULL}}};
        int exitCodeLength = endAutomaticThreadExit - automaticThreadExit;
        int err;
        Register mode;
        Register sp;
        Address stackStartMappedAt;
        Thread	*callerThPtr;
        pdom_t	destPD,
                callerPD;


        // XXX loader starts first thread in PD
        callerPD = NULL;
        callerThPtr = NULL;

        // get PD of caller
        if (callerId != 0)
        {
                if ( (callerThPtr = getTHbyId(callerId)) == NULL)
                {
                        return TM_THREADBUILDFAILED;
                }
                callerPD = callerThPtr->pdPtr;
        }
        // get destination PD
        if (hashListGet( pdomHashList, (void**)(&destPD),
                                pdId) != HASHOK)
        {
                return TM_THREADBUILDFAILED;
        }
        //-CJ	printf("threadBuild destination PD: ");
        //-CJ	pdPrint(pdId);
        // check if we have permission to build
        // 1: caller wants to create new thread within its own PD
        // 2: caller has kernel permission
        // 3: the new thread is created by the parent in a child PD
        if (	callerId == 0 || ((callerPD == destPD) ||
                                ((callerThPtr->permission) & KERNEL_PERM_MSK) ||
                                destPD->parent_pdId == callerPD->pdId) )
        {

                // check if max permission & priority are respected (enforce if not)
                if (priority < destPD->priority)
                {
                        // enforce (lower numers meen higher priority)
                        printf("Warning: threadBuild is enforcing priority\n");
                        priority = destPD->priority;
                }
                if (permission & ~(destPD->permission) )
                {
                        // enforce
                        printf("Warning: threadBuild is enforcing permission\n");
                        permission &= (destPD->permission);
                }
                // update information of PD (just the container, rest is done in pdCreate)
                // pdCreate is called by the loader XXX
                TAILQ_ADDTAIL(&(destPD->container), threadPtr, pdCont);


                threadPtr->pdPtr = destPD;
                threadPtr->id = id;
                threadPtr->parentId = callerId;

                if (name != NULL)
                {
                        str_ncpy(threadPtr->name, name, sizeof(threadPtr->name));
                }
                else
                {
                        str_ncpy(threadPtr->name, "no name", sizeof(threadPtr->name));
                }
                initMsgQueue( &(threadPtr->msgQueue));
                /* prepare the value of the status register of the new threads context
                 * it is adjusted that the new thread runs either in user or kernel
                 * mode with interrupts enabled.
                 */

                threadPtr->permission = permission;

                if (space == USER)
                {
                        if (priority < USER_PRIORITY_HIGH)    // smaller means a higher priority
                        {
                                printf("threadBuild: enforcing limit of user priority!\n");
                                priority = USER_PRIORITY_HIGH;
                        }
                        schedulerInsert(threadPtr, priority);
                        mode = STATUS_INT_ENABLE_USER_PREV;
                }
                else
                {
                        schedulerInsert(threadPtr, priority);
                        mode = STATUS_INT_ENABLE_KERNEL_PREV;
                }

                threadPtr->stackStart = (char*)(stackBaseAddress) + stackSize - 4;
                stackStartMappedAt = stackBaseMappedAt + stackSize - 4;
                threadPtr->stackEnd = stackBaseAddress;
                threadPtr->contextPtr = contextPtr;

                // printf("threadPtr->ContextPtr: 0x%x\n", threadPtr->contextPtr );

                /* here we setup the context. register access is machine dependent,
                 * so this is done in TMHal
                 */

                /* this function accesses the context first, e.g. to copy templates */
                tmSetMachineDependentRegisters(threadPtr->contextPtr, destPD);

    // DisplayContext("tmSetMachineDependentRegisters: ", threadPtr->contextPtr);

    /* LR 010115
     * Now, let's create the stack:
     *  - we consider a top-down stack-handling mechanism.  if the stack grows
     *    the other way, this needs to be adjusted !!!
     *           -------------- : top of stack == stackStart
     * sizeof(     exitMessage
     *  exitMsg) ..............
     * exitCode    exitCode
     *   length  .............. :
     * sizeof(     Argument 1   : This part is a machine dependent setup, I think!
     *   void*)  .............. : Nonethelss, we keep it due to the lack of time for
     * sizeof(     Argument 0   : adjusting all the existing code where needed.
     *   void*)  .............. :
     * sizeof(     RETURNADDR   : Address of exitCode
     *      sp   -------------- : stack pointer when thread starts
     */
    sp = (Register)
         ((char*)(threadPtr->stackStart) - /* top of stack */
          sizeof(Message)        - /* message      */
          exitCodeLength         - /* exitCode     */
          sizeof(Register)       - /* argument 1   */
          sizeof(Register)       - /* argument 0   */
          sizeof(Register)         /* return addr  */
         );
    //-LR printf("exitCodeLength: %i, sizeof(Message): %i\n", exitCodeLength, sizeof(Message));

    tmSetStackPointer(threadPtr->contextPtr, sp);

    // printf("threadBuild:  \"%s\"  thId %i  pdId %i\n",name, id, pdId);
    // printf("              stackBase : %p   mappedBase : %p\n", stackBaseAddress, stackBaseMappedAt);
    // printf("              stackStart: %p   mappedStart: %p\n", threadPtr->stackStart, stackStartMappedAt);
    // printf("              contextPtr: %p \n", contextPtr);

    //-CJ dumpMemory(stackStartMappedAt, sizeof(Message) + exitCodeLength, 0);

    byteCopy((char*)stackStartMappedAt - sizeof(Message),
             &threadExitMsg,
             sizeof(Message));

    byteCopy((char*)stackStartMappedAt - sizeof(Message) - exitCodeLength,
             automaticThreadExit,
             exitCodeLength);

    //-CJ dumpMemory(stackStartMappedAt, sizeof(Message) + exitCodeLength, 0);

    /* LR 010115
     * Now, we would set up the second argument if there were any:
     * tmSetArgument1(threadPtr->contextPtr, (Register)parameter_2, (Address)sp, (Address)PAGEROUNDDOWN(stackStartMappedAt));
     */
    tmSetArgument0(threadPtr->contextPtr, (Register)parameter, (Address)sp, 
                    (Address)PAGEROUNDDOWN(stackStartMappedAt));

    /* the code at sp+4 does an automatic exit() and will be executed
     * after the main procedure of the thread does a return without an exit()
     * see also the byteCopies a few lines above...
     */
    /* The return Address needs an adjustment in the case of the ia32: the
     * absolute address is fetched from the stack and jumped to by the ret().
     * The adjustment is made in the HAL-function.  Therefore, 
     * tmSetReturnAddress() MUST BE at least AFTER the tmSetMachineDependentRegisters().
     */
    tmSetReturnAddress(threadPtr->contextPtr,
                       (Register)(threadPtr->stackStart - sizeof(Message) - exitCodeLength),
                       (Address)sp, (Address)PAGEROUNDDOWN(stackStartMappedAt));

    tmSetProgramCounter(threadPtr->contextPtr, (Register)mainFunction);

    tmSetFramePointer(threadPtr->contextPtr, 0);

    tmSetStatusRegister(threadPtr->contextPtr, mode);

    enableAllInterruptsInContext(threadPtr->contextPtr);

    /* tmAdjustMachineDependentRegisters():
     * This function is used to adjust all the setup being done in a wildly 
     * ordered manner to the ProcContext.  Therefore, this machine must be 
     * called at the VERY END of the setup procedure.
     */
    tmAdjustMachineDependentRegisters(threadPtr->contextPtr, space);

  }
  else
  {
    // we do not have permission to create a thread in the given PD
    WARNING("TMThread: failed to create thread (not enough permission)\n");
    return TM_THREADBUILDFAILED;
  }

  // statistics
  tmNewThread();

  return TM_THREADBUILDOK;
}


/* This procedure destroys a thread structure. resources are in the ThreadHashList
 * so threadDestroy doesn't need to know about memory allocation.
 */
Error threadDestroy( ThreadId id)
{
  Thread* threadPtr;
  int pdEmpty = FALSE, pdId;

  /* Lookup in hash table to find pointer onto destination thread */
  if ( (threadPtr = getTHbyId(id)) == NULL)
  {
    /* inexistent threads can't call exit! */
    WARNING("threadExit(): could not find threadId");
    return -1;
  }

  /* Deallocate stack and cleanup the memory.
   * stackEnd is the basis address ! 
   * (have to do this before list remove, since
   * MM needs to lookup the thread pointer there!)
   */
  //-CJ	printf("try to free stack %p\n", (Address)(threadPtr->stackEnd));
  // vmFreeHint(threadPtr->pdPtr->pdId, (Address)(threadPtr->stackEnd)); // this must be done in tmHalDestroyThread()
  //	vmCleanup(threadPtr->id);

  /* Clean up hash table, global thread list and scheduler list */
  lock(threadLock);
  {
    hashListRemove(threadHashList, id);
    listRemove(threadList, threadPtr, NULL);
  }
  unlock(threadLock);

  schedulerRemove(threadPtr);
  
  // remove from PD
  lock(threadLock);
  {
    TAILQ_REMOVE( &(threadPtr->pdPtr->container), threadPtr, pdCont);
    if (TAILQ_EMPTY( &(threadPtr->pdPtr->container) ) )
    {
      pdEmpty = TRUE; // remember it, we cant destroy it yet
      pdId = threadPtr->pdPtr->pdId;
    }
  }
  unlock(threadLock); 
  
  // remove stacks and data structures (Thread and Context)
  if (tmHalDestroyThread(threadPtr, 
                         threadPtr->pdPtr->pdId, 
                         tmThreadPtr->pdPtr->pdId) != TM_OK)
  {
    WARNING("threadDestroy: remove thread from HAL failed");
  }

  if (pdEmpty)
  {
    // PD is empty => destroy it
    pdDestroy(pdId);
  }
  
  // hmFree((Address)threadPtr); // this is now done in tmHalDestroyThread()

  /* Wake all threads that were blocked waiting for
   * a message from exited thread 
   */
  ipcFreeBlockedThreads(id);

  // statistics
  tmDestroyThread();

  return TM_OK;
}


/*
 *
 */
ThreadId threadStart( ThreadMainFunction fctnAddr,
                      ThreadArg parameter, AddressSpace space,
                      char* name, ThreadId parentId, ThreadId destPD,
                      ThreadPriority priority, ThreadId desId)
{
  Thread* threadPtr;
  Address spaceFrom;
  ProcContextPtr ctx;
  unsigned long spaceSize;
  Address stackBaseAddress;
  Address stackBaseMapped;
  size_t stackSize;
  int flags;
  ThreadPermission permission;
#ifdef MACHINE_IS_IXP

  unsigned long tmp1, tmp2;
  extern struct vm_segmap* segmap;
#endif

  /* users may pass illegal pointers to name. this has to be checked here.
   * we check if the user buffer is inside the user address space and
   * watch for overflow!
   */

  if ( (threadPtr = getTHbyId(parentId)) == NULL)
  {
    return TM_THREADSTARTFAILED;
  }
  if ( THREAD_PERMISSION(threadPtr) == USER )
  {
    // Parent is a user thread
    mmAddressSpaceRange(USER, &spaceFrom, &spaceSize);
    if ((Address)name < spaceFrom || (unsigned long)name + MAXNAMESIZE >
        (unsigned long)spaceFrom + spaceSize
        || ULONG_MAX - (unsigned long)name < MAXNAMESIZE)
    {
      name = NULL;
    }
    permission = 0;
  }
  else
  {
    permission = KERNEL_PERM_MSK;
  }

#if 0   // Stack Allocation is now architecture dependent and done in tmHalInitProcCtx()
  /* Preparation of a new Thread entry, memory allocation for both Thread
   * and ProcContext structure. For simplicity, these structures are made
   * contiguous in a single call to hmAlloc(). 
   */
  if (hmAlloc( (Address*)&threadPtr,
               sizeof(Thread)+sizeof(ProcContext)) != HM_ALLOCOK)
  {
    ERROR("hmAlloc failed: couldn't create thread structure");
    return TM_THREADSTARTFAILED;
  }
  if (vmAllocateStack( destPD, &stackBaseAddress, getSegEnd(USER),
                       TM_DEFAULTTHREADSTACKSIZE, &stackBaseMapped) != VM_OK)
  {
    ERROR("couldn't create stack for new thread");
    /* Deallocate of threadPtr */
    hmFree((Address)threadPtr);
    return TM_THREADSTARTFAILED;
  }
  /*-CJ
  	printf("threadStart: vmAllocStack: stackbase %p (%p) end %p hint %p\n", 
  			stackBaseAddress, stackBaseMapped, 
  			stackBaseAddress + TM_DEFAULTTHREADSTACKSIZE, getSegEnd(USER));
   *///-CJ
  /* Three things may fail: we're out of thread id's or we can't insert
   * the thread into either the hash list or the global thread list:
   * in all cases we release the already allocated resources and return
   * a syscall failure.
   */
#endif 

  /* initialize threadPtr and the Stack of the Thread */
  flags = (space == KERNEL) ? TM_KERNEL : TM_NOFLAGS;
  if (tmHalInitThread(&threadPtr, destPD, tmThreadPtr->pdPtr->pdId, 
                      &stackBaseAddress, &stackBaseMapped, &stackSize, flags ) != TM_OK)
  {
    ERROR("Initializing threadPtr and Stack failed");
    return TM_THREADSTARTFAILED;
  }


  // if desId == 0 a new unique Id is returned, otherwise desId is returned
  // if it's not already taken (in this case getThPdomId returns 0)
  threadPtr->id = getThPdomId(desId);
  if (threadPtr->id == 0)
  {
    return TM_THREADSTARTFAILED;
  }

  //-LR   ERROR("Before threadLock");
  lock(threadLock);
  {
    //-LR     ERROR("In threadLock\n");
    //-BL printf("XXX TMThread: adding TID: %i\n", (int) threadPtr->id);
    if ((hashListAdd( threadHashList, threadPtr, threadPtr->id) != HASHOK) ||
        (listAddInFront( threadList, threadPtr, NULL) != LIST_OK))
    {
      unlock(threadLock);
      /* Deallocate stack and thread descriptor */
      // vmFree(stackBaseAddress);    // now architecture dependent
      // hmFree((Address)threadPtr);  // now architecture dependent
      tmHalDestroyThread(threadPtr, desId, tmThreadPtr->pdPtr->pdId);
      ERROR("got invalid thread id or list operations failed");
      return TM_THREADSTARTFAILED;
    }
  }
  unlock(threadLock);

  //-LR   ERROR("after threadLock\n");
  //-LR   Debug("after threadLock\n");
  //-LR   BRK();208f4
 
  /* we should check the priority first */
#warning __no_priority_check__

  //-LR   Debug("Building thread\n");
  //-LR   BRK();
  /* Building of the thread structure */
  if (threadBuild( threadPtr->id, destPD, parentId, name,
                   threadPtr->contextPtr,
                   stackBaseAddress, stackBaseMapped, stackSize,
                   fctnAddr, parameter, space, threadPtr, priority, permission)
      != TM_THREADBUILDOK)
  {
    WARNING("Warning threadStart: failed to build Thread");
    return TM_THREADSTARTFAILED;
  }

  //-CJ	printf("try to free stackBaseMapped %p\n", stackBaseMapped);
  // vmFreeHint(SELFPD, stackBaseMapped); // must be done in tmHalCleanupAfterThreadBuild()
  if (tmHalCleanupAfterThreadBuild(threadPtr, stackBaseMapped, flags) != TM_OK)
  {
    WARNING("failed to clean up after threadBuild()");
  }

  //-CJ threadStructDisplay(threadPtr);
  /* Last page of stack is protected to catch most overflows */
  /* vmProtect(...); */

  /* Initialize the Threads Lock */
  lockInit(&threadPtr->lock);
  lock(&threadPtr->lock);

  /* New thread is put in ready status */
  schedulerSetReady(threadPtr);

  unlock(&threadPtr->lock);
  
  /* at this point the new thread is born and may be scheduled (i.e. even
   * before the parent receives the child id as a reply message
   */
  return threadPtr->id;
}


/*
 * Handle tmExit() syscall
 */
void threadExit(ThreadId threadId)
{
  threadDestroy( threadId);
}


/*
 * This procedure is EXCLUSIVELY invoked in an exception context.
 * Thus it is possible to invoke the restoreContext() function.
 */
void threadYield()
{
  /* The next runnable thread is computed */
  schedule();

  /* next, msgDispatcher returns and a regular restoreContext() is
   * performed
   */
}


Error threadKill( ThreadId killedId, ThreadId killerId)
{
  Thread		*KilledThreadPtr,
  *KillerThreadPtr;

  /* Not allowed for a user thread to kill a kernel thread */
  if ( (KilledThreadPtr = getTHbyId(killedId)) == NULL)
  {
    return TM_THREADKILLFAILED;
  }
  if ( (KillerThreadPtr = getTHbyId(killerId)) == NULL)
  {
    return TM_THREADKILLFAILED;
  }
  if (THREAD_PERMISSION(KillerThreadPtr) == USER && THREAD_PERMISSION(KilledThreadPtr) == KERNEL)
  {
    /* The faulting user thread is killed (recursively) without
     * remorse. the new killer id is TMTHREADID so this will hopefully
     * happen...
     */
    if (threadKill(killerId, TMTHREADID) == TM_KILLFAILED)
    {
      return TM_THREADKILLFAILED;
    }
    return TM_OK;
  }
  threadDestroy(killedId);
  return TM_OK;
}


/* checking for a valid user space address */
static Boolean tmCheckTMInfoAddress(ThreadId id, Address address)
{
  Address spaceFrom;
  unsigned long spaceSize;
  static unsigned long size = sizeof(struct ThreadInfo_t) ;
  Thread* threadPtr;

  if ( (threadPtr = getTHbyId(id)) == NULL)
  {
    return FALSE;
  }
  if ( THREAD_PERMISSION(threadPtr) == KERNEL )
  {
    // it's a kernel thread => address always valid
    return TRUE;
  }

  /* buffer must be in user space (at least). otherwise users could
   * access kernel memory.
   *
   * an exact test would require the address to be inside a vmRegion,
   * but this would be quite an expensive test
   */
  mmAddressSpaceRange(USER, &spaceFrom, &spaceSize);
  if (address < spaceFrom || (unsigned long)address + size >
      (unsigned long)spaceFrom + spaceSize
      || ULONG_MAX - (unsigned long)address < size)
    return FALSE;
  else
    return TRUE;
}


/* answers questions about a thread's identity, nice, huh?
 */
Error threadInfo( ThreadId id, ThreadId aboutId,
                  ThreadInfoKind kind, ThreadInfo* infoPtr, long int values[])
{
  Thread* threadPtr;

  switch (kind)
  {
  case SPECIFIC_ID:
    if (aboutId == SELF)
    {
      aboutId = id;
    }
    /* Lookup in hash table to find pointer onto aboutId thread */
    if ( (threadPtr = getTHbyId(aboutId)) == NULL)
    {
      return TM_FAILED;
    }
    values[0] = aboutId;                 /* own thread id */
    values[1] = threadPtr->parentId;     /* parent thread id */
    break;

  case GETFIRST:
    if (listGetFirst(threadList, (void **)(&threadPtr)) != LIST_OK)
    {
      return TM_FAILED;
    }
    if (threadPtr == NULL)
    {
      return TM_FAILED;
    }
#if 0
    if (tmCheckTMInfoAddress(id, infoPtr) == FALSE)
    {
      printf("threadInfo: tmCheckTMInfoAddress failed\n");
      return TM_FAILED;
    }
#endif
    infoPtr->tid = threadPtr->id;
    infoPtr->ptid = threadPtr->parentId;
    str_cpy(infoPtr->name,threadPtr->name);
    infoPtr->status = threadPtr->schedInfo.status;
    break;

  case GETNEXT:
    if (listGetNext(threadList, (void **)(&threadPtr)) != LIST_OK)
    {
      return TM_FAILED;
    }
    if (threadPtr == NULL)
    {
      return TM_FAILED;
    }
#if 0
    if (tmCheckTMInfoAddress(id, infoPtr) == FALSE)
    {
      return TM_FAILED;
    }
#endif
    infoPtr->tid = threadPtr->id;
    infoPtr->ptid = threadPtr->parentId;
    str_cpy(infoPtr->name, threadPtr->name);
    infoPtr->status = threadPtr->schedInfo.status;
    break;

  case THREAD_BY_NAME:
    // no longer supported (not needed and not compatible with new
    // message passing) If needen has to be implementd in separate
    // call & message type)
    return TM_FAILED;

  case GET_ID_INFO:
    if (aboutId == SELF)
    {
      aboutId = id;
    }
    /* Lookup in hash table to find pointer onto aboutId thread */
    if ( (threadPtr = getTHbyId(aboutId)) == NULL)
    {
      return TM_FAILED;
    }
    if (threadPtr == NULL)
    {
      return TM_FAILED;
    }
#if 0
    if (tmCheckTMInfoAddress(id, infoPtr) == FALSE)
    {
      printf("threadInfo: tmCheckTMInfoAddress failed\n");
      return TM_FAILED;
    }
#endif
    infoPtr->tid = threadPtr->id;
    infoPtr->ptid = threadPtr->parentId;
    str_cpy(infoPtr->name, threadPtr->name);
    infoPtr->status = threadPtr->schedInfo.status;
    break;

  default:
    WARNING("invalid info requested");
    return TM_FAILED ;
    break;
  }
  return TM_OK;
}


/*
 * Returns name of current running thread. Could be replace by a direct
 * reference to (schedulerRunning())->name, however, such a function
 * provides more transparency (the global scheduler variable would have
 * to be declared everywhere where PANIC or WARNING is used !
 */
char* getCurrentThreadName()
{
  if (schedulerRunning() == NULL)
  {
    return NO_RUNNING_THREAD_STRING;
  }
  else
  {
    return schedulerRunning()->name;
  }
}


Thread* getTHbyId(ThreadId thId)
{
  Thread*		myTh;

  hashListGet(threadHashList, (void**)(&myTh), thId);
  // hashListGet writes NULL to myTh if look up failed, so we do not
  // have to catch that here
  return myTh;
}

void threadStructDisplay(Thread *threadPtr)
{
#if 0
  ProcContextPtr c;
#endif

  printf("threadPtr = %p\n", threadPtr);
  if (threadPtr)
  {
    printf("-id       = %li  ", threadPtr->id);
    printf("-name     = %p  ",  threadPtr->name);
    printf("-name     = %s\n",  threadPtr->name );
    printf("-stackStrt= %p  ",  threadPtr->stackStart);
    printf("-stackEnd = %p\n",  threadPtr->stackEnd  );
    printf("-msgQueue = %p  ",  &(threadPtr->msgQueue) );
    printf("-Ctxt     = %p\n",  threadPtr->contextPtr);
  }

#if 0
  c = threadPtr->contextPtr;
  printf("dumping context @ %p\n\tv0:  %p\tv1:  %p\n",
         c, c->returnValue[0], c->returnValue[1]);
  printf("\ta0:  %p\ta1:  %p\ta2:  %p\ta3:  %p\n",
         c->argument[0], c->argument[1], c->argument[2], c->argument[3]);
  printf("\tt0:  %p\tt1:  %p\tt2:  %p\tt3:  %p\n",
         c->callerSaved[0], c->callerSaved[1], c->callerSaved[2], c->callerSaved[3]);
  printf("\tt4:  %p\tt5:  %p\tt6:  %p\tt7:  %p\n",
         c->callerSaved[4], c->callerSaved[5], c->callerSaved[6], c->callerSaved[7]);
  printf("\tt8:  %p\tt9:  %p\n",
         c->callerSaved[8], c->callerSaved[9]);
  printf("\ts0:  %p\ts1:  %p\ts2:  %p\ts3:  %p\n",
         c->calleeSaved[0], c->calleeSaved[1], c->calleeSaved[2], c->calleeSaved[3]);
  printf("\ts4:  %p\ts5:  %p\ts6:  %p\ts7:  %p\n",
         c->calleeSaved[4], c->calleeSaved[5], c->calleeSaved[6], c->calleeSaved[7]);
  printf("\tgp:  %p\tsp:  %p\tfp:  %p\tra:  %p\n",
         c->globalPointer, c->stackPointer, c->framePointer, c->returnAddress);
  printf("\tpc:  %p\thi:  %p\tlo:  %p\tsa:  %p\n",
         c->programCounter, c->hilo[0], c->hilo[0], c->statusRegister);
#endif
}
