/*
   Syscall.c, Copyright  (c) by Eckart Zitzler, Christian Conrad, George Fankhauser,
   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/Topsy/Syscall.c,v $
  Author(s):             Eckart Zitzler, Christian Conrad, George Fankhauser
  Affiliation:           ETH Zuerich, TIK
  Version:               $Revision: 3042 $
  Creation Date:         
  Last Date of Change:   $Date: 2007-06-28 01:28:34 +0200 (Don, 28 Jun 2007) $      by: $Author: sryffel $
 */


#include "Topsy.h"
#include "Messages.h"
#include "Memory.h"
#include "Threads.h"
#include "TMIPC.h"
#include "IODevice.h"
#include "Support.h"
#include "Syscall.h"
#include "MemoryLayout.h"
#include "vm_extern.h"
#include "Assert.h"

#define SYSCALL_ERROR MessageError

/* generic syscall handling
 * this procedure is used for the regular syscalls but is also intended
 * for special messages sent to any server (drivers, user space servers)
 */
SYSCALL_ERROR genericSyscall(ThreadId to, Message* message, Message* reply)
{
  MessageId id;
  
  id = reply->id;
  if (tmMsgSend(to, message) != TM_MSGSENDOK)
  {
    return TM_MSGSENDFAILED;
  }
  tmMsgRecv(&to, ANYMSGTYPE, reply, INFINITY);
  
  if ( reply->id != id )
    return TM_MSGSENDFAILED;
  return TM_MSGRECVOK;
}


/* old memory management */
#if 0

SYSCALL_ERROR vmAlloc(Address *addressPtr, unsigned long int size)
{
  Message message, reply;

  message.id = VM_ALLOC;
  message.msg.vmAlloc.size = size;
  CLEAR_MSGENV(message);
  reply.id = VM_ALLOCREPLY;

  if (genericSyscall(MMTHREADID, &message, &reply) == TM_MSGSENDFAILED)
    return VM_ALLOCFAILED;

  *addressPtr = reply.msg.vmAllocReply.address;
  return reply.msg.vmAllocReply.errorCode;
}


SYSCALL_ERROR vmFree(Address address)
{
  Message message, reply;

  message.id = VM_UNALLOC;
  message.msg.vmFree.address = address;
  CLEAR_MSGENV(message);
  reply.id = VM_FREEREPLY;

  if (genericSyscall(MMTHREADID, &message, &reply) == TM_MSGSENDFAILED)
    return VM_FREEFAILED;

  return reply.msg.vmFreeReply.errorCode;
}


SYSCALL_ERROR vmMove(Address *addressPtr, ThreadId newOwner)
{
  Message message, reply;

  message.id = VM_MOVE;
  message.msg.vmMove.address = *addressPtr;
  message.msg.vmMove.newOwner = newOwner;
  CLEAR_MSGENV(message);
  reply.id = VM_MOVEREPLY;

  if (genericSyscall(MMTHREADID, &message, &reply) == TM_MSGSENDFAILED)
    return VM_MOVEFAILED;

  *addressPtr = reply.msg.vmMoveReply.address;
  return reply.msg.vmMoveReply.errorCode;
}


SYSCALL_ERROR vmProtect(Address startAddress, unsigned long int size,
                       ProtectionMode pmode)
{
  Message message, reply;

  message.id = VM_PROTECT;
  message.msg.vmProtect.startAddress = startAddress;
  message.msg.vmProtect.size = size;
  message.msg.vmProtect.pmode = pmode;
  CLEAR_MSGENV(message);
  reply.id = VM_PROTECTREPLY;

  if (genericSyscall(MMTHREADID, &message, &reply) == TM_MSGSENDFAILED)
    return VM_PROTECTFAILED;

  return reply.msg.vmProtectReply.errorCode;
}


SYSCALL_ERROR vmCleanup(ThreadId threadId)
{
  Message message;

  message.id = VM_CLEANUP;
  message.msg.vmCleanup.threadId = threadId;
  CLEAR_MSGENV(message);

  if (tmMsgSend(MMTHREADID, &message) != TM_MSGSENDOK)
  {
    return VM_CLEANUPFAILED;
  }
  return VM_CLEANUPOK;
}

#endif

/* new virtual memory management */

int vmAllocateStack(ThreadId pdId, address_t *baseaddr,
                    address_t hintaddr, size_t size, address_t *sharedbase)
{
  Message message, reply;

  message.id = VM_ALLOC_STACK;
  message.msg.vm_alloc_stack.pdId = pdId;
  message.msg.vm_alloc_stack.hintaddr = hintaddr;
  message.msg.vm_alloc_stack.size = size;
  CLEAR_MSGENV(message);
  reply.id = VM_REPLY;

  if (genericSyscall(MMTHREADID, &message, &reply) == TM_MSGSENDFAILED)
    return TM_MSGSENDFAILED;

  *baseaddr = reply.msg.vm_reply.ptr1;    /* stack base in target PD */
  *sharedbase = reply.msg.vm_reply.ptr2;  /* mapped base in calling PD */
  return reply.msg.vm_reply.err;
}

int vmFreeHint(ThreadId pdId, address_t hintaddr)
{
  Message message, reply;

  message.id = VM_REGION_FREE_HINT;
  message.msg.vm_free_hint.pdId = pdId;
  message.msg.vm_free_hint.hintaddr = hintaddr;
  CLEAR_MSGENV(message);
  reply.id = VM_REPLY;

  if (genericSyscall(MMTHREADID, &message, &reply) == TM_MSGSENDFAILED)
    return TM_MSGSENDFAILED;

  return reply.msg.vm_reply.err;
}

int vmFreeAll(ThreadId pdId)
{
  Message message, reply;

  message.id = VM_REGION_FREE_ALL;
  message.msg.vm_free_all.pdId = pdId;
  CLEAR_MSGENV(message);
  reply.id = VM_REPLY;

  if (genericSyscall(MMTHREADID, &message, &reply) == TM_MSGSENDFAILED)
    return TM_MSGSENDFAILED;

  return reply.msg.vm_reply.err;
}

/* thread management */
/* NOTE: tmMsgSend() and tmMsgRecv() are implemented in SyscallMsg.S */

SYSCALL_ERROR tmStart( ThreadId* id,
                      ThreadMainFunction function,
                      ThreadArg parameter,
                      char *name)
{
  Message message, reply;


  // attach a page to msg (don't keep, init PP)
  if (attachEnvMsg(&message, VM_PP_INIT ) != 0)
    return TM_STARTFAILED;
  message.id = TM_START;
  // could check length returned after each push...
  pushEnvMsg(&message, &function, sizeof(ThreadMainFunction));
  pushEnvMsg(&message, &parameter, sizeof(ThreadArg));
  if (str_len(name) >= MAXNAMESIZE)
  {
    detachEnvMsg(&message);
    return TM_STARTFAILED;
  }
  pushEnvMsg(&message, name, str_len(name) + 1);
  reply.id = TM_STARTREPLY;

  if (genericSyscall(TMTHREADID, &message, &reply) == TM_MSGSENDFAILED)
    return TM_STARTFAILED;

  *id = reply.msg.tmStartReply.newId;
  return reply.msg.tmStartReply.errorCode;
}


SYSCALL_ERROR tmKill( ThreadId id)
{
  Message message, reply;

  message.id = TM_KILL;
  message.msg.tmKill.id = id;
  CLEAR_MSGENV(message);
  reply.id = TM_KILLREPLY;

  if (genericSyscall(TMTHREADID, &message, &reply) == TM_MSGSENDFAILED)
    return TM_KILLFAILED;

  return reply.msg.tmKillReply.errorCode;
}


void tmYield()
{
  Message message;

  /* Building of a Message */
  message.id = TM_YIELD;
  CLEAR_MSGENV(message);

  /* Sending of message */
  if (tmMsgSend( TMTHREADID,
                 &message) != TM_MSGSENDOK )
  {}
  /* No reply is expected */
}


void tmExit()
{
  Message message;

  /* Building of a Message */
  message.id = TM_EXIT;
  CLEAR_MSGENV(message);

  /* Sending of message */
  while (tmMsgSend(TMTHREADID, &message) != TM_MSGSENDOK)
  {
    /* try until we succeed */
  }
  /* No reply is expected (i.e. the sender does no longer exist) */
}


SYSCALL_ERROR tmGetInfo(ThreadId about, ThreadId* tid, ThreadId* ptid)
{
  Message			message,
  reply;
  long				err,
  *values;
  size_t			len;

  message.id = TM_INFO;
  message.msg.tmInfo.kind = SPECIFIC_ID ;
  message.msg.tmInfo.about = about;
  CLEAR_MSGENV(message);
  reply.id = TM_INFOREPLY;

  if (genericSyscall(TMTHREADID, &message, &reply) == TM_MSGSENDFAILED)
    return TM_INFOFAILED;

  len = popEnvMsg_32(&reply, (uint32*) &err);
  if (len != sizeof(uint32) )
  {
    detachEnvMsg(&reply);
    return TM_INFOFAILED;
  }
  if (err == TM_INFOFAILED)
    return TM_INFOFAILED;			// to prevent poping things which are not there
  len = popEnvMsg(&reply, (address_t*)&values);
  if (len != (sizeof(long) * 2) )
  {
    detachEnvMsg(&reply);
    return TM_INFOFAILED;
  }
  *tid = values[0];
  *ptid = values[1];
  detachEnvMsg(&reply);

  return err;
}


SYSCALL_ERROR tmGetFirst(ThreadInfo* info)
{
  Message			message,
  reply;
  ThreadInfo*	infoPtr;
  long				err,
  *values;
  size_t			len;

  message.id = TM_INFO;
  message.msg.tmInfo.kind = GETFIRST;
  CLEAR_MSGENV(message);
  reply.id = TM_INFOREPLY;

  if (genericSyscall(TMTHREADID, &message, &reply) == TM_MSGSENDFAILED)
    return TM_INFOFAILED;

  len = popEnvMsg_32(&reply, (uint32*) &err);
  if (len != sizeof(uint32) )
  {
    detachEnvMsg(&reply);
    return TM_INFOFAILED;
  }
  if (err == TM_INFOFAILED)
    return TM_INFOFAILED;			// to prevent poping things which are not there
  len = popEnvMsg(&reply, (address_t*)&values);
  if (len != (sizeof(long) * 2) )
  {
    detachEnvMsg(&reply);
    return TM_INFOFAILED;
  }
  len = popEnvMsg(&reply, (address_t*)&infoPtr);
  if (len != sizeof(ThreadInfo) )
  {
    detachEnvMsg(&reply);
    return TM_INFOFAILED;
  }
  byteCopy(info, infoPtr, sizeof(ThreadInfo));
  detachEnvMsg(&reply);

  return err;
}


SYSCALL_ERROR tmGetNext(ThreadInfo* info)
{
  Message			message,
  reply;
  ThreadInfo*	infoPtr;
  long				err,
  *values;
  size_t			len;

  message.id = TM_INFO;
  message.msg.tmInfo.kind = GETNEXT;
  CLEAR_MSGENV(message);
  reply.id = TM_INFOREPLY;

  if (genericSyscall(TMTHREADID, &message, &reply) == TM_MSGSENDFAILED)
    return TM_INFOFAILED;

  len = popEnvMsg_32(&reply, (uint32*) &err);
  if (len != sizeof(uint32) )
  {
    detachEnvMsg(&reply);
    return TM_INFOFAILED;
  }
  if (err == TM_INFOFAILED)
    return TM_INFOFAILED;			// to prevent poping things which are not there
  len = popEnvMsg(&reply, (address_t*)&values);
  if (len != (sizeof(long) * 2) )
  {
    detachEnvMsg(&reply);
    return TM_INFOFAILED;
  }
  len = popEnvMsg(&reply, (address_t*)&infoPtr);
  if (len != sizeof(ThreadInfo) )
  {
    detachEnvMsg(&reply);
    return TM_INFOFAILED;
  }
  byteCopy(info, infoPtr, sizeof(ThreadInfo));
  detachEnvMsg(&reply);

  return err;
}


SYSCALL_ERROR tmGetIDInfo(ThreadId about, ThreadInfo* info)
{
  Message			message,
  reply;
  ThreadInfo*	infoPtr;
  long				err,
  *values;
  size_t			len;

  message.id = TM_INFO ;
  message.msg.tmInfo.about = about;
  message.msg.tmInfo.kind = GET_ID_INFO ;
  CLEAR_MSGENV(message);
  reply.id = TM_INFOREPLY;

  if (genericSyscall(TMTHREADID, &message, &reply) == TM_MSGSENDFAILED)
    return TM_INFOFAILED;

  len = popEnvMsg_32(&reply, (uint32*) &err);
  if (len != sizeof(uint32) )
  {
    detachEnvMsg(&reply);
    return TM_INFOFAILED;
  }
  if (err == TM_INFOFAILED)
    return TM_INFOFAILED;			// to prevent poping things which are not there
  len = popEnvMsg(&reply, (address_t*)&values);
  if (len != (sizeof(long) * 2) )
  {
    detachEnvMsg(&reply);
    return TM_INFOFAILED;
  }
  len = popEnvMsg(&reply, (address_t*)&infoPtr);
  if (len != sizeof(ThreadInfo) )
  {
    detachEnvMsg(&reply);
    return TM_INFOFAILED;
  }
  byteCopy(info, infoPtr, sizeof(ThreadInfo));
  detachEnvMsg(&reply);

  return err;
}


SYSCALL_ERROR tmGetTime(unsigned long* seconds, unsigned long* microSeconds)
{
  Message message, reply;

  message.id = TM_GETTIME ;
  CLEAR_MSGENV(message);
  reply.id = TM_GETTIMEREPLY;

  if (genericSyscall(TMTHREADID, &message, &reply) == TM_MSGSENDFAILED)
    return TM_TIMEFAILED;
  *seconds = reply.msg.tmTime.seconds;
  *microSeconds = reply.msg.tmTime.microSeconds;
  return reply.msg.tmTime.errorCode;
}


SYSCALL_ERROR tmSetTime(unsigned long seconds, unsigned long microSeconds)
{
  Message message, reply;

  message.id = TM_SETTIME ;
  message.msg.tmTime.seconds = seconds;
  message.msg.tmTime.microSeconds = microSeconds;
  CLEAR_MSGENV(message);
  reply.id = TM_SETTIMEREPLY;

  if (genericSyscall(TMTHREADID, &message, &reply) == TM_MSGSENDFAILED)
    return TM_TIMEFAILED;
  return reply.msg.tmTime.errorCode;
}

SYSCALL_ERROR tmSetTimer(unsigned long ticks,
                        unsigned long *timerId)
{
#ifdef WITH_TIMER_QUEUE
  Message msg, reply;

  msg.id    = TM_TIMERSET;
  msg.msg.tmTimerSet.deltaTicks         = ticks;
  CLEAR_MSGENV(msg);
  reply.id  = TM_TIMERSETREPLY;

  if (genericSyscall(TMTHREADID, &msg, &reply) == TM_MSGSENDFAILED)
    return TM_TIMERSETFAILED;
  *timerId = reply.msg.tmTimerSetReply.timerId;
  return reply.msg.tmTimerSetReply.errorCode;

#endif

  return TM_TIMERSETFAILED;
}

SYSCALL_ERROR tmClearTimer(unsigned long timerId)
{
#ifdef WITH_TIMER_QUEUE
  Message msg, reply;

  msg.id    = TM_TIMERCLEAR;
  msg.msg.tmTimerClear.timerId  = timerId;
  CLEAR_MSGENV(msg);
  reply.id  = TM_TIMERCLEARREPLY;

  if (genericSyscall(TMTHREADID, &msg, &reply) == TM_MSGSENDFAILED)
    return TM_TIMERCLEARFAILED;

  return reply.msg.tmTimerClearReply.errorCode;

#endif

  return TM_TIMERCLEARFAILED;
}

/* tmAwaitTimer() simply awaits a message being sent back
 */
SYSCALL_ERROR tmAwaitTimer(unsigned long *timerId)
{
#ifdef WITH_TIMER_QUEUE
  ThreadId sendingThread;
  Message  expiredMsg;

  sendingThread = ANY;                /* who should be the sender */
  expiredMsg.id = TM_TIMEREXPIRED;  /* which kind of message do we expect */
  if (tmMsgRecv(&sendingThread, expiredMsg.id, &expiredMsg, INFINITY)
      == TM_MSGRECVOK)
  {
    *timerId = expiredMsg.msg.tmTimerExpired.timerId;
    return TM_TIMEREXPIREDOK;
  }
#endif
  return TM_TIMEREXPIREDFAILED;
}


/* io interface */

// ioLoad should be used exclusivly by the kernel XXX
SYSCALL_ERROR ioLoad(unsigned short major, Address dev, unsigned short *minor)
{
  Message			msg,
  reply;


  // attach a page to msg (don't keep, init PP)
  if (attachEnvMsg(&msg, VM_PP_INIT ) != 0)
    return IO_LOADFAILED;
  msg.id = IO_LOAD;
  // could check length returned after each push...
  pushEnvMsg(&msg, &major, sizeof(unsigned short));
  pushEnvMsg(&msg, dev, sizeof(IODeviceDesc));
  reply.id = IO_LOADREPLY;

  if (genericSyscall(IOTHREADID, &msg, &reply) == TM_MSGSENDFAILED)
    return IO_LOADFAILED;

  *minor = reply.msg.ioLoadReply.deviceMinor;
  return reply.msg.ioLoadReply.errorCode;
}

SYSCALL_ERROR ioOpen(unsigned short major, unsigned short minor, ThreadId* id)
{
  Message message, reply;

  message.id = IO_OPEN;
  message.msg.ioOpen.deviceMajor = major;
  message.msg.ioOpen.deviceMinor = minor;
  CLEAR_MSGENV(message);
  reply.id = IO_OPENREPLY;

  if (genericSyscall(IOTHREADID, &message, &reply) == TM_MSGSENDFAILED)
    return IO_OPENFAILED;

  *id = reply.msg.ioOpenReply.deviceThreadId;
  return reply.msg.ioOpenReply.errorCode;
}


SYSCALL_ERROR ioClose(ThreadId id)
{
  Message message, reply;
  ThreadId any = ANY;

  message.id = IO_CLOSE;
  message.msg.ioClose.deviceThreadId = id;
  CLEAR_MSGENV(message);
  reply.id = IO_CLOSEREPLY;

  if (tmMsgSend(IOTHREADID, &message) != TM_MSGSENDOK)
  {
    return TM_MSGSENDFAILED;
  }
  /* ioCloseReply may come from a driver or a driver (that's why &any) */
  tmMsgRecv(&any, reply.id, &reply, INFINITY);
  return TM_MSGRECVOK;

  return reply.msg.ioCloseReply.errorCode;
}


SYSCALL_ERROR ioRead(ThreadId id, char* buffer, unsigned long int* nOfBytes)
{
  Message message, reply;

  if (*nOfBytes > PAGESIZE)
  {
    //-CJ		printf("can't read more than %i Bytes at once\n", PAGESIZE);
    *nOfBytes = PAGESIZE;
  }

  message.id = IO_READ;
  message.msg.ioRead.size = *nOfBytes;
  CLEAR_MSGENV(message);
  reply.id = IO_READREPLY;
  if (genericSyscall(id, &message, &reply) == TM_MSGSENDFAILED)
    return IO_READFAILED;

  *nOfBytes = reply.msg.ioReadReply.bytesRead;
  // a more sophisticated and faster method would be to pass
  // the enveloppe pointer on to the thread who called ioRead
  // and free it there. However ioRead jsut reads a few bytes
  // in most cases and for testing the solution with byteCopy
  // will do. XXX
  byteCopy(buffer,reply.enveloppe, *nOfBytes);
  detachEnvMsg(&reply);
  return reply.msg.ioReadReply.errorCode;
}


SYSCALL_ERROR ioWrite(ThreadId id, char* buffer, unsigned long int* nOfBytes)
{
  Message message, reply;

  if (*nOfBytes > PAGESIZE)
  {
    *nOfBytes = PAGESIZE;
  }

  message.id = IO_WRITE;
  message.msg.ioWrite.size = *nOfBytes;
  reply.id = IO_WRITEREPLY;

  // could build up & fill enveloppe in calling thread, but
  // this is a celaner solution and ioWrite tranfers just
  // a few bytes in most cases anyway. XXX
  // attach a page to msg (don't keep, do not init PP)
  // detach is done only in IO
  if (attachEnvMsg(&message, 0) != 0)
    return IO_WRITEFAILED;
  byteCopy(message.enveloppe, buffer, *nOfBytes);

  if (genericSyscall(id, &message, &reply) == TM_MSGSENDFAILED)
    return IO_WRITEFAILED;

  *nOfBytes = reply.msg.ioWriteReply.bytesWritten;
  return reply.msg.ioWriteReply.errorCode;
}


SYSCALL_ERROR ioInit(ThreadId id)
{
  Message message, reply;

  message.id = IO_INIT;
  CLEAR_MSGENV(message);
  reply.id = IO_INITREPLY;

  if (genericSyscall(id, &message, &reply) == TM_MSGSENDFAILED)
    return IO_INITFAILED;

  return reply.msg.ioInitReply.errorCode;
}

SYSCALL_ERROR ioSubscribe(ThreadId id, ThreadId from)
{
  Message message, reply;

  message.id = IO_SUBSCRIBE;
  CLEAR_MSGENV(message);
  reply.id = IO_SUBSCRIBEREPLY;

  if (genericSyscall(id, &message, &reply) == TM_MSGSENDFAILED)
    return IO_SUBSCRIBEFAILED;

  return reply.msg.ioInitReply.errorCode;
}

SYSCALL_ERROR ioUnSubscribe(ThreadId id, ThreadId from)
{
  Message message, reply;

  message.id = IO_UNSUBSCRIBE;
  CLEAR_MSGENV(message);
  reply.id = IO_UNSUBSCRIBEREPLY;

  if (genericSyscall(id, &message, &reply) == TM_MSGSENDFAILED)
    return IO_UNSUBSCRIBEFAILED;

  return reply.msg.ioInitReply.errorCode;
}

void ioGetAddr(ThreadId id, char *addr, int len)
{
  Message message, reply;

  message.id = IO_GETADDR;
  CLEAR_MSGENV(message);
  reply.id = IO_GETADDRREPLY;

  if (genericSyscall(id, &message, &reply) == TM_MSGSENDFAILED)
    return;
  while(len-- > 0)
    addr[len]=reply.msg.ioGetAddrReply.addr[len];
}

SYSCALL_ERROR ioSockGetPort(ThreadId id, unsigned short *port)
{
  Message		message,
  reply;


  message.id = SOCK_GET_PORT;
  CLEAR_MSGENV(message);
  reply.id = NET_SOCKET_PORT;

  if (genericSyscall(id, &message, &reply) == TM_MSGSENDFAILED)
    return IO_SOCKPORTFAILED;

  *port = reply.msg.netSockPort.port;
  return reply.msg.netSockPort.errorCode;
}


/*** push & pop for new message structure ***/
// get a page from MM to build up an eveloppe for carrying large
// data structures (can be accessed by push & pop)
SYSCALL_ERROR attachEnvMsg(Message* msg, uint32 flags)
{
  Message		message,
  reply;


  message.id = VM_ATTACH_MSG_PAGE;
  // pass flags to MM but strip no MM flags
  message.msg.vm_msg_page.flags = flags & ~VM_PP_INIT;
  CLEAR_MSGENV(message);
  reply.id = VM_ATTACH_MSG_PAGE_REPLY;
  if (genericSyscall(MMTHREADID, &message, &reply) == TM_MSGSENDFAILED)
  {
    //-CJ		printf("attachEnvMsg: Syscall not successful\n");
    return -1; // Syscall failed
  }

  msg->enveloppe = reply.msg.vm_msg_page.pageAddr;
  if ( (reply.msg.vm_msg_page.error != VM_OK) || (msg->enveloppe == NULL) )
  {
    //-CJ		printf("attachEnvMsg: got no page...\n");
    // call to MM failed
    return -1;
  }
  else
  {
    // page successfuly attached => initailize enveloppe
    //-BL printf("attachEnvMsg: got a page (%p) => initialize it\n", msg->enveloppe);
    if (flags & VM_PP_INIT)
    {
      // if we use push & pop we have to init the push&pop state
      // variables in the message. If we jsut use the buffer and
      // do it all by hand, we don't want anybody to mess around
      // with the message data
      initEnvMsg(msg);
    }
    return 0;
  }
}


// release the page used for the eveloppe
SYSCALL_ERROR detachEnvMsg(Message* msg)
{
  Message		message,
  reply;


  message.id = VM_DETACH_MSG_PAGE;
  message.msg.vm_msg_page.pageAddr = msg->enveloppe;
  CLEAR_MSGENV(message);
  reply.id = VM_DETACH_MSG_PAGE_REPLY;
  if (genericSyscall(MMTHREADID, &message, &reply) == TM_MSGSENDFAILED)
  {
    //-CJ		printf("detachEnvMsg: Syscall not successful\n");
    return -1; // Syscall failed
  }

  if ( reply.msg.vm_msg_page.error != VM_OK )
  {
    //-CJ		printf("detachEnvMsg: failed\n");
    // call to MM failed
    return -1;
  }
  else
  {
    // page successfuly detached
    msg->enveloppe = NULL;
    return 0;
  }
}


// initialize the state (msg->msg must not be used
// for private data when using pus&pop)
int initEnvMsg(Message* msg)
{
  if (msg == NULL)
    return -1;

  msg->msg.pushpop.num_args = 0;
  msg->msg.pushpop.push_pos = 0;
  msg->msg.pushpop.pop_pos = 0;

  return 0;
}


// store an argument in the page enveloppe
inline int pushEnvMsg(Message* msg, address_t data, size_t len)
{
  if ( (msg->msg.pushpop.push_pos + WORDROUND(len) + sizeof(size_t))
       >= PAGESIZE)
  {
    //-CJ			printf("pushEnvMsg: overflow\n");
    return 0;		// not enough space in page enveloppe
  }
  // store length
  *((size_t*)((uint32)msg->enveloppe + msg->msg.pushpop.push_pos)) = len;
  msg->msg.pushpop.push_pos += sizeof(size_t);
  // store argument
  //-BL printf("pushEnvMsg: push an argument (%p) of length %li to address %p\n", data, len, msg->enveloppe + msg->msg.pushpop.push_pos);
  byteCopy(msg->enveloppe + msg->msg.pushpop.push_pos, data, len);
  msg->msg.pushpop.push_pos += WORDROUND(len);	// align next argument
  // increase argument count
  msg->msg.pushpop.num_args++;
  // return bytes written
  return len;
}


// for ease of use we have some fixed size pushs
int pushEnvMsg_8(Message* msg, uint8 data)
{
  return pushEnvMsg(msg, &data, sizeof(uint8));
}


// for ease of use we have some fixed size pushs
int pushEnvMsg_16(Message* msg, uint16 data)
{
  return pushEnvMsg(msg, &data, sizeof(uint16));
}


// for ease of use we have some fixed size pushs
int pushEnvMsg_32(Message* msg, uint32 data)
{
  return pushEnvMsg(msg, &data, sizeof(uint32));
}


size_t popEnvMsg_8(Message* msg, uint8* retVal)
{
  uint8			*data;
  size_t		len;


  len = popEnvMsg(msg, (address_t*)&data);
  if (len != sizeof(uint8) )
  {
    return 0;
  }
  else
  {
    byteCopy(retVal, data, sizeof(uint8));
    return len;
  }
}


size_t popEnvMsg_16(Message* msg, uint16* retVal)
{
  uint16		*data;
  size_t		len;


  len = popEnvMsg(msg, (address_t*)&data);
  if (len != sizeof(uint16) )
  {
    return 0;
  }
  else
  {
    byteCopy(retVal, data, sizeof(uint16));
    return len;
  }
}


size_t popEnvMsg_32(Message* msg, uint32* retVal)
{
  uint32		*data;
  size_t		len;


  len = popEnvMsg(msg, (address_t*)&data);
  if (len != sizeof(uint32) )
  {
    return 0;
  }
  else
  {
    byteCopy(retVal, data, sizeof(uint32));
    return len;
  }
}


// get next argument from page enveloppe (begin with the
// first argument pushed => FIFO)
inline size_t popEnvMsg(Message* msg, address_t* addr)
{
  size_t	len;


  if (msg->msg.pushpop.num_args == 0 || msg->enveloppe == NULL)
  {
    //-CJ			printf("popEnvMsg: underflow\n");
    return 0;	// no (more) arguments in the enveloppe or attach failed
  }
  // get length of argument
  len = *((size_t*)((uint32)msg->enveloppe + msg->msg.pushpop.pop_pos));
  //-BL printf("popEnvMsg: want to pop an argument of length %li\n", len);
  // get the address of the argument
  // the next size value on word boundary
  *addr = (address_t)((uint32)msg->enveloppe + msg->msg.pushpop.pop_pos
                      + sizeof(size_t));
  // set pop position to next argument (aligned on word boundary)
  msg->msg.pushpop.pop_pos += WORDROUND(len) + sizeof(size_t);
  // reduce number of stored argument by the one popped
  msg->msg.pushpop.num_args--;
  // return the length of the argument
  return len;
}

#if 0
/*** internal support routines ***/
void byte_Copy( Address targetAddress, Address sourceAddress,
                unsigned long int nbBytes)
{
  char* src = (char*)sourceAddress;
  char* tar = (char*)targetAddress;
  unsigned long int i = 0;

  for (i = 0; i < nbBytes; i++)
  {
    *tar++ = *src++;
  }
}
#endif

