//---------------------------------------------------------------------------
//	CondHandleWait
//
//	This function makes the calling process block on the condition variable
//	till either ConditionHandleSignal or ConditionHandleBroadcast is
//	received. The process calling CondHandleWait must have acquired the
//	lock associated with the condition variable (the lock that was passed
//	to CondCreate. This implies the lock handle needs to be stored
//	somewhere. hint! hint!) for this function to
//	succeed. If the calling process has not acquired the lock, it does not
//	block on the condition variable, but a value of 1 is returned
//	indicating that the call was not successful. Return value of 0 implies
//	that the call was successful.
//
//	This function should be written in such a way that the calling process
//	should release the lock associated with this condition variable before
//	going to sleep, so that the process that intends to signal this
//	process could acquire the lock for that purpose. After waking up, the
//	blocked process should acquire (i.e. wait on) the lock associated with
//	the condition variable. In other words, this process does not
//	"actually" wake up until the process calling CondHandleSignal or
//	CondHandleBroadcast releases the lock explicitly.
//---------------------------------------------------------------------------
int CondWait(Cond *c) {
  Link	*l;
  int   intrval;
    
  if (!c) return SYNC_FAIL;

  // Conds are atomic
  intrval = DisableIntrs ();
  dbprintf ('I', "CondWait: Old interrupt value was 0x%x.\n", intrval);

  // Check to see if the current process owns the lock
  if (c->lock->pid != GetCurrentPid()) {
    dbprintf('s', "CondWait: Proc %d does not own cond %d\n", GetCurrentPid(), (int)(c-conds));
    RestoreIntrs(intrval);
    return SYNC_FAIL;
  }

  dbprintf ('s', "CondWait: Proc %d waiting on cond %d.  Putting to sleep.\n", GetCurrentPid(), (int)(c-conds));
  if ((l = AQueueAllocLink ((void *)currentPCB)) == NULL) {
    printf("FATAL ERROR: could not allocate link for cond queue in CondWait!\n");
    exitsim();
  }
  if (AQueueInsertLast (&c->waiting, l) != QUEUE_SUCCESS) {
    printf("FATAL ERROR: could not insert new link into cond waiting queue in CondWait!\n");
    exitsim();
  }
  // Release the lock before going to sleep
  LockRelease(c->lock);
  RestoreIntrs(intrval); // Don't want interrupts disabled while we sleep
  ProcessSleep();
  // Immediately acquire the lock upon waking
  LockAcquire(c->lock);
  return SYNC_SUCCESS;
}
Beispiel #2
0
//--------------------------------------------------------------------------------
// 
// int MboxCloseAllByPid(int pid);
//
// Scans through all mailboxes and removes this pid from their "open procs" list.
// If this was the only open process, then it makes the mailbox available.  Call
// this function in ProcessFreeResources in process.c.
//
// Returns MBOX_FAIL on failure.
// Returns MBOX_SUCCESS on success.
//
//--------------------------------------------------------------------------------
int MboxCloseAllByPid(int pid) {
	int i;
	int j;
	uint32 intrval;
	for(i = 0; i < MBOX_NUM_MBOXES; i++) {
		mbox* xbox = &mboxes[i];
		if(xbox->opened_pids[pid] == true) {	
			xbox->opened_pids[pid] = false;
			xbox->opened_pids_count -= 1;
			intrval = DisableIntrs();
			if(xbox->opened_pids_count == 0) {
				// reset the mbox	
				for(j = xbox->head_cbobi; j <= xbox->tail_cbobi; (j = (j+1) % MBOX_MAX_BUFFERS_PER_MBOX)) {
					mbox_buffers[xbox->cbobi[j]].inuse = false;
					xbox->cbobi[j] = -1;
				}
				//sems[xbox->s_mbox_emptyslots].inuse = false;
				//sems[xbox->s_mbox_fillslots].inuse = false;
				//locks[xbox->l_mbox].inuse = false;
				xbox->head_cbobi = 0;
				xbox->tail_cbobi = 0;
				xbox->inuse = false;
			}
			RestoreIntrs(intrval);
		}
	}
	return MBOX_SUCCESS;
}
Beispiel #3
0
//-------------------------------------------------------
//
// mbox_t MboxCreate();
//
// Allocate an available mailbox structure for use. 
//
// Returns the mailbox handle on success
// Returns MBOX_FAIL on error.
//
//-------------------------------------------------------
mbox_t MboxCreate() {
	mbox_t imbox;
	uint32 intrval;

	// grabbing a Mbox should be an atomic operation
	intrval = DisableIntrs();
	for(imbox = 0; imbox < MBOX_NUM_MBOXES; imbox++) {
		if(mboxes[imbox].inuse == false) {
			mboxes[imbox].inuse = true;
			break;
		}
	}
	RestoreIntrs(intrval);
	if(imbox == MAX_SEMS) return MBOX_FAIL;
	if ((mboxes[imbox].s_mbox_emptyslots = SemCreate(MBOX_MAX_BUFFERS_PER_MBOX - 1)) == SYNC_FAIL) {
		printf("Bad sem_create in MboxOpen");
		exitsim();
	}
	if ((mboxes[imbox].s_mbox_fillslots = SemCreate(0)) == SYNC_FAIL) {
		printf("Bad sem_create in MboxOpen");
		exitsim();
	}
	if((mboxes[imbox].l_mbox = LockCreate()) == SYNC_FAIL) {
		printf("Bad lock_create in MboxModInit");
		exitsim();
	}
	return imbox;
}
//----------------------------------------------------------------------
//
//	SemSignal
//
//	Signal on a semaphore.  Again, details are in Section 6.4 of
//	_OSC_.
//
//----------------------------------------------------------------------
int SemSignal (Sem *sem) {
  Link *l;
  int	intrs;
  PCB *pcb;

  if (!sem) return SYNC_FAIL;

  intrs = DisableIntrs ();
  dbprintf ('s', "SemSignal: Process %d Signalling on sem %d, count=%d.\n", GetCurrentPid(), (int)(sem-sems), sem->count);
  // Increment internal counter before checking value
  sem->count++;
  if (sem->count > 0) { // check if there is a process to wake up
    if (!AQueueEmpty(&sem->waiting)) { // there is a process to wake up
      l = AQueueFirst(&sem->waiting);
      pcb = (PCB *)AQueueObject(l);
      if (AQueueRemove(&l) != QUEUE_SUCCESS) { 
        printf("FATAL ERROR: could not remove link from semaphore queue in SemSignal!\n");
        exitsim();
      }
      dbprintf ('s', "SemSignal: Waking up PID %d.\n", (int)(GetPidFromAddress(pcb)));
      ProcessWakeup (pcb);
      // Decrement counter on behalf of woken up PCB
      sem->count--;
    }
  }
  RestoreIntrs (intrs);
  return SYNC_SUCCESS;
}
//---------------------------------------------------------------------------
//	LockHandleRelease
//
//	This procedure releases the unique lock described by the handle. It
//	first checks whether the lock is a valid lock. If not, it returns SYNC_FAIL.
//	If the lock is a valid lock, it should check whether the calling
//	process actually holds the lock. If not it returns SYNC_FAIL. Otherwise it
//	releases the lock, and returns SYNC_SUCCESS.
//---------------------------------------------------------------------------
int LockRelease(Lock *k) {
  Link *l;
  int	intrs;
  PCB *pcb;

  if (!k) return SYNC_FAIL;

  intrs = DisableIntrs ();
  dbprintf ('s', "LockRelease: Proc %d releasing lock %d.\n", GetCurrentPid(), (int)(k-locks));

  if (k->pid != GetCurrentPid()) {
    dbprintf('s', "LockRelease: Proc %d does not own lock %d.\n", GetCurrentPid(), (int)(k-locks));
    return SYNC_FAIL;
  }
  k->pid = -1;
  if (!AQueueEmpty(&k->waiting)) { // there is a process to wake up
    l = AQueueFirst(&k->waiting);
    pcb = (PCB *)AQueueObject(l);
    if (AQueueRemove(&l) != QUEUE_SUCCESS) { 
      printf("FATAL ERROR: could not remove link from lock queue in LockRelease!\n");
      exitsim();
    }
    dbprintf ('s', "LockRelease: Waking up PID %d, assigning lock.\n", (int)(GetPidFromAddress(pcb)));
    k->pid = GetPidFromAddress(pcb);
    ProcessWakeup (pcb);
  }
  RestoreIntrs (intrs);
  return SYNC_SUCCESS;
}
Beispiel #6
0
//-------------------------------------------------------
//
// mbox_t MboxCreate();
//
// Allocate an available mailbox structure for use. 
//
// Returns the mailbox handle on success
// Returns MBOX_FAIL on error.
//
//-------------------------------------------------------
mbox_t MboxCreate() {
  
  int ct;
  mbox_t mbox_ct;
  uint32 intrval;
  
  // grabbing a mbox should be an atomic operation
  intrval = DisableIntrs();
  for(mbox_ct = 0; mbox_ct < MBOX_NUM_MBOXES; mbox_ct++){
    // inuse = 0: the mbox is reserved, but not opened for any process yet
    if(system_mbox[mbox_ct].num_of_pid_inuse == -1 ){
      system_mbox[mbox_ct].num_of_pid_inuse = 0;
      break;
    }
  }
  RestoreIntrs(intrval);

  if(mbox_ct == MBOX_NUM_MBOXES){
    return MBOX_FAIL;
  }

  system_mbox[mbox_ct].mbox_buffer_head = 0;
  system_mbox[mbox_ct].mbox_buffer_tail = 0;
  system_mbox[mbox_ct].mbox_buffer_slot_used = 0;
  system_mbox[mbox_ct].mbox_buffer_lock = LockCreate();
  system_mbox[mbox_ct].mbox_buffer_fill = CondCreate(system_mbox[mbox_ct].mbox_buffer_lock);
  system_mbox[mbox_ct].mbox_buffer_empty = CondCreate(system_mbox[mbox_ct].mbox_buffer_lock);

  for(ct = 0; ct < PROCESS_MAX_PROCS; ct++){
    // No proc opens the mbox when it is created
    system_mbox[mbox_ct].mbox_pid_list[ct] = 0;
  }

  return mbox_ct;
}
//----------------------------------------------------------------------
//
//	SemWait
//
//	Wait on a semaphore.  As described in Section 6.4 of _OSC_,
//	we decrement the counter and suspend the process if the
//	semaphore's value is less than 0.  To ensure atomicity,
//	interrupts are disabled for the entire operation, but must be
//      turned on before going to sleep.
//
//----------------------------------------------------------------------
int SemWait (Sem *sem) {
  Link	*l;
  int		intrval;
    
  if (!sem) return SYNC_FAIL;

  intrval = DisableIntrs ();
  dbprintf ('I', "SemWait: Old interrupt value was 0x%x.\n", intrval);
  dbprintf ('s', "SemWait: Proc %d waiting on sem %d, count=%d.\n", GetCurrentPid(), (int)(sem-sems), sem->count);
  if (sem->count <= 0) {
    dbprintf('s', "SemWait: putting process %d to sleep\n", GetCurrentPid());
    if ((l = AQueueAllocLink ((void *)currentPCB)) == NULL) {
      printf("FATAL ERROR: could not allocate link for semaphore queue in SemWait!\n");
      exitsim();
    }
    if (AQueueInsertLast (&sem->waiting, l) != QUEUE_SUCCESS) {
      printf("FATAL ERROR: could not insert new link into semaphore waiting queue in SemWait!\n");
      exitsim();
    }
    ProcessSleep();
    // Don't decrement couter here because that's handled in SemSignal for us
  } else {
    sem->count--; // Decrement internal counter
    dbprintf('s', "SemWait: Proc %d granted permission to continue by sem %d\n", GetCurrentPid(), (int)(sem-sems));
  }
  RestoreIntrs (intrval);
  return SYNC_SUCCESS;
}
//---------------------------------------------------------------------------
//	CondHandleSignal
//
//	This call wakes up exactly one process waiting on the condition
//	variable, if at least one is waiting. If there are no processes
//	waiting on the condition variable, it does nothing. In either case,
//	the calling process must have acquired the lock associated with
//	condition variable for this call to succeed, in which case it returns
//	0. If the calling process does not own the lock, it returns 1,
//	indicating that the call was not successful. This function should be
//	written in such a way that the calling process should retain the
//	acquired lock even after the call completion (in other words, it
//	should not release the lock it has already acquired before the call).
//
//	Note that the process woken up by this call tries to acquire the lock
//	associated with the condition variable as soon as it wakes up. Thus,
//	for such a process to run, the process invoking CondHandleSignal
//	must explicitly release the lock after the call is complete.
//---------------------------------------------------------------------------
int CondSignal(Cond *c) {
  Link *l;
  int intrs;
  PCB *pcb;

  if (!c) return SYNC_FAIL;

  intrs = DisableIntrs ();
  dbprintf ('s', "CondSignal: Proc %d signalling cond %d.\n", GetCurrentPid(), (int)(c-conds));

  if (c->lock->pid != GetCurrentPid()) {
    dbprintf('s', "CondSignal: Proc %d does not own cond %d.\n", GetCurrentPid(), (int)(c-conds));
    return SYNC_FAIL;
  }
  if (!AQueueEmpty(&c->waiting)) { // there is a process to wake up
    l = AQueueFirst(&c->waiting);
    pcb = (PCB *)AQueueObject(l);
    if (AQueueRemove(&l) != QUEUE_SUCCESS) { 
      printf("FATAL ERROR: could not remove link from cond queue in CondSignal!\n");
      exitsim();
    }
    dbprintf ('s', "CondSignal: Waking up PID %d, it still needs to acquire lock.\n", GetPidFromAddress(pcb));
    ProcessWakeup (pcb);
  } else {
    dbprintf('s', "CondSignal: Proc %d signalled, but no processes were waiting.\n", GetCurrentPid());
  }
  RestoreIntrs (intrs);
  return SYNC_SUCCESS;
}
Beispiel #9
0
//-------------------------------------------------------
//
// int MboxClose(mbox_t);
//
// Close the mailbox for use to the current process.
// If the number of processes using the given mailbox
// is zero, then disable the mailbox structure and
// return it to the set of available mboxes.
//
// Returns MBOX_FAIL on failure.
// Returns MBOX_SUCCESS on success.
//
//-------------------------------------------------------
int MboxClose(mbox_t handle) {
	int cpid = GetCurrentPid();
	uint32 intrval;
	int j;
	mbox* xbox = &mboxes[handle];
	if(xbox->opened_pids[cpid] == false) {
		return MBOX_FAIL;
	}
	xbox->opened_pids[cpid] = false;
	intrval = DisableIntrs();
	xbox->opened_pids_count -= 1;
	if(xbox->opened_pids_count == 0) {
		// reset the mbox	
		for(j = xbox->head_cbobi; j <= xbox->tail_cbobi; (j = (j+1) % MBOX_MAX_BUFFERS_PER_MBOX)) {
			mbox_buffers[xbox->cbobi[j]].inuse = false;
			xbox->cbobi[j] = -1;
		}
		//sems[xbox->s_mbox_emptyslots].inuse = false;
		//sems[xbox->s_mbox_fillslots].inuse = false;
		//locks[xbox->l_mbox].inuse = false;
		xbox->head_cbobi = 0;
		xbox->tail_cbobi = 0;
		xbox->inuse = false;
	}
	RestoreIntrs(intrval);
	return MBOX_SUCCESS;
}
Beispiel #10
0
//----------------------------------------------------------------------
//
//	ProcessSchedule
//
//	Schedule the next process to run.  If there are no processes to
//	run, exit.  This means that there should be an idle loop process
//	if you want to allow the system to "run" when there's no real
//	work to be done.
//
//	NOTE: the scheduler should only be called from a trap or interrupt
//	handler.  This way, interrupts are disabled.  Also, it must be
//	called consistently, and because it might be called from an interrupt
//	handler (the timer interrupt), it must ALWAYS be called from a trap
//	or interrupt handler.
//
//	Note that this procedure doesn't actually START the next process.
//	It only changes the currentPCB and other variables so the next
//	return from interrupt will restore a different context from that
//	which was saved.
//
//----------------------------------------------------------------------
void ProcessSchedule () {
  PCB *pcb=NULL;
  int i=0;
  Link *l=NULL;
  uint32 intrvals = 0;

  intrvals = DisableIntrs();

  // The OS exits if there's no runnable process.  This is a feature, not a
  // bug.  An easy solution to allowing no runnable "user" processes is to
  // have an "idle" process that's simply an infinite loop.
  if (AQueueEmpty(&runQueue)) {
    if (!AQueueEmpty(&waitQueue)) {
      printf("FATAL ERROR: no runnable processes, but there are sleeping processes waiting!\n");
      l = AQueueFirst(&waitQueue);
      while (l != NULL) {
        pcb = AQueueObject(l);
        printf("Sleeping process %d: ", i++); printf("PID = %d\n", (int)(pcb - pcbs));
        l = AQueueNext(l);
      }
      exitsim();
    }
    printf ("No runnable processes - exiting!\n");
    exitsim ();	// NEVER RETURNS
  }

  // Move the front of the queue to the end if currentPCB is not on sleep queue.  
  // The running process was the one in front.
  if (currentPCB->flags & PROCESS_STATUS_RUNNABLE) {
    AQueueMoveAfter(&runQueue, AQueueLast(&runQueue), AQueueFirst(&runQueue));
  }

  // Now, run the one at the head of the queue.
  pcb = (PCB *)AQueueObject(AQueueFirst(&runQueue));
  currentPCB = pcb;
  dbprintf ('p',"About to switch to PCB 0x%x,flags=0x%x @ 0x%x\n",
	    (int)pcb, pcb->flags, (int)(pcb->sysStackPtr[PROCESS_STACK_IAR]));

  // Clean up zombie processes here.  This is done at interrupt time
  // because it can't be done while the process might still be running
  while (!AQueueEmpty(&zombieQueue)) {
    pcb = (PCB *)AQueueObject(AQueueFirst(&zombieQueue));
    dbprintf ('p', "Freeing zombie PCB 0x%x.\n", (int)pcb);
    if (AQueueRemove(&(pcb->l)) != QUEUE_SUCCESS) {
      printf("FATAL ERROR: could not remove zombie process from zombieQueue in ProcessSchedule!\n");
      exitsim();
    }
    ProcessFreeResources(pcb);
  }

  RestoreIntrs(intrvals);
}
//---------------------------------------------------------------------------
//	LockHandleAcquire
//
//	This routine acquires a lock given its handle. The handle must be a 
//	valid handle for this routine to succeed. In that case this routine 
//	returns SYNC_FAIL. Otherwise the routine returns SYNC_SUCCESS.
//
//	Your implementation should be such that if a process that already owns
//	the lock calls LockHandleAcquire for that lock, it should not block.
//---------------------------------------------------------------------------
int LockAcquire(Lock *k) {
  Link	*l;
  int		intrval;
    
  if (!k) return SYNC_FAIL;

  // Locks are atomic
  intrval = DisableIntrs ();
  dbprintf ('I', "LockAcquire: Old interrupt value was 0x%x.\n", intrval);

  // Check to see if the current process owns the lock
  if (k->pid == GetCurrentPid()) {
    dbprintf('s', "LockAcquire: Proc %d already owns lock %d\n", GetCurrentPid(), (int)(k-locks));
    RestoreIntrs(intrval);
    return SYNC_SUCCESS;
  }

  dbprintf ('s', "LockAcquire: Proc %d asking for lock %d.\n", GetCurrentPid(), (int)(k-locks));
  if (k->pid >= 0) { // Lock is already in use by another process
    dbprintf('s', "LockAcquire: putting process %d to sleep\n", GetCurrentPid());
    if ((l = AQueueAllocLink ((void *)currentPCB)) == NULL) {
      printf("FATAL ERROR: could not allocate link for lock queue in LockAcquire!\n");
      exitsim();
    }
    if (AQueueInsertLast (&k->waiting, l) != QUEUE_SUCCESS) {
      printf("FATAL ERROR: could not insert new link into lock waiting queue in LockAcquire!\n");
      exitsim();
    }
    ProcessSleep();
  } else {
    dbprintf('s', "LockAcquire: lock is available, assigning to proc %d\n", GetCurrentPid());
    k->pid = GetCurrentPid();
  }
  RestoreIntrs(intrval);
  return SYNC_SUCCESS;
}
Beispiel #12
0
//-------------------------------------------------------
// 
// void MboxOpen(mbox_t);
//
// Open the mailbox for use by the current process.  Note
// that it is assumed that the internal lock/mutex handle 
// of the mailbox and the inuse flag will not be changed 
// during execution.  This allows us to get the a valid 
// lock handle without a need for synchronization.
//
// Returns MBOX_FAIL on failure.
// Returns MBOX_SUCCESS on success.
//
//-------------------------------------------------------
int MboxOpen(mbox_t handle) {
	int cpid = GetCurrentPid();
	uint32 intrval;

	if(handle > MBOX_NUM_MBOXES || handle < 0) {
		return MBOX_FAIL;
	}
	if(mboxes[handle].inuse == false) {
		return MBOX_FAIL;
	}
	if(mboxes[handle].opened_pids[cpid] == false) {
		mboxes[handle].opened_pids[cpid] = true;
		intrval = DisableIntrs();
		mboxes[handle].opened_pids_count += 1;
		RestoreIntrs(intrval);
	}
	return MBOX_SUCCESS;
}
//----------------------------------------------------------------------
//
//	SemSignal
//
//	Signal on a semaphore.  Again, details are in Section 6.4 of
//	_OSC_.
//
//----------------------------------------------------------------------
void
SemSignal (Sem *sem)
{
    Link	*l;
    int		intrs;

    intrs = DisableIntrs ();
    dbprintf ('s', "Signalling on sem 0x%x, count=%d.\n", sem, sem->count);
    sem->count += 1;
    if (sem->count <= 0) {
	l = QueueFirst (&sem->waiting);
	QueueRemove (l);
	dbprintf ('s', "Waking up PCB 0x%x.\n", l->object);
	ProcessWakeup ((PCB *)(l->object));
	QueueFreeLink (l);
    }
    RestoreIntrs (intrs);
}
//-------------------------------------------------------
// 
// void MboxOpen(mbox_t);
//
// Open the mailbox for use by the current process.  Note
// that it is assumed that the internal lock/mutex handle 
// of the mailbox and the inuse flag will not be changed 
// during execution.  This allows us to get the a valid 
// lock handle without a need for synchronization.
//
// Returns MBOX_FAIL on failure.
// Returns MBOX_SUCCESS on success.
//
//-------------------------------------------------------
int MboxOpen(mbox_t handle) {
	int intrs;

	if(MailBox[handle].inuse == false)
	{
		printf("Currently passed mailbox handle : %d by calling process : %d is unallocated\n", handle ,GetCurrentPid());
		return MBOX_FAIL;
	}

	intrs = DisableIntrs ();

	MailBox[handle].process_count++;
	MailBox[handle].procs_link[GetCurrentPid()] = 1;
		

	RestoreIntrs (intrs);
	return MBOX_SUCCESS;
}
//----------------------------------------------------------------------
// 	SemCreate
//
//	Grabs a Semaphore, initializes it and returns a handle to this
//	semaphore. All subsequent accesses to this semaphore should be made
//	through this handle.  Returns SYNC_FAIL on failure.
//----------------------------------------------------------------------
sem_t SemCreate(int count) {
  sem_t sem;
  uint32 intrval;

  // grabbing a semaphore should be an atomic operation
  intrval = DisableIntrs();
  for(sem=0; sem<MAX_SEMS; sem++) {
    if(sems[sem].inuse==0) {
      sems[sem].inuse = 1;
      break;
    }
  }
  RestoreIntrs(intrval);
  if(sem==MAX_SEMS) return SYNC_FAIL;

  if (SemInit(&sems[sem], count) != SYNC_SUCCESS) return SYNC_FAIL;
  return sem;
}
//-----------------------------------------------------------------------
//	LockCreate
//
//	LockCreate grabs a lock from the systeme-wide pool of locks and 
//	initializes it.
//	It also sets the inuse flag of the lock to indicate that the lock is
//	being used by a process. It returns a unique id for the lock. All the
//	references to the lock should be made through the returned id. The
//	process of grabbing the lock should be atomic.
//
//	If a new lock cannot be created, your implementation should return
//	INVALID_LOCK (see synch.h).
//-----------------------------------------------------------------------
lock_t LockCreate() {
  lock_t l;
  uint32 intrval;

  // grabbing a lock should be an atomic operation
  intrval = DisableIntrs();
  for(l=0; l<MAX_LOCKS; l++) {
    if(locks[l].inuse==0) {
      locks[l].inuse = 1;
      break;
    }
  }
  RestoreIntrs(intrval);
  if(l==MAX_LOCKS) return SYNC_FAIL;

  if (LockInit(&locks[l]) != SYNC_SUCCESS) return SYNC_FAIL;
  return l;
}
Beispiel #17
0
//-------------------------------------------------------
//
// mbox_t MboxCreate();
//
// Allocate an available mailbox structure for use. 
//
// Returns the mailbox handle on success
// Returns MBOX_FAIL on error.
//
//-------------------------------------------------------
mbox_t MboxCreate() {
    mbox_t mbox;
    uint32 intrval;

    //grabbing a mailbox should be an atomic operation
    intrval = DisableIntrs();
    for(mbox=0; mbox<MBOX_NUM_MBOXES; mbox++){
        if(mboxs[mbox].inuse == 0){
            mboxs[mbox].inuse = 1;
            break;
        }
    }
    RestoreIntrs(intrval);

    if((mboxs[mbox].l = LockCreate()) == SYNC_FAIL){
        printf("FATAL ERROR: Could not create lock for mbox\n");
        exitsim();
    }

    if((mboxs[mbox].s_msg_full = SemCreate(0)) == SYNC_FAIL) {
        printf("Bad sem create in mbox create\n ");
        exitsim();
    }

    if((mboxs[mbox].s_msg_empty = SemCreate(MBOX_MAX_BUFFERS_PER_MBOX)) == SYNC_FAIL) {
        printf("Bad sem create in mbox create\n ");
        exitsim();
    }

    if(mbox == MBOX_NUM_MBOXES) return MBOX_FAIL;

    if(AQueueInit(&mboxs[mbox].messages) != QUEUE_SUCCESS){
        printf("FATAL ERROR: Could not initialize mbox messsage queue\n");
        exitsim();
    }

    if(AQueueInit(&mboxs[mbox].pids) != QUEUE_SUCCESS){
        printf("FATAL ERROR: Could not initialize mbox pid queue\n");
        exitsim();
    }

    return mbox;
}
//----------------------------------------------------------------------
//
//	SemWait
//
//	Wait on a semaphore.  As described in Section 6.4 of _OSC_,
//	we decrement the counter and suspend the process if the
//	semaphore's value is less than 0.  To ensure atomicity,
//	interrupts are disabled for the entire operation.  Note that,
//	if the process is put to sleep, interrupts will be OFF when
//	it returns from sleep.  Thus, we enable interrupts at the end of
//	the routine.
//
//----------------------------------------------------------------------
void
SemWait (Sem *sem)
{
    Link	*l;
    int		intrval;

    intrval = DisableIntrs ();
    dbprintf ('I', "Old interrupt value was 0x%x.\n", intrval);
    dbprintf ('s', "Proc 0x%x waiting on sem 0x%x, count=%d.\n", currentPCB,
	      sem, sem->count);
    sem->count -= 1;
    if (sem->count < 0) {
	l = QueueAllocLink ();
	QueueLinkInit (l, (void *)currentPCB);
	dbprintf ('s', "Suspending current proc (0x%x).\n", currentPCB);
	QueueInsertLast (&sem->waiting, l);
	ProcessSleep ();
    }
    RestoreIntrs (intrval);
}
Beispiel #19
0
int DiskWriteBlock (uint32 blocknum, disk_block *b) {
  int fsfd = -1;
  uint32 intrvals = 0;
  char *filename = NULL;

  if (blocknum >= DISK_NUMBLOCKS) {
    printf("DiskWriteBlock: cannot write to block larger than filesystem size\n");
    return DISK_FAIL;
  }

  // Check that you remembered to rename the filename for your group
  filename = DISK_FILENAME;
  if (filename[11] == 'X') {
    printf("DiskWriteBlock: you didn't change the filesystem filename in include/os/disk.h.  Cowardly refusing to do anything.\n");
    GracefulExit();
  }

  intrvals = DisableIntrs();

  // Open the hard disk file
  if ((fsfd = FsOpen(DISK_FILENAME, FS_MODE_RW)) < 0) {
    printf ("DiskWriteBlock: File system %s cannot be opened!\n", DISK_FILENAME);
    return DISK_FAIL;
  }

  /* printf("DiskWriteBlock: fsfd = %d\n", fsfd); */

  // Write data to virtual disk
  FsSeek(fsfd, blocknum * DISK_BLOCKSIZE, FS_SEEK_SET);
  if (FsWrite(fsfd, b->data, DISK_BLOCKSIZE) != DISK_BLOCKSIZE) {
    printf ("DiskWriteBlock: Block %d could not be written!\n", blocknum);
    FsClose (fsfd);
    return DISK_FAIL;
  }

  // Close the hard disk file
  FsClose (fsfd);

  RestoreIntrs(intrvals);
  return DISK_BLOCKSIZE;
}
//--------------------------------------------------------------------------
//	CondCreate
//
//	This function grabs a condition variable from the system-wide pool of
//	condition variables and associates the specified lock with
//	it. It should also initialize all the fields that need to initialized.
//	The lock being associated should be a valid lock, which means that
//	it should have been obtained via previous call to LockCreate(). 
//	
//	If for some reason a condition variable cannot be created (no more
//	condition variables left, or the specified lock is not a valid lock),
//	this function should return INVALID_COND (see synch.h). Otherwise it
//	should return handle of the condition variable.
//--------------------------------------------------------------------------
cond_t CondCreate(lock_t lock) {
  cond_t c;
  uint32 intrval;

  if (lock < 0) return SYNC_FAIL;

  // grabbing a cond should be an atomic operation
  intrval = DisableIntrs();
  for(c=0; c<MAX_LOCKS; c++) {
    if (conds[c].inuse==0) {
      conds[c].inuse = 1;
      break;
    }
  }
  RestoreIntrs(intrval);
  if(c==MAX_CONDS) return SYNC_FAIL;

  if (CondInit(&conds[c]) != SYNC_SUCCESS) return SYNC_FAIL;
  conds[c].lock = &locks[lock];
  return c;
}
//----------------------------------------------------------------------
//
//	doInterrupt
//
//	Handle an interrupt or trap.
//
//----------------------------------------------------------------------
void
dointerrupt (unsigned int cause, unsigned int iar, unsigned int isr,
	     uint32 *trapArgs)
{
  int	result;
  int	i;
  uint32	args[4];
  int	intrs;

  dbprintf ('t',"Interrupt cause=0x%x iar=0x%x isr=0x%x args=0x%08x.\n",
	    cause, iar, isr, (int)trapArgs);
  // If the TRAP_INSTR bit is set, this was from a trap instruction.
  // If the bit isn't set, this was a system interrupt.
  if (cause & TRAP_TRAP_INSTR) {
    cause &= ~TRAP_TRAP_INSTR;
    switch (cause) {
    case TRAP_CONTEXT_SWITCH:
      dbprintf ('t', "Got a context switch trap!\n");
      ProcessSchedule ();
      break;
    case TRAP_EXIT:
      dbprintf ('t', "Got an exit trap!\n");
      ProcessDestroy (currentPCB);
      ProcessSchedule ();
      break;
    case TRAP_PROCESS_FORK:
      dbprintf ('t', "Got a fork trap!\n");
      break;
    case TRAP_PROCESS_SLEEP:
      dbprintf ('t', "Got a process sleep trap!\n");
      ProcessSuspend (currentPCB);
      ProcessSchedule ();
      break;
    case TRAP_PRINTF:
      // Call the trap printf handler and pass the arguments and a flag
      // indicating whether the trap was called from system mode.
      dbprintf ('t', "Got a printf trap!\n");
      TrapPrintfHandler (trapArgs, isr & DLX_STATUS_SYSMODE);
      break;
    case TRAP_OPEN:
      // Get the arguments to the trap handler.  If this is a user mode trap,
      // copy them from user space.
      if (isr & DLX_STATUS_SYSMODE) {
	args[0] = trapArgs[0];
	args[1] = trapArgs[1];
      } else {
	char	filename[32];
	// trapArgs points to the trap arguments in user space.  There are
	// two of them, so copy them to to system space.  The first argument
	// is a string, so it has to be copied to system space and the
	// argument replaced with a pointer to the string in system space.
	MemoryCopyUserToSystem (currentPCB, trapArgs, args, sizeof(args[0])*2);
	MemoryCopyUserToSystem (currentPCB, args[0], filename, 31);
	// Null-terminate the string in case it's longer than 31 characters.
	filename[31] = '\0';
	// Set the argument to be the filename
	args[0] = (uint32)filename;
      }
      // Allow Open() calls to be interruptible!
      intrs = EnableIntrs ();
      ProcessSetResult (currentPCB, args[1] + 0x10000);
      printf ("Got an open with parameters ('%s',0x%x)\n", (char *)args[0], args[1]);
      RestoreIntrs (intrs);
      break;
    case TRAP_CLOSE:
      // Allow Close() calls to be interruptible!
      intrs = EnableIntrs ();
      ProcessSetResult (currentPCB, -1);
      RestoreIntrs (intrs);
      break;
    case TRAP_READ:
      // Allow Read() calls to be interruptible!
      intrs = EnableIntrs ();
      ProcessSetResult (currentPCB, -1);
      RestoreIntrs (intrs);
      break;
    case TRAP_WRITE:
      // Allow Write() calls to be interruptible!
      intrs = EnableIntrs ();
      ProcessSetResult (currentPCB, -1);
      RestoreIntrs (intrs);
      break;
    case TRAP_DELETE:
      intrs = EnableIntrs ();
      ProcessSetResult (currentPCB, -1);
      RestoreIntrs (intrs);
      break;
    case TRAP_SEEK:
      intrs = EnableIntrs ();
      ProcessSetResult (currentPCB, -1);
      RestoreIntrs (intrs);
      break;
    case TRAP_PROCESS_GETPID:
      intrs = EnableIntrs ();
      ProcessSetResult (currentPCB, GetCurrentPid());
      RestoreIntrs (intrs);
      break;
    default:
      printf ("Got an unrecognized trap (0x%x) - exiting!\n",
	      cause);
      exitsim ();
      break;
    }
  } else {
    switch (cause) {
    case TRAP_TIMER:
      dbprintf ('t', "Got a timer interrupt!\n");
      ProcessSchedule ();
      break;
    case TRAP_KBD:
      do {
	i = *((uint32 *)DLX_KBD_NCHARSIN);
	result = *((uint32 *)DLX_KBD_GETCHAR);
	printf ("Got a keyboard interrupt (char=0x%x(%c), nleft=%d)!\n",
		result, result, i);
      } while (i > 1);
      break;
    case TRAP_ACCESS:
      printf ("Exiting after illegal access at iar=0x%x, isr=0x%x\n",
	      iar, isr);
      exitsim ();
      break;
    case TRAP_ADDRESS:
      printf ("Exiting after illegal address at iar=0x%x, isr=0x%x\n",
	      iar, isr);
      exitsim ();
      break;
    case TRAP_ILLEGALINST:
      printf ("Exiting after illegal instruction at iar=0x%x, isr=0x%x\n",
	      iar, isr);
      exitsim ();
      break;
    case TRAP_PAGEFAULT:
      printf ("Exiting after page fault at iar=0x%x, isr=0x%x\n",
	      iar, isr);
      exitsim ();
      break;
    default:
      printf ("Got an unrecognized system interrupt (0x%x) - exiting!\n",
	      cause);
      exitsim ();
      break;
    }
  }
  dbprintf ('t',"About to return from dointerrupt.\n");
  // Note that this return may schedule a new process!
  intrreturn ();
}
Beispiel #22
0
//----------------------------------------------------------------------
//
//	doInterrupt
//
//	Handle an interrupt or trap.
//
//----------------------------------------------------------------------
void
dointerrupt (unsigned int cause, unsigned int iar, unsigned int isr,
	     uint32 *trapArgs)
{
  int	result;
  int	i;
  uint32	args[4];
  int	intrs;
  uint32 handle;
  int ihandle;

  dbprintf ('t',"Interrupt cause=0x%x iar=0x%x isr=0x%x args=0x%08x.\n",
	    cause, iar, isr, (int)trapArgs);
  // If the TRAP_INSTR bit is set, this was from a trap instruction.
  // If the bit isn't set, this was a system interrupt.
  if (cause & TRAP_TRAP_INSTR) {
    cause &= ~TRAP_TRAP_INSTR;
    switch (cause) {
    case TRAP_CONTEXT_SWITCH:
      dbprintf ('t', "Got a context switch trap!\n");
      ProcessSchedule ();
      ClkResetProcess();
      break;
    case TRAP_EXIT:
    case TRAP_USER_EXIT:
      dbprintf ('t', "Got an exit trap!\n");
      ProcessDestroy (currentPCB);
      ProcessSchedule ();
      ClkResetProcess();
      break;
    case TRAP_PROCESS_FORK:
      dbprintf ('t', "Got a fork trap!\n");
      break;
    case TRAP_PROCESS_SLEEP:
      dbprintf ('t', "Got a process sleep trap!\n");
      ProcessSuspend (currentPCB);
      ProcessSchedule ();
      ClkResetProcess();
      break;
    case TRAP_PRINTF:
      // Call the trap printf handler and pass the arguments and a flag
      // indicating whether the trap was called from system mode.
      dbprintf ('t', "Got a printf trap!\n");
      TrapPrintfHandler (trapArgs, isr & DLX_STATUS_SYSMODE);
      break;
    case TRAP_OPEN:
      // Get the arguments to the trap handler.  If this is a user mode trap,
      // copy them from user space.
      if (isr & DLX_STATUS_SYSMODE) {
	args[0] = trapArgs[0];
	args[1] = trapArgs[1];
      } else {
	char	filename[32];
	// trapArgs points to the trap arguments in user space.  There are
	// two of them, so copy them to to system space.  The first argument
	// is a string, so it has to be copied to system space and the
	// argument replaced with a pointer to the string in system space.
	MemoryCopyUserToSystem (currentPCB, (char *)trapArgs, (char *)args, sizeof(args[0])*2);
	MemoryCopyUserToSystem (currentPCB, (char *)(args[0]), (char *)filename, 31);
	// Null-terminate the string in case it's longer than 31 characters.
	filename[31] = '\0';
	// Set the argument to be the filename
	args[0] = (uint32)filename;
      }
      // Allow Open() calls to be interruptible!
      intrs = EnableIntrs ();
      ProcessSetResult (currentPCB, args[1] + 0x10000);
      printf ("Got an open with parameters ('%s',0x%x)\n", (char *)(args[0]), args[1]);
      RestoreIntrs (intrs);
      break;
    case TRAP_CLOSE:
      // Allow Close() calls to be interruptible!
      intrs = EnableIntrs ();
      ProcessSetResult (currentPCB, -1);
      RestoreIntrs (intrs);
      break;
    case TRAP_READ:
      // Allow Read() calls to be interruptible!
      intrs = EnableIntrs ();
      ProcessSetResult (currentPCB, -1);
      RestoreIntrs (intrs);
      break;
    case TRAP_WRITE:
      // Allow Write() calls to be interruptible!
      intrs = EnableIntrs ();
      ProcessSetResult (currentPCB, -1);
      RestoreIntrs (intrs);
      break;
    case TRAP_DELETE:
      intrs = EnableIntrs ();
      ProcessSetResult (currentPCB, -1);
      RestoreIntrs (intrs);
      break;
    case TRAP_SEEK:
      intrs = EnableIntrs ();
      ProcessSetResult (currentPCB, -1);
      RestoreIntrs (intrs);
      break;
    case TRAP_PROCESS_GETPID:
      ProcessSetResult(currentPCB, GetCurrentPid()); 
      break;
    case TRAP_PROCESS_CREATE:
      TrapProcessCreateHandler(trapArgs, isr & DLX_STATUS_SYSMODE);
      break;
    case TRAP_SEM_CREATE:
      ihandle = GetIntFromTrapArg(trapArgs, isr & DLX_STATUS_SYSMODE);
      ihandle = SemCreate(ihandle);
      ProcessSetResult(currentPCB, ihandle); //Return handle
      break;
    case TRAP_SEM_WAIT:
      ihandle = GetIntFromTrapArg(trapArgs, isr & DLX_STATUS_SYSMODE);
      handle = SemHandleWait(ihandle);
      ProcessSetResult(currentPCB, handle); //Return 1 or 0
      break;
    case TRAP_SEM_SIGNAL:
      ihandle = GetIntFromTrapArg(trapArgs, isr & DLX_STATUS_SYSMODE);
      handle = SemHandleSignal(ihandle);
      ProcessSetResult(currentPCB, handle); //Return 1 or 0
      break;
    case TRAP_MALLOC:
      ihandle = GetIntFromTrapArg(trapArgs, isr & DLX_STATUS_SYSMODE);
      ihandle = (int)malloc(currentPCB, ihandle);
      ProcessSetResult(currentPCB, ihandle); //Return handle
      break;
    case TRAP_MFREE:
      ihandle = GetIntFromTrapArg(trapArgs, isr & DLX_STATUS_SYSMODE);
      ihandle = mfree(currentPCB, (void*)ihandle);
      ProcessSetResult(currentPCB, ihandle); //Return handle
      break;
    case TRAP_LOCK_CREATE:
      ihandle = LockCreate();
      ProcessSetResult(currentPCB, ihandle); //Return handle
      break;
    case TRAP_LOCK_ACQUIRE:
      ihandle = GetIntFromTrapArg(trapArgs, isr & DLX_STATUS_SYSMODE);
      handle = LockHandleAcquire(ihandle);
      ProcessSetResult(currentPCB, handle); //Return 1 or 0
      break;
    case TRAP_LOCK_RELEASE:
      ihandle = GetIntFromTrapArg(trapArgs, isr & DLX_STATUS_SYSMODE);
      handle = LockHandleRelease(ihandle);
      ProcessSetResult(currentPCB, handle); //Return 1 or 0
      break;
    case TRAP_COND_CREATE:
      ihandle = GetIntFromTrapArg(trapArgs, isr & DLX_STATUS_SYSMODE);
      ihandle = CondCreate(ihandle);
      ProcessSetResult(currentPCB, ihandle); //Return handle
      break;
    case TRAP_COND_WAIT:
      ihandle = GetIntFromTrapArg(trapArgs, isr & DLX_STATUS_SYSMODE);
      ihandle = CondHandleWait(ihandle);
      ProcessSetResult(currentPCB, ihandle); //Return 1 or 0
      break;
    case TRAP_COND_SIGNAL:
      ihandle = GetIntFromTrapArg(trapArgs, isr & DLX_STATUS_SYSMODE);
      ihandle = CondHandleSignal(ihandle);
      ProcessSetResult(currentPCB, ihandle); //Return 1 or 0
      break;
    case TRAP_COND_BROADCAST:
      ihandle = GetIntFromTrapArg(trapArgs, isr & DLX_STATUS_SYSMODE);
      ihandle = CondHandleBroadcast(ihandle);
      ProcessSetResult(currentPCB, ihandle); //Return 1 or 0
      break;
    default:
      printf ("Got an unrecognized trap (0x%x) - exiting!\n",
	      cause);
      exitsim ();
      break;
    }
  } else {
    switch (cause) {
    case TRAP_TIMER:
      dbprintf ('t', "Got a timer interrupt!\n");
      // ClkInterrupt returns 1 when 1 "process quantum" has passed, meaning
      // that it's time to call ProcessSchedule again.
      if (ClkInterrupt()) {
        ProcessSchedule ();
      }
      break;
    case TRAP_KBD:
      do {
	i = *((uint32 *)DLX_KBD_NCHARSIN);
	result = *((uint32 *)DLX_KBD_GETCHAR);
	printf ("Got a keyboard interrupt (char=0x%x(%c), nleft=%d)!\n",
		result, result, i);
      } while (i > 1);
      break;
    case TRAP_ACCESS:
      printf ("Exiting after illegal access at iar=0x%x, isr=0x%x\n", iar, isr);
      exitsim ();
      break;
    case TRAP_ADDRESS:
      printf ("Exiting after illegal address at iar=0x%x, isr=0x%x\n",
	      iar, isr);
      exitsim ();
      break;
    case TRAP_ILLEGALINST:
      printf ("Exiting after illegal instruction at iar=0x%x, isr=0x%x\n",
	      iar, isr);
      exitsim ();
      break;
    case TRAP_PAGEFAULT:
      MemoryPageFaultHandler(currentPCB);
      break;
    default:
      printf ("Got an unrecognized system interrupt (0x%x) - exiting!\n",
	      cause);
      exitsim ();
      break;
    }
  }
  dbprintf ('t',"About to return from dointerrupt.\n");
  // Note that this return may schedule a new process!
  intrreturn ();
}
Beispiel #23
0
//----------------------------------------------------------------------
//
//	ProcessFork
//
//	Create a new process and make it runnable.  This involves the
//	following steps:
//	* Allocate resources for the process (PCB, memory, etc.)
//	* Initialize the resources
//	* Place the PCB on the runnable queue
//
//	NOTE: This code has been tested for system processes, but not
//	for user processes.
//
//----------------------------------------------------------------------
int ProcessFork (VoidFunc func, uint32 param, char *name, int isUser) {
  int i,j;                   // Loop index variable
  int fd, n;               // Used for reading code from files.
  int start, codeS, codeL; // Used for reading code from files.
  int dataS, dataL;        // Used for reading code from files.
  int addr = 0;            // Used for reading code from files.
  unsigned char buf[100];  // Used for reading code from files.
  uint32 *stackframe;      // Stores address of current stack frame.
  PCB *pcb;                // Holds pcb while we build it for this process.
  int intrs;               // Stores previous interrupt settings.
  uint32  initial_user_params[MAX_ARGS+2]; // Initial memory for user parameters (argc, argv)
                                           // initial_user_params[0] = argc
                                           // initial_user_params[1] = argv, points to initial_user_params[2]
                                           // initial_user_params[2] = address of string for argv[0]
                                           // initial_user_params[3] = address of string for argv[1]
                                           //                           ...
  uint32 argc=0;           // running counter for number of arguments
  uint32 offset;           // Used in parsing command line argument strings, holds offset (in bytes) from 
                           // beginning of the string to the current argument.
  uint32 initial_user_params_bytes;  // total number of bytes in initial user parameters array

  int newpage;
  int index;
  int *p;


  intrs = DisableIntrs ();
  dbprintf ('I', "ProcessFork-Old interrupt value was 0x%x.\n", intrs);
  dbprintf ('p', "ProcessFork-Entering ProcessFork args=0x%x 0x%x %s %d\n", (int)func,
	    param, name, isUser);
  // Get a free PCB for the new process
  if (AQueueEmpty(&freepcbs)) {
    printf ("ProcessFork-FATAL error: no free processes!\n");
    exitsim ();	// NEVER RETURNS!
  }
  pcb = (PCB *)AQueueObject(AQueueFirst (&freepcbs));
  dbprintf ('p', "ProcessFork-Got a link @ 0x%x\n", (int)(pcb->l));
  if (AQueueRemove (&(pcb->l)) != QUEUE_SUCCESS) {
    printf("ProcessFork-FATAL ERROR: could not remove link from freepcbsQueue in ProcessFork!\n");
    exitsim();
  }
  // This prevents someone else from grabbing this process
  ProcessSetStatus (pcb, PROCESS_STATUS_RUNNABLE);

  // At this point, the PCB is allocated and nobody else can get it.
  // However, it's not in the run queue, so it won't be run.  Thus, we
  // can turn on interrupts here.
  RestoreIntrs (intrs);

  // Copy the process name into the PCB.
  dstrcpy(pcb->name, name);

  //----------------------------------------------------------------------
  // This section initializes the memory for this process
  //----------------------------------------------------------------------
  // Allocate 1 page for system stack, 1 page for user stack (at top of
  // virtual address space), and 4 pages for user code and global data.

  //---------------------------------------------------------
  // STUDENT: allocate pages for a new process here.  The
  // code below assumes that you set the "stackframe" variable
  // equal to the last 4-byte-aligned address in physical page
  // for the system stack.
  //---------------------------------------------------------

  ////////////////////////////////////////////////////////////////
  // JSM, allocate 6 physical pages for new process

  // First, get L2 Page Table for index 0 of L1 Page Table
  index = MemoryAllocateL2PT();
	if (index == -1)
	{
		printf ("ProcessFork-FATAL: couldn't allocate L2 Page Table for index 0 of L1 Page Table - no free page tables!\n");
		exitsim ();	// NEVER RETURNS!
	}
  // Assign L1 entry to address of start of L2 Page Table
	pcb->pagetable[0] = (uint32)&level2_pt_block[index];

	p = (uint32 *)pcb->pagetable[0];//L2

  // Allocate 4 pages for code and data
  for(i = 0; i < 4; i++)
  {
		newpage = MemoryAllocatePage();
		if (newpage == 0)
		{
			printf ("ProcessFork-FATAL: couldn't allocate memory - no free pages!\n");
			exitsim ();	// NEVER RETURNS!
		}
		dbprintf('p', "ProcessFork-Allocating physical page #%d (Address 0x%.8X) for process virtual page #%d (data/code)\n", newpage, (newpage*MEM_PAGE_SIZE), i);

		*(p+i) = ((newpage*MEM_PAGE_SIZE) | MEM_PTE_VALID);
		dbprintf('p', "Contents at 0x%.8X: 0x%.8X\n\n", (int)(p+i), *(p+i));
  }
	/////////////////////////////////////////////////////
	//YF ADDED allocate page for heap
  // First, initialize the heapfree map
  for (j=0; j<MEM_MAX_HEAP_FREEMAP; j++){
  	pcb->HeapFreeMap[j] = 0;
  }
  for (j =0; j<MEM_MAX_HEAP_POINTER_ARRAY; j++){
  	pcb->HeapPtrSizes[j] = 0;
  }

  index = MemoryAllocateL2PT();
	if (index == -1)
	{
		printf ("ProcessFork-FATAL: couldn't allocate L2 Page Table for index 0 of L1 Page Table - no free page tables!\n");
		exitsim ();	// NEVER RETURNS!
	}
  // Assign L1 entry to address of start of L2 Page Table
	pcb->pagetable[0] = (uint32)&level2_pt_block[index];
	p = (uint32 *)pcb->pagetable[0];

	newpage = MemoryAllocatePage();
	if (newpage == 0)
	{
		printf ("ProcessFork-FATAL: couldn't allocate memory - no free pages!\n");
		exitsim ();	// NEVER RETURNS!
	}
	dbprintf('p', "ProcessFork-Allocating physical page #%d (Address 0x%.8X) for process virtual page #%d (Heap Allocation)\n", newpage, (newpage*MEM_PAGE_SIZE), i);
	//address for catch in Memory translate. UserHeap Address =0x4000 =
	*(p+i) = ((newpage*MEM_PAGE_SIZE) | MEM_PTE_VALID | MEM_PTE_HEAP); //TODO ProcessFork: marked as a heap.
	pcb->userHeapArea = (uint32 *)(newpage*MEM_PAGE_SIZE);
	dbprintf('p', "Heap area is at physical address:0x%.8X   L2 Address: 0x%.8X\n", (int)(newpage*MEM_PAGE_SIZE), *(p+i));
	dbprintf('p', "Contents at 0x%.8X: 0x%.8X\n\n", (int)(p+i), *(p+i));
	//Done with Heap allocation

///////////////////////////////////////////////////////////////////////////////////////////////////
  // Allocate page for user stack
  // First, get L2 Page Table for index 15 of L1 Page Table
  index = MemoryAllocateL2PT();
	if (index == -1)
	{
		printf ("ProcessFork-FATAL: couldn't allocate L2 Page Table for index 0 of L1 Page Table - no free page tables!\n");
		exitsim ();	// NEVER RETURNS!
	}
  // Assign L1 entry to address of start of L2 Page Table
	pcb->pagetable[MEM_L1_PAGE_TABLE_SIZE-1] = (uint32)&level2_pt_block[index];
	p = (uint32 *)pcb->pagetable[MEM_L1_PAGE_TABLE_SIZE-1];

	newpage = MemoryAllocatePage();
	if (newpage == 0)
	{
		printf ("ProcessFork-FATAL: couldn't allocate memory - no free pages!\n");
		exitsim ();	// NEVER RETURNS!
	}
	dbprintf('p', "ProcessFork-Allocating physical page #%d (Address 0x%.8X) for process virtual page #%d (user stack)\n\n", newpage, (newpage*MEM_PAGE_SIZE), (MEM_L1_PAGE_TABLE_SIZE*MEM_L2_PAGE_TABLE_SIZE)-1);

	*(p+(MEM_L2_PAGE_TABLE_SIZE-1)) = ((newpage*MEM_PAGE_SIZE) | MEM_PTE_VALID);
	dbprintf('p', "Contents at 0x%.8X: 0x%.8X\n\n", (int)(p+(MEM_L2_PAGE_TABLE_SIZE-1)), *(p+(MEM_L2_PAGE_TABLE_SIZE-1)));

  // Allocate page for system stack
	newpage = MemoryAllocatePage();
	if (newpage == 0)
	{
		printf ("ProcessFork-FATAL: couldn't allocate memory - no free pages!\n");
		exitsim ();	// NEVER RETURNS!
	}
	dbprintf('p', "ProcessFork-Allocating physical page #%d (Address 0x%.8X) for process system stack\n\n", newpage, (newpage*MEM_PAGE_SIZE));
	pcb->sysStackArea = newpage * MEM_PAGE_SIZE;
	stackframe = (uint32 *)(pcb->sysStackArea + (MEM_PAGE_SIZE-4));
	dbprintf('p', "ProcessFork-Initializing system stack pointer to 0x%.8X\n\n", (uint32)stackframe);

  ////////////////////////////////////////////////////////////////

  // Now that the stack frame points at the bottom of the system stack memory area, we need to
  // move it up (decrement it) by one stack frame size because we're about to fill in the
  // initial stack frame that will be loaded for this PCB when it gets switched in by 
  // ProcessSchedule the first time.
  stackframe -= PROCESS_STACK_FRAME_SIZE;

  // The system stack pointer is set to the base of the current interrupt stack frame.
  pcb->sysStackPtr = stackframe;
  // The current stack frame pointer is set to the same thing.
  pcb->currentSavedFrame = stackframe;

  //----------------------------------------------------------------------
  // This section sets up the stack frame for the process.  This is done
  // so that the frame looks to the interrupt handler like the process
  // was "suspended" right before it began execution.  The standard
  // mechanism of swapping in the registers and returning to the place
  // where it was "interrupted" will then work.
  //----------------------------------------------------------------------

  // The previous stack frame pointer is set to 0, meaning there is no
  // previous frame.
  dbprintf('m', "ProcessFork-ProcessFork: stackframe = 0x%x\n", (int)stackframe);
  stackframe[PROCESS_STACK_PREV_FRAME] = 0;

  //----------------------------------------------------------------------
  // STUDENT: setup the PTBASE, PTBITS, and PTSIZE here on the current
  // stack frame.
  //----------------------------------------------------------------------

  // JSM added PTBASE, PTBITS, and PTSIZE on stack frame
  //////////////////////////////////////////////////////////
  stackframe[PROCESS_STACK_PTBASE] = (uint32)&(pcb->pagetable[0]);
  dbprintf('p', "ProcessFork-PTBASE: 0x%.8X\n\n", (uint32)&(pcb->pagetable[0]));
  stackframe[PROCESS_STACK_PTSIZE] = MEM_L1_PAGE_TABLE_SIZE;
  dbprintf('p', "ProcessFork-PTSIZE: 0x%.8X\n\n", MEM_L1_PAGE_TABLE_SIZE);
  stackframe[PROCESS_STACK_PTBITS] = (MEM_L2FIELD_FIRST_BITNUM << 16) | MEM_L1FIELD_FIRST_BITNUM;
  dbprintf('p', "ProcessFork-PTBITS: 0x%.8X\n\n", (MEM_L2FIELD_FIRST_BITNUM << 16) | MEM_L1FIELD_FIRST_BITNUM);

  //////////////////////////////////////////////////////////

  if (isUser) {//user prog .dlx.obj
    dbprintf ('p', "ProcessFork-About to load %s\n", name);
    fd = ProcessGetCodeInfo (name, &start, &codeS, &codeL, &dataS, &dataL);
    if (fd < 0) {
      // Free newpage and pcb so we don't run out...
      ProcessFreeResources (pcb);
      return (-1);
    }

    dbprintf ('p', "ProcessFork-File %s -> start=0x%08x\n", name, start);
    dbprintf ('p', "ProcessFork-File %s -> code @ 0x%08x (size=0x%08x)\n", name, codeS,
	      codeL);
    dbprintf ('p', "ProcessFork-File %s -> data @ 0x%08x (size=0x%08x)\n", name, dataS,
	      dataL);

    while ((n = ProcessGetFromFile (fd, buf, &addr, sizeof (buf))) > 0) {
      dbprintf ('p', "ProcessFork-Placing %d bytes at vaddr %08x.\n", n, addr - n);
      // Copy the data to user memory.  Note that the user memory needs to
      // have enough space so that this copy will succeed!
      MemoryCopySystemToUser (pcb, buf, (char *)(addr - n), n);
    }
    FsClose (fd);
    stackframe[PROCESS_STACK_ISR] = PROCESS_INIT_ISR_USER;

    //----------------------------------------------------------------------
    // STUDENT: setup the initial user stack pointer here as the top
    // of the process's virtual address space (4-byte aligned).
    //----------------------------------------------------------------------

    // JSM initialized user stack pointer
    //////////////////////////////////////////////////////////
    stackframe[PROCESS_STACK_USER_STACKPOINTER] = (MEM_MAX_VIRTUAL_ADDRESS-3);
    dbprintf('p', "ProcessFork-USER_STACKPOINTER: 0x%.8X\n\n", stackframe[PROCESS_STACK_USER_STACKPOINTER]);
    //////////////////////////////////////////////////////////

    //--------------------------------------------------------------------
    // This part is setting up the initial user stack with argc and argv.
    //--------------------------------------------------------------------

    // Copy the entire set of strings of command line parameters onto the user stack.
    // The "param" variable is a pointer to the start of a sequenial set of strings,
    // each ending with its own '\0' character.  The final "string" of the sequence
    // must be an empty string to indicate that the sequence is done.  Since we
    // can't figure out how long the set of strings actually is in this scenario,
    // we have to copy the maximum possible string length and parse things manually.
    stackframe[PROCESS_STACK_USER_STACKPOINTER] -= SIZE_ARG_BUFF;
    MemoryCopySystemToUser (pcb, (char *)param, (char *)stackframe[PROCESS_STACK_USER_STACKPOINTER], SIZE_ARG_BUFF);

    // Now that the main string is copied into the user space, we need to setup
    // argv as an array of pointers into that string, and argc as the total
    // number of arguments found in the string.  The first call to get_argument
    // should return 0 as the offset of the first string.
    offset = get_argument((char *)param);
   
    // Compute the addresses in user space of where each string for the command line arguments
    // begins.  These addresses make up the argv array.
    for(argc=0; argc < MAX_ARGS; argc++) {
      // The "+2" is because initial_user_params[0] is argc, and initial_user_params[1] is argv.
      // The address can be found as the current stack pointer (which points to the start of
      // the params list) plus the byte offset of the parameter from the beginning of
      // the list of parameters.
      initial_user_params[argc+2] = stackframe[PROCESS_STACK_USER_STACKPOINTER] + offset;
      offset = get_argument(NULL);
      if (offset == 0) {
        initial_user_params[argc+2+1] = 0; // last entry should be a null value
        break;
      }
    }
    // argc is currently the index of the last command line argument.  We need it to instead
    // be the number of command line arguments, so we increment it by 1.
    argc++;

    // Now argc can be stored properly
    initial_user_params[0] = argc;

    // Compute where initial_user_params[3] will be copied in user space as the 
    // base of the array of string addresses.  The entire initial_user_params array
    // of uint32's will be copied onto the stack.  We'll move the stack pointer by
    // the necessary amount, then start copying the array.  Therefore, initial_user_params[3]
    // will reside at the current stack pointer value minus the number of command line
    // arguments (argc).
    initial_user_params[1] = stackframe[PROCESS_STACK_USER_STACKPOINTER] - (argc*sizeof(uint32));

    // Now copy the actual memory.  Remember that stacks grow down from the top of memory, so 
    // we need to move the stack pointer first, then do the copy.  The "+2", as before, is 
    // because initial_user_params[0] is argc, and initial_user_params[1] is argv.
    initial_user_params_bytes = (argc + 2) * sizeof(uint32);

    stackframe[PROCESS_STACK_USER_STACKPOINTER] -= initial_user_params_bytes;
    MemoryCopySystemToUser (pcb, (char *)initial_user_params, (char *)(stackframe[PROCESS_STACK_USER_STACKPOINTER]), initial_user_params_bytes);

    // Set the correct address at which to execute a user process.
    stackframe[PROCESS_STACK_IAR] = (uint32)start;

    // Flag this as a user process
    pcb->flags |= PROCESS_TYPE_USER;
  } else {
    // Don't worry about messing with any code here for kernel processes because
    // there aren't any kernel processes in DLXOS.

    // Set r31 to ProcessExit().  This will only be called for a system
    // process; user processes do an exit() trap.
    stackframe[PROCESS_STACK_IREG+31] = (uint32)ProcessExit;

    // Set the stack register to the base of the system stack.
    //stackframe[PROCESS_STACK_IREG+29]=pcb->sysStackArea + MEM_PAGESIZE;

    // Set the initial parameter properly by placing it on the stack frame
    // at the location pointed to by the "saved" stack pointer (r29).
    *((uint32 *)(stackframe[PROCESS_STACK_IREG+29])) = param;

    // Set up the initial address at which to execute.  This is done by
    // placing the address into the IAR slot of the stack frame.
    stackframe[PROCESS_STACK_IAR] = (uint32)func;

    // Set the initial value for the interrupt status register
    stackframe[PROCESS_STACK_ISR] = PROCESS_INIT_ISR_SYS;

    // Mark this as a system process.
    pcb->flags |= PROCESS_TYPE_SYSTEM;
  }

  // Place the PCB onto the run queue.
  intrs = DisableIntrs ();
  if ((pcb->l = AQueueAllocLink(pcb)) == NULL) {
    printf("FATAL ERROR: could not get link for forked PCB in ProcessFork!\n");
    exitsim();
  }
  if (AQueueInsertLast(&runQueue, pcb->l) != QUEUE_SUCCESS) {
    printf("FATAL ERROR: could not insert link into runQueue in ProcessFork!\n");
    exitsim();
  }
  RestoreIntrs (intrs);

  // If this is the first process, make it the current one
  if (currentPCB == NULL) {
    dbprintf ('p', "Setting currentPCB=0x%x, stackframe=0x%x\n",
	      (int)pcb, (int)(pcb->currentSavedFrame));
    currentPCB = pcb;
  }

  dbprintf ('p', "Leaving ProcessFork (%s)\n", name);
  // Return the process number (found by subtracting the PCB number
  // from the base of the PCB array).
  return (pcb - pcbs);
}
//-------------------------------------------------------
//
// int MboxSend(mbox_t handle,int length, void* message);
//
// Send a message (pointed to by "message") of length
// "length" bytes to the specified mailbox.  Messages of
// length 0 are allowed.  The call 
// blocks when there is not enough space in the mailbox.
// Messages cannot be longer than MBOX_MAX_MESSAGE_LENGTH.
// Note that the calling process must have opened the 
// mailbox via MboxOpen.
//
// Returns MBOX_FAIL on failure.
// Returns MBOX_SUCCESS on success.
//
//-------------------------------------------------------
int MboxSend(mbox_t handle, int length, void* message) {

	int intrs;
	int wasEmpty = 0;
	Link * mbuffer_link;

	if(MailBox[handle].inuse == 0)
	{
		printf("Currently passed mailbox handle : %d by calling process : %d is unallocated\n", handle, GetCurrentPid());
		return MBOX_FAIL;
	}

	if(MailBox[handle].procs_link[GetCurrentPid()] == false)
	{
		printf("Mailbox : %d not already opened by calling proces :%d\n", handle, GetCurrentPid());
		return MBOX_FAIL;
	}

	intrs = DisableIntrs();

	LockHandleAcquire(MailBox[handle].lock);

	while(AQueueLength(&MailBox[handle].buffers) == MBOX_MAX_BUFFERS_PER_MBOX || used_buffers == MBOX_NUM_BUFFERS)
	{
		CondHandleWait(MailBox[handle].moreSpace);
	}

	//printf("Buffer size of queue before inserting messager by : %d is : %d\n", GetCurrentPid(), AQueueLength(&MailBox[handle].buffers));

	if(AQueueLength(&MailBox[handle].buffers) == 0)
		wasEmpty = 1;

	if(AQueueLength(&MailBox[handle].buffers) != MBOX_MAX_BUFFERS_PER_MBOX)
	{
		if(length > MBOX_MAX_MESSAGE_LENGTH)
		{
			printf("Messge passed by user process : %d larger than accepted message length (Messge length sent - %d)", GetCurrentPid(), length);
			return MBOX_FAIL;
		}
	
		bcopy(message, Messg_Buffers[used_buffers++].message, 8); 
		
		Messg_Buffers[used_buffers - 1].size = length;

		//printf("Original Message : %s Copied  : %s in : %d\n", (char *)message, (char *)(Messg_Buffers[used_buffers - 1].message), handle);

		if((mbuffer_link = AQueueAllocLink(&Messg_Buffers[used_buffers - 1])) == QUEUE_FAIL)
		{
			printf("FATAL Error : Link object could not be created for message buffer : %d in process : %d",used_buffers - 1, GetCurrentPid());
			exitsim();
		}

		if(AQueueInsertLast(&MailBox[handle].buffers, mbuffer_link) != QUEUE_SUCCESS)
		{
			printf("FATAL Error : Link object could not be created for message buffer : %d in process : %d",used_buffers - 1, GetCurrentPid());
			exitsim();	
		}
	}

	//printf("Message inserted by process : %d using buffer : %d with current count : %d\n", GetCurrentPid(), buffer_no, AQueueLength(&MailBox[handle].buffers));

	//if(wasEmpty)
	CondHandleSignal(MailBox[handle].moreData);

	LockHandleRelease(MailBox[handle].lock);

	RestoreIntrs(intrs);

  return MBOX_SUCCESS;
}
//-------------------------------------------------------
//
// int MboxRecv(mbox_t handle, int maxlength, void* message);
//
// Receive a message from the specified mailbox.  The call 
// blocks when there is no message in the buffer.  Maxlength
// should indicate the maximum number of bytes that can be
// copied from the buffer into the address of "message".  
// An error occurs if the message is larger than maxlength.
// Note that the calling process must have opened the mailbox 
// via MboxOpen.
//   
// Returns MBOX_FAIL on failure.
// Returns number of bytes written into message on success.
//
//-------------------------------------------------------
int MboxRecv(mbox_t handle, int maxlength, void* message) {
  	
	int intrs;
	int wasFull = 0, buffersSaturated = 0;
	mbox_message * user_mesg;
	Link *l;
	
	if(MailBox[handle].inuse == false)
	{
		printf("Currently passed mailbox handle : %d by calling process : %d is unallocated\n", handle, GetCurrentPid());
		return MBOX_FAIL;
	}

	if(MailBox[handle].procs_link[GetCurrentPid()] == false)
	{
		printf("Mailbox : %d not already opened by calling proces :%d\n", handle, GetCurrentPid());
		return MBOX_FAIL;
	}

	intrs = DisableIntrs();

	LockHandleAcquire(MailBox[handle].lock);

	while(AQueueLength(&MailBox[handle].buffers) == 0)
	{
		//printf("Waiting for mailbox to have more messages\n");
		CondHandleWait(MailBox[handle].moreData);
	}

	//printf("Received mailbox lock by calling process : %d with mailbox buffer count : %d\n", GetCurrentPid(), AQueueLength(&MailBox[handle].buffers));

	if(AQueueLength(&MailBox[handle].buffers) == MBOX_MAX_BUFFERS_PER_MBOX)
		wasFull = 1;

	if(AQueueLength(&MailBox[handle].buffers) < MBOX_MAX_BUFFERS_PER_MBOX && used_buffers == MBOX_NUM_BUFFERS)
		buffersSaturated = 1;

	//printf("Obtained message in receiver : %d from buffer index : %d\n", user_mesg->size, (user_mesg - Messg_Buffers));

	if((user_mesg = (mbox_message *)AQueueObject(MailBox[handle].buffers.first)) == NULL)
	{
		printf("Undefined/unallocated Link pointer obtained from mailbox queue with handle :%d in process : %d\n", handle, GetCurrentPid());
		//printf("MailBox buffer size : %d\n", AQueueLength(&MailBox[handle].buffers));
		return MBOX_FAIL;
	}
		
	if(maxlength < user_mesg->size)
	{
		printf("Message : %s requested by user process : %d larger than acceptable message length : %d (Messge length received : %d)\n", user_mesg->message, GetCurrentPid(), maxlength, user_mesg->size);
		return MBOX_FAIL;
	}

	bcopy(user_mesg->message, message, user_mesg->size); 

	l = MailBox[handle].buffers.first;
	MailBox[handle].buffers.first = AQueueNext(MailBox[handle].buffers.first);
	if(AQueueRemove(&l) == QUEUE_FAIL)
	{
		printf("FATAL Error : Message object Link for buffer : %d received by process : %d could not be removed from queue of mailbox handle : %d",used_buffers, GetCurrentPid(), handle);
		exitsim();
	}

	used_buffers--;

	//if(wasFull || buffersSaturated)
	CondHandleSignal(MailBox[handle].moreSpace);

	LockHandleRelease(MailBox[handle].lock);

	RestoreIntrs(intrs);

  return user_mesg;
}
Beispiel #26
0
//----------------------------------------------------------------------
//
//	ProcessFork
//
//	Create a new process and make it runnable.  This involves the
//	following steps:
//	* Allocate resources for the process (PCB, memory, etc.)
//	* Initialize the resources
//	* Place the PCB on the runnable queue
//
//	NOTE: This code has been tested for system processes, but not
//	for user processes.
//
//----------------------------------------------------------------------
int
ProcessFork (VoidFunc func, uint32 param, int p_nice, int p_info,char *name, int isUser)
{
  int		i, j, fd, n;
  Link		*l;
  int		start, codeS, codeL, dataS, dataL;
  uint32	*stackframe;
  int		newPage;
  PCB		*pcb;
  int	addr = 0;
  int		intrs;
  unsigned char buf[100];
  uint32 dum[MAX_ARGS+8], count, offset;
  char *str;


  intrs = DisableIntrs ();
  dbprintf ('I', "Old interrupt value was 0x%x.\n", intrs);
  dbprintf ('p', "Entering ProcessFork args=0x%x 0x%x %s %d\n", func,
	    param, name, isUser);
  // Get a free PCB for the new process
  if (QueueEmpty (&freepcbs)) {
    printf ("FATAL error: no free processes!\n");
    exitsim ();	// NEVER RETURNS!
  }
  l = QueueFirst (&freepcbs);
  dbprintf ('p', "Got a link @ 0x%x\n", l);
  QueueRemove (l);
  pcb = (PCB *)(l->object);
  // This prevents someone else from grabbing this process
  ProcessSetStatus (pcb, PROCESS_STATUS_RUNNABLE);

  // At this point, the PCB is allocated and nobody else can get it.
  // However, it's not in the run queue, so it won't be run.  Thus, we
  // can turn on interrupts here.
  dbprintf ('I', "Before restore interrupt value is 0x%x.\n", CurrentIntrs());
  RestoreIntrs (intrs);
  dbprintf ('I', "New interrupt value is 0x%x.\n", CurrentIntrs());

  // Copy the process name into the PCB.
  dstrcpy (pcb->name, name);

  //----------------------------------------------------------------------
  // This section initializes the memory for this process
  //----------------------------------------------------------------------
  // For now, we'll use one user page and a page for the system stack.
  // For system processes, though, all pages must be contiguous.
  // Of course, system processes probably need just a single page for
  // their stack, and don't need any code or data pages allocated for them.
  pcb->npages = 1;
  newPage = MemoryAllocPage ();
  if (newPage == 0) {
    printf ("aFATAL: couldn't allocate memory - no free pages!\n");
    exitsim ();	// NEVER RETURNS!
  }
  pcb->pagetable[0] = MemorySetupPte (newPage);
  newPage = MemoryAllocPage ();
  if (newPage == 0) {
    printf ("bFATAL: couldn't allocate system stack - no free pages!\n");
    exitsim ();	// NEVER RETURNS!
  }
  pcb->sysStackArea = newPage * MEMORY_PAGE_SIZE;


  //---------------------------------------
  // Lab3: initialized pcb member for your scheduling algorithm here
  //--------------------------------------
  pcb->p_nice = p_nice < 0 ? 0 : p_nice;
  pcb->p_info = p_info;
  pcb->sleeptime = my_timer_get();
  pcb->estcpu = 0;
  pcb->prio = PUSER;
  pcb->processed = 1 - processedFlag;
  pcb->estcputime = 0;
  //----------------------------------------------------------------------
  // Stacks grow down from the top.  The current system stack pointer has
  // to be set to the bottom of the interrupt stack frame, which is at the
  // high end (address-wise) of the system stack.
  stackframe = ((uint32 *)(pcb->sysStackArea + MEMORY_PAGE_SIZE)) -
    (PROCESS_STACK_FRAME_SIZE + 8);
  // The system stack pointer is set to the base of the current interrupt
  // stack frame.
  pcb->sysStackPtr = stackframe;
  // The current stack frame pointer is set to the same thing.
  pcb->currentSavedFrame = stackframe;

  dbprintf ('p',
	    "Setting up PCB @ 0x%x (sys stack=0x%x, mem=0x%x, size=0x%x)\n",
	    pcb, pcb->sysStackArea, pcb->pagetable[0],
	    pcb->npages * MEMORY_PAGE_SIZE);

  //----------------------------------------------------------------------
  // This section sets up the stack frame for the process.  This is done
  // so that the frame looks to the interrupt handler like the process
  // was "suspended" right before it began execution.  The standard
  // mechanism of swapping in the registers and returning to the place
  // where it was "interrupted" will then work.
  //----------------------------------------------------------------------

  // The previous stack frame pointer is set to 0, meaning there is no
  // previous frame.
  stackframe[PROCESS_STACK_PREV_FRAME] = 0;

  // Set the base of the level 1 page table.  If there's only one page
  // table level, this is it.  For 2-level page tables, put the address
  // of the level 1 page table here.  For 2-level page tables, we'll also
  // have to build up the necessary tables....
  stackframe[PROCESS_STACK_PTBASE] = (uint32)&(pcb->pagetable[0]);

  // Set the size (maximum number of entries) of the level 1 page table.
  // In our case, it's just one page, but it could be larger.
  stackframe[PROCESS_STACK_PTSIZE] = pcb->npages;

  // Set the number of bits for both the level 1 and level 2 page tables.
  // This can be changed on a per-process basis if desired.  For now,
  // though, it's fixed.
  stackframe[PROCESS_STACK_PTBITS] = (MEMORY_L1_PAGE_SIZE_BITS
					  + (MEMORY_L2_PAGE_SIZE_BITS << 16));


  if (isUser) {
    dbprintf ('p', "About to load %s\n", name);
    fd = ProcessGetCodeInfo (name, &start, &codeS, &codeL, &dataS, &dataL);
    if (fd < 0) {
      // Free newpage and pcb so we don't run out...
      ProcessFreeResources (pcb);
      return (-1);
    }
    dbprintf ('p', "File %s -> start=0x%08x\n", name, start);
    dbprintf ('p', "File %s -> code @ 0x%08x (size=0x%08x)\n", name, codeS,
	      codeL);
    dbprintf ('p', "File %s -> data @ 0x%08x (size=0x%08x)\n", name, dataS,
	      dataL);
    while ((n = ProcessGetFromFile (fd, buf, &addr, sizeof (buf))) > 0) {
      dbprintf ('p', "Placing %d bytes at vaddr %08x.\n", n, addr - n);
      // Copy the data to user memory.  Note that the user memory needs to
      // have enough space so that this copy will succeed!
      MemoryCopySystemToUser (pcb, buf, addr - n, n);
    }
    FsClose (fd);
    stackframe[PROCESS_STACK_ISR] = PROCESS_INIT_ISR_USER;
    // Set the initial stack pointer correctly.  Currently, it's just set
    // to the top of the (single) user address space allocated to this
    // process.
    str = (char *)param;
    stackframe[PROCESS_STACK_IREG+29] = MEMORY_PAGE_SIZE - SIZE_ARG_BUFF;
    // Copy the initial parameter to the top of stack

    MemoryCopySystemToUser (pcb, (char *)str,
			    (char *)stackframe[PROCESS_STACK_IREG+29],
			    SIZE_ARG_BUFF-32);

    offset = get_argument((char *)param);

    dum[2] = MEMORY_PAGE_SIZE - SIZE_ARG_BUFF + offset;
    for(count=3;;count++)
    {
      offset=get_argument(NULL);
      dum[count] = MEMORY_PAGE_SIZE - SIZE_ARG_BUFF + offset;
      if(offset==0)
      {
        break;
      }
    }
    dum[0] = count-2;
    dum[1] = MEMORY_PAGE_SIZE - SIZE_ARG_BUFF - (count-2)*4;
    MemoryCopySystemToUser (pcb, (char *)dum,
			    (char *)(stackframe[PROCESS_STACK_IREG+29]-count*4),
			    (count)*sizeof(uint32));
    stackframe[PROCESS_STACK_IREG+29] -= 4*count;
    // Set the correct address at which to execute a user process.
    stackframe[PROCESS_STACK_IAR] = (uint32)start;
    pcb->flags |= PROCESS_TYPE_USER;
  } else {
    // Set r31 to ProcessExit().  This will only be called for a system
    // process; user processes do an exit() trap.
    stackframe[PROCESS_STACK_IREG+31] = (uint32)ProcessExit;

    // Set the stack register to the base of the system stack.
    stackframe[PROCESS_STACK_IREG+29]=pcb->sysStackArea + MEMORY_PAGE_SIZE-32;

    // Set the initial parameter properly by placing it on the stack frame
    // at the location pointed to by the "saved" stack pointer (r29).
    *((uint32 *)(stackframe[PROCESS_STACK_IREG+29])) = param;

    // Set up the initial address at which to execute.  This is done by
    // placing the address into the IAR slot of the stack frame.
    stackframe[PROCESS_STACK_IAR] = (uint32)func;

    // Set the initial value for the interrupt status register
    stackframe[PROCESS_STACK_ISR] = PROCESS_INIT_ISR_SYS;

    // Mark this as a system process.
    pcb->flags |= PROCESS_TYPE_SYSTEM;
  }

  // Place the PCB onto the run queue.
  intrs = DisableIntrs ();
  QueueInsertLast (&runQueue[pcb->prio/4], l);
  RestoreIntrs (intrs);

  // If this is the first process, make it the current one
  if (currentPCB == NULL) {
    dbprintf ('p', "Setting currentPCB=0x%x, stackframe=0x%x\n",
	      pcb, pcb->currentSavedFrame);
    currentPCB = pcb;
  }
  dbprintf ('p', "Leaving ProcessFork (%s)\n", name);
  // Return the process number (found by subtracting the PCB number
  // from the base of the PCB array).
  return (pcb - pcbs);
}
Beispiel #27
0
//----------------------------------------------------------------------
//
//	ProcessFork
//
//	Create a new process and make it runnable.  This involves the
//	following steps:
//	* Allocate resources for the process (PCB, memory, etc.)
//	* Initialize the resources
//	* Place the PCB on the runnable queue
//
//	NOTE: This code has been tested for system processes, but not
//	for user processes.
//
//----------------------------------------------------------------------
int ProcessFork (VoidFunc func, uint32 param, char *name, int isUser) {
  int i;                   // Loop index variable
  int fd, n;               // Used for reading code from files.
  int start, codeS, codeL; // Used for reading code from files.
  int dataS, dataL;        // Used for reading code from files.
  int addr = 0;            // Used for reading code from files.
  unsigned char buf[100];  // Used for reading code from files.
  uint32 *stackframe;      // Stores address of current stack frame.
  PCB *pcb;                // Holds pcb while we build it for this process.
  int intrs;               // Stores previous interrupt settings.
  uint32  initial_user_params[MAX_ARGS+2]; // Initial memory for user parameters (argc, argv)
                                           // initial_user_params[0] = argc
                                           // initial_user_params[1] = argv, points to initial_user_params[2]
                                           // initial_user_params[2] = address of string for argv[0]
                                           // initial_user_params[3] = address of string for argv[1]
                                           //                           ...
  uint32 argc=0;           // running counter for number of arguments
  uint32 offset;           // Used in parsing command line argument strings, holds offset (in bytes) from
                           // beginning of the string to the current argument.
  uint32 initial_user_params_bytes;  // total number of bytes in initial user parameters array
  int newPage;


  intrs = DisableIntrs ();
  dbprintf ('I', "Old interrupt value was 0x%x.\n", intrs);
  dbprintf ('p', "Entering ProcessFork args=0x%x 0x%x %s %d\n", (int)func,
	    param, name, isUser);
  // Get a free PCB for the new process
  if (AQueueEmpty(&freepcbs)) {
    printf ("FATAL error: no free processes!\n");
    exitsim ();	// NEVER RETURNS!
  }
  pcb = (PCB *)AQueueObject(AQueueFirst (&freepcbs));
  dbprintf ('p', "Got a link @ 0x%x\n", (int)(pcb->l));
  if (AQueueRemove (&(pcb->l)) != QUEUE_SUCCESS) {
    printf("FATAL ERROR: could not remove link from freepcbsQueue in ProcessFork!\n");
    exitsim();
  }
  // This prevents someone else from grabbing this process
  ProcessSetStatus (pcb, PROCESS_STATUS_RUNNABLE);

  // At this point, the PCB is allocated and nobody else can get it.
  // However, it's not in the run queue, so it won't be run.  Thus, we
  // can turn on interrupts here.
  RestoreIntrs (intrs);

  // Copy the process name into the PCB.
  dstrcpy(pcb->name, name);

  //----------------------------------------------------------------------
  // This section initializes the memory for this process
  //----------------------------------------------------------------------
  // Allocate 1 page for system stack, 1 page for user stack (at top of
  // virtual address space), and 4 pages for user code and global data.
  //---------------------------------------------------------
  // STUDENT: allocate pages for a new process here.  The
  // code below assumes that you set the "stackframe" variable
  // equal to the last 4-byte-aligned address in physical page
  // for the system stack.
  //---------------------------------------------------------
  // Pages for code and global data and Heap
  pcb->npages = 5;
  for(i = 0; i < pcb->npages; i++) {
    newPage = MemoryAllocPage();
    if(newPage == MEM_FAIL) {
      printf ("FATAL: couldn't allocate memory - no free pages!\n");
      ProcessFreeResources (pcb);
      return PROCESS_FORK_FAIL;
    }
    pcb->pagetable[i] = MemorySetupPte (newPage);
  }
  // Initialize nodes in pool
  for (i = 1; i <= MEM_HEAP_MAX_NODES; i++) {
    pcb->htree_array[i].parent = NULL;
    pcb->htree_array[i].cleft = NULL;
    pcb->htree_array[i].crght = NULL;
    pcb->htree_array[i].index = i;
    pcb->htree_array[i].size = -1;
    pcb->htree_array[i].addr = -1;
    pcb->htree_array[i].inuse = 0;
    pcb->htree_array[i].order = -1;
  }
  // Initialize Heap tree
  pcb->htree_array[1].size = MEM_PAGESIZE;
  pcb->htree_array[1].addr = 0;
  pcb->htree_array[1].order = 7;
  // user stack
  pcb->npages += 1;
  newPage = MemoryAllocPage();
  if(newPage == MEM_FAIL) {
    printf ("FATAL: couldn't allocate user stack - no free pages!\n");
    ProcessFreeResources (pcb);
    return PROCESS_FORK_FAIL;
  }
  pcb->pagetable[MEM_ADDRESS_TO_PAGE(MEM_MAX_VIRTUAL_ADDRESS)] = MemorySetupPte (newPage);

  // for system stack
  newPage = MemoryAllocPage ();
  if(newPage == MEM_FAIL) {
    printf ("FATAL: couldn't allocate system stack - no free pages!\n");
    ProcessFreeResources (pcb);
    return PROCESS_FORK_FAIL;
  }

  pcb->sysStackArea = newPage * MEM_PAGESIZE;
  //----------------------------------------------------------------------
  // Stacks grow down from the top.  The current system stack pointer has
  // to be set to the bottom of the interrupt stack frame, which is at the
  // high end (address-wise) of the system stack.
  stackframe = (uint32 *)(pcb->sysStackArea + MEM_PAGESIZE - 4);
  dbprintf('p', "ProcessFork: SystemStack page=%d sysstackarea=0x%x\n", newPage, pcb->sysStackArea);

  // Now that the stack frame points at the bottom of the system stack memory area, we need to
  // move it up (decrement it) by one stack frame size because we're about to fill in the
  // initial stack frame that will be loaded for this PCB when it gets switched in by
  // ProcessSchedule the first time.
  stackframe -= PROCESS_STACK_FRAME_SIZE;

  // The system stack pointer is set to the base of the current interrupt stack frame.
  pcb->sysStackPtr = stackframe;
  // The current stack frame pointer is set to the same thing.
  pcb->currentSavedFrame = stackframe;

  dbprintf ('p', "Setting up PCB @ 0x%x (sys stack=0x%x, mem=0x%x, size=0x%x)\n",
    (int)pcb, pcb->sysStackArea, pcb->pagetable[0], pcb->npages * MEM_PAGESIZE);

  //----------------------------------------------------------------------
  // This section sets up the stack frame for the process.  This is done
  // so that the frame looks to the interrupt handler like the process
  // was "suspended" right before it began execution.  The standard
  // mechanism of swapping in the registers and returning to the place
  // where it was "interrupted" will then work.
  //----------------------------------------------------------------------

  // The previous stack frame pointer is set to 0, meaning there is no
  // previous frame.
  dbprintf('p', "ProcessFork: stackframe = 0x%x\n", (int)stackframe);
  stackframe[PROCESS_STACK_PREV_FRAME] = 0;

  //----------------------------------------------------------------------
  // STUDENT: setup the PTBASE, PTBITS, and PTSIZE here on the current
  // stack frame.
  //----------------------------------------------------------------------
  // Set the base of the level 1 page table.  If there's only one page
  // table level, this is it.  For 2-level page tables, put the address
  // of the level 1 page table here.  For 2-level page tables, we'll also
  // have to build up the necessary tables....
  stackframe[PROCESS_STACK_PTBASE] = (uint32)(&(pcb->pagetable[0]));

  // Set the size (maximum number of entries) of the level 1 page table.
  // In our case, it's just one page, but it could be larger.
  stackframe[PROCESS_STACK_PTSIZE] = MEM_PAGE_TBL_SIZE;

  // Set the number of bits for both the level 1 and level 2 page tables.
  // This can be changed on a per-process basis if desired.  For now,
  // though, it's fixed.
  stackframe[PROCESS_STACK_PTBITS] = (MEM_L1FIELD_FIRST_BITNUM << 16) + MEM_L1FIELD_FIRST_BITNUM;

  if (isUser) {
    dbprintf ('p', "About to load %s\n", name);
    fd = ProcessGetCodeInfo (name, &start, &codeS, &codeL, &dataS, &dataL);
    if (fd < 0) {
      // Free newPage and pcb so we don't run out...
      ProcessFreeResources (pcb);
      return (-1);
    }

    dbprintf ('p', "File %s -> start=0x%08x\n", name, start);
    dbprintf ('p', "File %s -> code @ 0x%08x (size=0x%08x)\n", name, codeS,
	      codeL);
    dbprintf ('p', "File %s -> data @ 0x%08x (size=0x%08x)\n", name, dataS,
	      dataL);

    while ((n = ProcessGetFromFile (fd, buf, &addr, sizeof (buf))) > 0) {
      dbprintf ('i', "Placing %d bytes at vaddr %08x.\n", n, addr - n);
      // Copy the data to user memory.  Note that the user memory needs to
      // have enough space so that this copy will succeed!
      MemoryCopySystemToUser (pcb, buf, (char *)(addr - n), n);
    }
    FsClose (fd);
    stackframe[PROCESS_STACK_ISR] = PROCESS_INIT_ISR_USER;

    //----------------------------------------------------------------------
    // STUDENT: setup the initial user stack pointer here as the top
    // of the process's virtual address space (4-byte aligned).
    //----------------------------------------------------------------------
    stackframe[PROCESS_STACK_USER_STACKPOINTER] = MEM_MAX_VIRTUAL_ADDRESS - 3;
    dbprintf('p', "ProcessFork: UserStack usrsp=0x%x\n", stackframe[PROCESS_STACK_USER_STACKPOINTER]);

    //--------------------------------------------------------------------
    // This part is setting up the initial user stack with argc and argv.
    //--------------------------------------------------------------------

    // Copy the entire set of strings of command line parameters onto the user stack.
    // The "param" variable is a pointer to the start of a sequenial set of strings,
    // each ending with its own '\0' character.  The final "string" of the sequence
    // must be an empty string to indicate that the sequence is done.  Since we
    // can't figure out how long the set of strings actually is in this scenario,
    // we have to copy the maximum possible string length and parse things manually.
    stackframe[PROCESS_STACK_USER_STACKPOINTER] -= SIZE_ARG_BUFF;
    MemoryCopySystemToUser (pcb, (char *)param, (char *)stackframe[PROCESS_STACK_USER_STACKPOINTER], SIZE_ARG_BUFF);

    // Now that the main string is copied into the user space, we need to setup
    // argv as an array of pointers into that string, and argc as the total
    // number of arguments found in the string.  The first call to get_argument
    // should return 0 as the offset of the first string.
    offset = get_argument((char *)param);

    // Compute the addresses in user space of where each string for the command line arguments
    // begins.  These addresses make up the argv array.
    for(argc=0; argc < MAX_ARGS; argc++) {
      // The "+2" is because initial_user_params[0] is argc, and initial_user_params[1] is argv.
      // The address can be found as the current stack pointer (which points to the start of
      // the params list) plus the byte offset of the parameter from the beginning of
      // the list of parameters.
      initial_user_params[argc+2] = stackframe[PROCESS_STACK_USER_STACKPOINTER] + offset;
      offset = get_argument(NULL);
      if (offset == 0) {
        initial_user_params[argc+2+1] = 0; // last entry should be a null value
        break;
      }
    }
    // argc is currently the index of the last command line argument.  We need it to instead
    // be the number of command line arguments, so we increment it by 1.
    argc++;

    // Now argc can be stored properly
    initial_user_params[0] = argc;

    // Compute where initial_user_params[3] will be copied in user space as the
    // base of the array of string addresses.  The entire initial_user_params array
    // of uint32's will be copied onto the stack.  We'll move the stack pointer by
    // the necessary amount, then start copying the array.  Therefore, initial_user_params[3]
    // will reside at the current stack pointer value minus the number of command line
    // arguments (argc).
    initial_user_params[1] = stackframe[PROCESS_STACK_USER_STACKPOINTER] - (argc*sizeof(uint32));

    // Now copy the actual memory.  Remember that stacks grow down from the top of memory, so
    // we need to move the stack pointer first, then do the copy.  The "+2", as before, is
    // because initial_user_params[0] is argc, and initial_user_params[1] is argv.
    initial_user_params_bytes = (argc + 2) * sizeof(uint32);

    stackframe[PROCESS_STACK_USER_STACKPOINTER] -= initial_user_params_bytes;
    MemoryCopySystemToUser (pcb, (char *)initial_user_params, (char *)(stackframe[PROCESS_STACK_USER_STACKPOINTER]), initial_user_params_bytes);

    // Set the correct address at which to execute a user process.
    stackframe[PROCESS_STACK_IAR] = (uint32)start;

    // Flag this as a user process
    pcb->flags |= PROCESS_TYPE_USER;
  } else {
    // Don't worry about messing with any code here for kernel processes because
    // there aren't any kernel processes in DLXOS.

    // Set r31 to ProcessExit().  This will only be called for a system
    // process; user processes do an exit() trap.
    stackframe[PROCESS_STACK_IREG+31] = (uint32)ProcessExit;

    // Set the stack register to the base of the system stack.
    //stackframe[PROCESS_STACK_IREG+29]=pcb->sysStackArea + MEM_PAGESIZE;

    // Set the initial parameter properly by placing it on the stack frame
    // at the location pointed to by the "saved" stack pointer (r29).
    *((uint32 *)(stackframe[PROCESS_STACK_IREG+29])) = param;

    // Set up the initial address at which to execute.  This is done by
    // placing the address into the IAR slot of the stack frame.
    stackframe[PROCESS_STACK_IAR] = (uint32)func;

    // Set the initial value for the interrupt status register
    stackframe[PROCESS_STACK_ISR] = PROCESS_INIT_ISR_SYS;

    // Mark this as a system process.
    pcb->flags |= PROCESS_TYPE_SYSTEM;
  }

  // Place the PCB onto the run queue.
  intrs = DisableIntrs ();
  if ((pcb->l = AQueueAllocLink(pcb)) == NULL) {
    printf("FATAL ERROR: could not get link for forked PCB in ProcessFork!\n");
    exitsim();
  }
  if (AQueueInsertLast(&runQueue, pcb->l) != QUEUE_SUCCESS) {
    printf("FATAL ERROR: could not insert link into runQueue in ProcessFork!\n");
    exitsim();
  }
  RestoreIntrs (intrs);

  // If this is the first process, make it the current one
  if (currentPCB == NULL) {
    dbprintf ('p', "Setting currentPCB=0x%x, stackframe=0x%x\n",
	      (int)pcb, (int)(pcb->currentSavedFrame));
    currentPCB = pcb;
  }

  dbprintf ('p', "Leaving ProcessFork (%s)\n", name);
  // Return the process number (found by subtracting the PCB number
  // from the base of the PCB array).
  return (pcb - pcbs);
}
Beispiel #28
0
int ProcessRealFork (PCB* ppcb) {
  PCB *cpcb;                // Holds pcb while we build it for this process.
  int intrs;               // Stores previous interrupt settings.
  int newPage;
  int i;
  uint32 *stackframe;


  intrs = DisableIntrs ();
  dbprintf ('I', "Old interrupt value was 0x%x.\n", intrs);
  dbprintf ('p', "Entering ProcessRealFork ppcb=%d\n", GetPidFromAddress(ppcb));
  // Get a free PCB for the new process
  if (AQueueEmpty(&freepcbs)) {
    printf ("FATAL error: no free processes!\n");
    exitsim (); // NEVER RETURNS!
  }
  cpcb = (PCB *)AQueueObject(AQueueFirst (&freepcbs));
  dbprintf ('p', "Got a link @ 0x%x\n", (int)(cpcb->l));
  if (AQueueRemove (&(cpcb->l)) != QUEUE_SUCCESS) {
    printf("FATAL ERROR: could not remove link from freepcbsQueue in ProcessFork!\n");
    exitsim();
  }
  // This prevents someone else from grabbing this process
  ProcessSetStatus (cpcb, PROCESS_STATUS_RUNNABLE);
  
  // cpcb shares code and global data with ppcb
  for(i = 0; i < 4; i++) {
    ppcb->pagetable[i] |= MEM_PTE_READONLY;
    MemorySharePage(ppcb->pagetable[i]);
  }
  // user stack is also shared at first
  ppcb->pagetable[MEM_ADDRESS_TO_PAGE(MEM_MAX_VIRTUAL_ADDRESS)] |= MEM_PTE_READONLY; 
  MemorySharePage(ppcb->pagetable[MEM_ADDRESS_TO_PAGE(MEM_MAX_VIRTUAL_ADDRESS)]);

  // copy parent pcb to child pcb
  bcopy((char *)ppcb, (char *)cpcb, sizeof(PCB));
  // At this point, the PCB is allocated and nobody else can get it.
  // However, it's not in the run queue, so it won't be run.  Thus, we
  // can turn on interrupts here.
  RestoreIntrs (intrs);

  // for system stack
  newPage = MemoryAllocPage ();
  if(newPage == MEM_FAIL) {
    printf ("FATAL: couldn't allocate system stack - no free pages!\n");
    ProcessFreeResources (cpcb);
    return PROCESS_FORK_FAIL;
  }
  cpcb->sysStackArea = newPage * MEM_PAGESIZE;
  // copy system stack from ppcb to cpcb
  bcopy((char *)(ppcb->sysStackArea), (char *)(cpcb->sysStackArea), MEM_PAGESIZE);
  dbprintf('p', "ProcessRealFork: SystemStack page=%d sysstackarea=0x%x\n", newPage, cpcb->sysStackArea);
  // printf("ProcessRealFork: parent_sysstackarea=0x%x child_sysstackarea=0x%x\n", ppcb->sysStackArea, cpcb->sysStackArea);

  //----------------------------------------------------------------------
  // Stacks grow down from the top.  The current system stack pointer has
  // to be set to the bottom of the interrupt stack frame, which is at the
  // high end (address-wise) of the system stack.
  stackframe = (uint32 *)(cpcb->sysStackArea + MEM_PAGESIZE - 4);

  // Now that the stack frame points at the bottom of the system stack memory area, we need to
  // move it up (decrement it) by one stack frame size because we're about to fill in the
  // initial stack frame that will be loaded for this PCB when it gets switched in by
  // ProcessSchedule the first time.
  stackframe -= PROCESS_STACK_FRAME_SIZE;

  // The system stack pointer is set to the base of the current interrupt stack frame.
  cpcb->sysStackPtr = stackframe;
  // The current stack frame pointer is set to the same thing.
  cpcb->currentSavedFrame = stackframe;

  dbprintf ('p', "Setting up PCB @ 0x%x (sys stack=0x%x, mem=0x%x, size=0x%x)\n",
    (int)cpcb, cpcb->sysStackArea, cpcb->pagetable[0], cpcb->npages * MEM_PAGESIZE);

  //----------------------------------------------------------------------
  // STUDENT: setup the PTBASE, PTBITS, and PTSIZE here on the current
  // stack frame.
  //----------------------------------------------------------------------
  // Set the base of the level 1 page table.  If there's only one page
  // table level, this is it.  For 2-level page tables, put the address
  // of the level 1 page table here.  For 2-level page tables, we'll also
  // have to build up the necessary tables....
  stackframe[PROCESS_STACK_PTBASE] = (uint32)(&(cpcb->pagetable[0]));

  // Place the PCB onto the run queue.
  intrs = DisableIntrs ();
  if ((cpcb->l = AQueueAllocLink(cpcb)) == NULL) {
    printf("FATAL ERROR: could not get link for forked PCB in ProcessFork!\n");
    exitsim();
  }
  if (AQueueInsertLast(&runQueue, cpcb->l) != QUEUE_SUCCESS) {
    printf("FATAL ERROR: could not insert link into runQueue in ProcessFork!\n");
    exitsim();
  }
  RestoreIntrs (intrs);
  ProcessSetResult(cpcb, 0);
  ProcessSetResult(ppcb, GetPidFromAddress(cpcb));
  
  // TEST PRINTS
  printf("\nIn ProcessRealFork:\n");
  printf("----- Page table of parent process PID:%d -----\n", GetPidFromAddress(ppcb));
  ProcessForkTestPrints(ppcb);
  printf("\n----- Page table of child process PID: %d -----\n", GetPidFromAddress(cpcb));
  ProcessForkTestPrints(cpcb);

  dbprintf ('p', "Leaving ProcessRealFork cpcbid=%d\n", GetPidFromAddress(cpcb));
  return PROCESS_FORK_SUCCESS;
}