//--------------------------------------------------------------------------- // 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; }
//-------------------------------------------------------------------------------- // // 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; }
//------------------------------------------------------- // // 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; }
//------------------------------------------------------- // // 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; }
//------------------------------------------------------- // // 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; }
//---------------------------------------------------------------------- // // 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; }
//------------------------------------------------------- // // 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; }
//------------------------------------------------------- // // 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); }
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 (); }
//---------------------------------------------------------------------- // // 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 (); }
//---------------------------------------------------------------------- // // 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; }
//---------------------------------------------------------------------- // // 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); }
//---------------------------------------------------------------------- // // 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); }
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; }