//---------------------------------------------------------------------- // // ProcessFreeResources // // Free the resources associated with a process. This assumes the // process isn't currently on any queue. // //---------------------------------------------------------------------- void ProcessFreeResources (PCB *pcb) { int i = 0; dbprintf ('p', "ProcessFreeResources: function started\n"); // Allocate a new link for this pcb on the freepcbs queue if ((pcb->l = AQueueAllocLink(pcb)) == NULL) { printf("FATAL ERROR: could not get Queue Link in ProcessFreeResources!\n"); GracefulExit(); } // Set the pcb's status to available pcb->flags = PROCESS_STATUS_FREE; // Insert the link into the freepcbs queue if (AQueueInsertLast(&freepcbs, pcb->l) != QUEUE_SUCCESS) { printf("FATAL ERROR: could not insert PCB link into freepcbs queue in ProcessFreeResources!\n"); GracefulExit(); } // Free the process's memory. for (i = 0; i < pcb->npages; i++) { MemoryFreePte (pcb->pagetable[i]); } // Free the page allocated for the system stack MemoryFreePage (pcb->sysStackArea / MEMORY_PAGE_SIZE); ProcessSetStatus (pcb, PROCESS_STATUS_FREE); dbprintf ('p', "ProcessFreeResources: function complete\n"); }
//---------------------------------------------------------------------- // // ProcessModuleInit // // Initialize the process module. This involves initializing all // of the process control blocks to appropriate values (ie, free // and available). We also need to initialize all of the queues. // //---------------------------------------------------------------------- void ProcessModuleInit () { int i; dbprintf ('p', "ProcessModuleInit: function started\n"); AQueueInit (&freepcbs); AQueueInit(&runQueue); AQueueInit (&waitQueue); AQueueInit (&zombieQueue); // For each PCB slot in the global pcbs array: for (i = 0; i < PROCESS_MAX_PROCS; i++) { dbprintf ('p', "Initializing PCB %d @ 0x%x.\n", i, (int)&(pcbs[i])); // First, set the internal PCB link pointer to a newly allocated link if ((pcbs[i].l = AQueueAllocLink(&pcbs[i])) == NULL) { printf("FATAL ERROR: could not allocate link in ProcessModuleInit!\n"); GracefulExit(); } // Next, set the pcb to be available pcbs[i].flags = PROCESS_STATUS_FREE; // Finally, insert the link into the queue if (AQueueInsertFirst(&freepcbs, pcbs[i].l) != QUEUE_SUCCESS) { printf("FATAL ERROR: could not insert PCB link into queue in ProcessModuleInit!\n"); GracefulExit(); } } // There are no processes running at this point, so currentPCB=NULL currentPCB = NULL; dbprintf ('p', "ProcessModuleInit: function complete\n"); }
static THREAD_FUNC StatusServer(void *argptr) { FILE *fp; UINT16 port; int sd, peer; struct sockaddr_in serv_addr, cli_addr; int yes = 1, ilen = sizeof(int), clen = sizeof(cli_addr); static char *fid = "StatusServer"; LogMsg(LOG_DEBUG, "%s: thread started, id = %d", fid, THREAD_SELF()); /* Create socket and bind */ port = *((UINT16 *) argptr); if ((sd = socket(AF_INET, SOCK_STREAM, 0)) == INVALID_SOCKET) { LogMsg(LOG_ERR, "%s: socket: %s", fid, strerror(errno)); GracefulExit(MY_MOD_ID + 1); } memset((void *) &serv_addr, 0, sizeof(serv_addr)); serv_addr.sin_family = AF_INET; serv_addr.sin_addr.s_addr = htonl(INADDR_ANY); serv_addr.sin_port = htons(port); setsockopt(sd, SOL_SOCKET, SO_REUSEADDR, (char *) &yes, ilen); if (bind( sd, (struct sockaddr *) &serv_addr, sizeof(serv_addr)) != 0) { LogMsg(LOG_ERR, "%s: bind: %s", fid, strerror(errno)); GracefulExit(MY_MOD_ID + 2); } /* Start listening for connectinos */ if (listen(sd, 5) != 0) { LogMsg(LOG_ERR, "%s: listen: %s", fid, strerror(errno)); GracefulExit(MY_MOD_ID + 3); } /* Service one connection at a time */ LogMsg(LOG_INFO, "listening for status requests at port %hu", port); while (1) { peer = INVALID_SOCKET; while (peer == INVALID_SOCKET) { peer = accept(sd, (struct sockaddr *) &cli_addr, &clen); if (peer == INVALID_SOCKET && errno != EINTR) { LogMsg(LOG_ERR, "%s: accept: %s (ignored)", fid, strerror(errno)); } else { if ((fp = fdopen(peer, "w")) == NULL) { LogMsg(LOG_ERR, "%s: fdopen: %s", fid, strerror(errno)); } else { PrintStatusReport(fp); fclose(fp); } shutdown(peer, 2); close(peer); } } } }
VOID InitClientList(PARAM *par) { UINT32 i; static char *fid = "InitClientList"; ClientList.num = par->maxclient; ClientList.client = (CLIENT *) calloc(1,ClientList.num * sizeof(CLIENT)); if (ClientList.client == NULL) { LogMsg(LOG_ERR, "%s: calloc: %s", fid, strerror(errno)); GracefulExit(MY_MOD_ID + 1); } for (i = 0; i < ClientList.num; i++) { MUTEX_INIT(&ClientList.client[i].mutex); ClientList.client[i].index = i; ClientList.client[i].iacp = (IACP *) NULL; ClientList.client[i].ident = (char *) NULL; ClientList.client[i].finished = FALSE; ClientList.client[i].result = IACP_ALERT_NONE; ClientList.client[i].master = par->master; isiInitIncoming(&ClientList.client[i].incoming); isiInitDataRequest(&ClientList.client[i].datreq); ClientList.client[i].send.buflen = par->buflen.send; ClientList.client[i].send.buf = (UINT8 *) malloc(ClientList.client[i].send.buflen); if (ClientList.client[i].send.buf == NULL) { LogMsg(LOG_ERR, "%s: malloc: %s", fid, strerror(errno)); GracefulExit(MY_MOD_ID + 2); } ClientList.client[i].recv.buflen = par->buflen.recv; ClientList.client[i].recv.buf = (UINT8 *) malloc(ClientList.client[i].recv.buflen); if (ClientList.client[i].recv.buf == NULL) { LogMsg(LOG_ERR, "%s: malloc: %s", fid, strerror(errno)); GracefulExit(MY_MOD_ID + 3); } ClientList.client[i].temp.buflen = par->buflen.send * 2; /* bigger to handle compression failure */ ClientList.client[i].temp.buf = (UINT8 *) malloc(ClientList.client[i].temp.buflen); if (ClientList.client[i].temp.buf == NULL) { LogMsg(LOG_ERR, "%s: malloc: %s", fid, strerror(errno)); GracefulExit(MY_MOD_ID + 4); } ClientList.client[i].last.msg.len = 0; ClientList.client[i].last.msg.data = (UINT8 *) malloc(ClientList.client[i].recv.buflen); if (ClientList.client[i].last.msg.data == NULL) { LogMsg(LOG_ERR, "%s: malloc: %s", fid, strerror(errno)); GracefulExit(MY_MOD_ID + 3); } ClientList.client[i].last.datreq = NULL; ClientList.client[i].pkt.indx = -1; listInit(&ClientList.client[i].history); } }
int DiskCreate() { char *filename = NULL; int fsfd = -1; disk_block b; int i; // Check that you remembered to rename the filename for your group filename = DISK_FILENAME; if (filename[11] == 'X') { printf("DiskCreate: you didn't change the filesystem filename in include/os/disk.h. Cowardly refusing to do anything.\n"); GracefulExit(); } // Open the hard disk file if ((fsfd = FsOpen(DISK_FILENAME, FS_MODE_WRITE)) < 0) { printf ("DiskCreate: File system %s cannot be opened!\n", DISK_FILENAME); return DISK_FAIL; } // Write all zeros to the hard disk file to make sure it is the right size. // You need to do this because the writeblock/readblock operations are allowed in // random order. bzero(b.data, DISK_BLOCKSIZE); for(i=0; i<DISK_NUMBLOCKS; i++) { FsWrite(fsfd, b.data, DISK_BLOCKSIZE); } // Close the hard disk file if (FsClose(fsfd) < 0) { printf("DiskCreate: unable to close open file!\n"); return DISK_FAIL; } return DISK_SUCCESS; }
// file_delete(char *filename) int TrapFileDeleteHandler(uint32 *trapArgs, int sysMode) { char filename[FILE_MAX_FILENAME_LENGTH]; char *user_filename = NULL; int i; // If we're not in system mode, we need to copy everything from the // user-space virtual address to the kernel space address if (!sysMode) { // Get the arguments themselves into system space // Argument 0: userland pointer to filename string MemoryCopyUserToSystem (currentPCB, (trapArgs+0), &user_filename, sizeof(uint32)); // Now copy userland filename string into our filename buffer for(i=0; i<FILE_MAX_FILENAME_LENGTH; i++) { MemoryCopyUserToSystem(currentPCB, (user_filename+i), &(filename[i]), sizeof(char)); // Check for end of user-space string if (filename[i] == '\0') break; } if (i == FILE_MAX_FILENAME_LENGTH) { printf("TrapFileDeleteHandler: length of filename longer than allowed!\n"); GracefulExit(); } dbprintf('F', "TrapDeleteCloseHandler: just parsed filename (%s) from trapArgs\n", filename); } else { // Already in kernel space, no address translation necessary dstrncpy(filename, (char *)(trapArgs[0]), FILE_MAX_FILENAME_LENGTH); } return FileDelete(filename); }
//---------------------------------------------------------------------- // // 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; dbprintf ('p', "Now entering ProcessSchedule (cur=0x%x, %d ready)\n", (int)currentPCB, AQueueLength (&runQueue)); // 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); } GracefulExit(); } printf ("No runnable processes - exiting!\n"); GracefulExit (); // NEVER RETURNS } // Move the front of the queue to the end. The running process was the one in front. 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"); GracefulExit(); } ProcessFreeResources(pcb); } dbprintf ('p', "Leaving ProcessSchedule (cur=0x%x)\n", (int)currentPCB); }
//---------------------------------------------------------------------- // // ProcessDestroy // // Destroy a process by setting its status to zombie and putting it // on the zombie queue. The next time the scheduler is called, this // process will be marked as free. We can't necessarily do it now // because we might be the currently running process. // // NOTE: This must only be called from an interrupt or trap. However, // it need not be followed immediately by a ProcessSchedule() because // the process can continue running. // //---------------------------------------------------------------------- void ProcessDestroy (PCB *pcb) { dbprintf ('p', "ProcessDestroy (%d): function started\n", GetCurrentPid()); ProcessSetStatus (pcb, PROCESS_STATUS_ZOMBIE); if (AQueueRemove(&(pcb->l)) != QUEUE_SUCCESS) { printf("FATAL ERROR: could not remove link from queue in ProcessDestroy!\n"); GracefulExit(); } if ((pcb->l = AQueueAllocLink(pcb)) == NULL) { printf("FATAL ERROR: could not get link for zombie PCB in ProcessDestroy!\n"); GracefulExit(); } if (AQueueInsertFirst(&zombieQueue, pcb->l) != QUEUE_SUCCESS) { printf("FATAL ERROR: could not insert link into runQueue in ProcessWakeup!\n"); GracefulExit(); } dbprintf ('p', "ProcessDestroy (%d): function complete\n", GetCurrentPid()); }
//---------------------------------------------------------------------- // // ProcessWakeup // // Wake up a process from its slumber. This only involves putting // it on the run queue; it's not guaranteed to be the next one to // run. // // NOTE: This must only be called from an interrupt or trap. It // need not be followed immediately by ProcessSchedule() because // the currently running process is unaffected. // //---------------------------------------------------------------------- void ProcessWakeup (PCB *wakeup) { dbprintf ('p',"Waking up PID %d.\n", (int)(wakeup - pcbs)); // Make sure it's not yet a runnable process. ASSERT (wakeup->flags & PROCESS_STATUS_WAITING, "Trying to wake up a non-sleeping process!\n"); ProcessSetStatus (wakeup, PROCESS_STATUS_RUNNABLE); if (AQueueRemove(&(wakeup->l)) != QUEUE_SUCCESS) { printf("FATAL ERROR: could not remove wakeup PCB from waitQueue in ProcessWakeup!\n"); GracefulExit(); } if ((wakeup->l = AQueueAllocLink(wakeup)) == NULL) { printf("FATAL ERROR: could not get link for wakeup PCB in ProcessWakeup!\n"); GracefulExit(); } if (AQueueInsertLast(&runQueue, wakeup->l) != QUEUE_SUCCESS) { printf("FATAL ERROR: could not insert link into runQueue in ProcessWakeup!\n"); GracefulExit(); } }
//---------------------------------------------------------------------- // // ProcessSuspend // // Place a process in suspended animation until it's // awakened by ProcessAwaken. // // NOTE: This must only be called from an interrupt or trap. It // should be immediately followed by ProcessSchedule(). // //---------------------------------------------------------------------- void ProcessSuspend (PCB *suspend) { // Make sure it's already a runnable process. dbprintf ('p', "ProcessSuspend (%d): function started\n", GetCurrentPid()); ASSERT (suspend->flags & PROCESS_STATUS_RUNNABLE, "Trying to suspend a non-running process!\n"); ProcessSetStatus (suspend, PROCESS_STATUS_WAITING); if (AQueueRemove(&(suspend->l)) != QUEUE_SUCCESS) { printf("FATAL ERROR: could not remove process from run Queue in ProcessSuspend!\n"); GracefulExit(); } if ((suspend->l = AQueueAllocLink(suspend)) == NULL) { printf("FATAL ERROR: could not get Queue Link in ProcessSuspend!\n"); GracefulExit(); } if (AQueueInsertLast(&waitQueue, suspend->l) != QUEUE_SUCCESS) { printf("FATAL ERROR: could not insert suspend PCB into waitQueue!\n"); GracefulExit(); } dbprintf ('p', "ProcessSuspend (%d): function complete\n", GetCurrentPid()); }
VOID StartStatusServer(PARAM *par) { THREAD tid; static UINT16 StaticPort; static char *fid = "StartStatusServer"; StaticPort = par->status; if (!THREAD_CREATE(&tid, StatusServer, (void *) &StaticPort)) { LogMsg(LOG_ERR, "%s: THREAD_CREATE: %s", fid, strerror(errno)); GracefulExit(MY_MOD_ID + 0); } THREAD_DETACH(tid); }
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; }
//---------------------------------------------------------------------- // // main // // This routine is called when the OS starts up. It allocates a // PCB for the first process - the one corresponding to the initial // thread of execution. Note that the stack pointer is already // set correctly by _osinit (assembly language code) to point // to the stack for the 0th process. This stack isn't very big, // though, so it should be replaced by the system stack of the // currently running process. // //---------------------------------------------------------------------- void main (int argc, char *argv[]) { int i,j; int n; char buf[120]; char *userprog = (char *)0; int base=0; int numargs=0; int allargs_offset = 0; char allargs[SIZE_ARG_BUFF]; debugstr[0] = '\0'; printf ("Got %d arguments.\n", argc); printf ("Available memory: 0x%x -> 0x%x.\n", (int)lastosaddress, MemoryGetSize ()); printf ("Argument count is %d.\n", argc); for (i = 0; i < argc; i++) { printf ("Argument %d is %s.\n", i, argv[i]); } FsModuleInit (); for (i = 0; i < argc; i++) { if (argv[i][0] == '-') { switch (argv[i][1]) { case 'D': dstrcpy (debugstr, argv[++i]); break; case 'i': n = dstrtol (argv[++i], (void *)0, 0); ditoa (n, buf); printf ("Converted %s to %d=%s\n", argv[i], n, buf); break; case 'f': { int start, codeS, codeL, dataS, dataL, fd, j; int addr = 0; static unsigned char buf[200]; fd = ProcessGetCodeInfo (argv[++i], &start, &codeS, &codeL, &dataS, &dataL); printf ("File %s -> start=0x%08x\n", argv[i], start); printf ("File %s -> code @ 0x%08x (size=0x%08x)\n", argv[i], codeS, codeL); printf ("File %s -> data @ 0x%08x (size=0x%08x)\n", argv[i], dataS, dataL); while ((n = ProcessGetFromFile (fd, buf, &addr, sizeof (buf))) > 0) { for (j = 0; j < n; j += 4) { printf ("%08x: %02x%02x%02x%02x\n", addr + j - n, buf[j], buf[j+1], buf[j+2], buf[j+3]); } } close (fd); break; } case 'u': userprog = argv[++i]; base = i; // Save the location of the user program's name break; default: printf ("Option %s not recognized.\n", argv[i]); break; } if(userprog) break; } } dbprintf ('i', "About to initialize queues.\n"); AQueueModuleInit (); dbprintf ('i', "After initializing queues.\n"); MemoryModuleInit (); dbprintf ('i', "After initializing memory.\n"); ProcessModuleInit (); dbprintf ('i', "After initializing processes.\n"); SynchModuleInit (); dbprintf ('i', "After initializing synchronization tools.\n"); KbdModuleInit (); dbprintf ('i', "After initializing keyboard.\n"); ClkModuleInit(); for (i = 0; i < 100; i++) { buf[i] = 'a'; } i = FsOpen ("vm", FS_MODE_WRITE); dbprintf ('i', "VM Descriptor is %d\n", i); FsSeek (i, 0, FS_SEEK_SET); FsWrite (i, buf, 80); FsClose (i); DfsModuleInit(); dbprintf ('i', "After initializing dfs filesystem.\n"); FileModuleInit(); dbprintf ('i', "After initializing file module.\n"); // Setup command line arguments if (userprog != (char *)0) { numargs=0; allargs_offset = 0; // Move through each of the argv addresses for(i=0; i<argc-base; i++) { // At each argv address, copy the string into allargs, including the '\0' for(j=0; allargs_offset < SIZE_ARG_BUFF; j++) { allargs[allargs_offset++] = argv[i+base][j]; if (argv[i+base][j] == '\0') break; // end of this string } numargs++; } allargs[SIZE_ARG_BUFF-1] = '\0'; // set last char to NULL for safety ProcessFork(0, (uint32)allargs, userprog, 1); } else { dbprintf('i', "No user program passed!\n"); } // Start the clock which will in turn trigger periodic ProcessSchedule's ClkStart(); intrreturn (); // Should never be called because the scheduler exits when there // are no runnable processes left. GracefulExit(); // NEVER RETURNS! }
//---------------------------------------------------------------------- // // 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 fd, n; 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; dbprintf ('p', "ProcessFork (%d): function started\n", GetCurrentPid()); 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"); GracefulExit (); // 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"); GracefulExit(); } // 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", (int)CurrentIntrs()); RestoreIntrs (intrs); dbprintf ('I', "New interrupt value is 0x%x.\n", (int)CurrentIntrs()); // Copy the process name into the PCB. dbprintf('p', "ProcessFork: Copying process name (%s) to pcb\n", name); 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"); GracefulExit (); // NEVER RETURNS! } pcb->pagetable[0] = MemorySetupPte (newPage); newPage = MemoryAllocPage (); if (newPage == 0) { printf ("bFATAL: couldn't allocate system stack - no free pages!\n"); GracefulExit (); // NEVER RETURNS! } pcb->sysStackArea = newPage * MEMORY_PAGE_SIZE; //---------------------------------------------------------------------- // 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", (int)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 PCB onto run queue intrs = DisableIntrs (); if ((pcb->l = AQueueAllocLink(pcb)) == NULL) { printf("FATAL ERROR: could not get link for forked PCB in ProcessFork!\n"); GracefulExit(); } if (AQueueInsertLast(&runQueue, pcb->l) != QUEUE_SUCCESS) { printf("FATAL ERROR: could not insert link into runQueue in ProcessFork!\n"); GracefulExit(); } 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). dbprintf ('p', "ProcessFork (%d): function complete\n", GetCurrentPid()); return (pcb - pcbs); }
//---------------------------------------------------------------------- // // 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, 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: 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_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; // Traps for Disk Access case TRAP_DISK_WRITE_BLOCK: ihandle = TrapDiskWriteBlockHandler(trapArgs, isr & DLX_STATUS_SYSMODE); ProcessSetResult(currentPCB, ihandle); break; case TRAP_DISK_SIZE: ProcessSetResult(currentPCB, DiskSize()); break; case TRAP_DISK_BLOCKSIZE: ProcessSetResult(currentPCB, DiskBytesPerBlock()); break; case TRAP_DISK_CREATE: ProcessSetResult(currentPCB, DiskCreate()); break; // Traps for DFS filesystem case TRAP_DFS_INVALIDATE: DfsInvalidate(); break; // Traps for file functions case TRAP_FILE_OPEN: ProcessSetResult(currentPCB, TrapFileOpenHandler(trapArgs, isr & DLX_STATUS_SYSMODE)); break; case TRAP_FILE_CLOSE: ProcessSetResult(currentPCB, TrapFileCloseHandler(trapArgs, isr & DLX_STATUS_SYSMODE)); break; case TRAP_FILE_DELETE: ProcessSetResult(currentPCB, TrapFileDeleteHandler(trapArgs, isr & DLX_STATUS_SYSMODE)); break; case TRAP_FILE_READ: ProcessSetResult(currentPCB, TrapFileReadHandler(trapArgs, isr & DLX_STATUS_SYSMODE)); break; case TRAP_FILE_WRITE: ProcessSetResult(currentPCB, TrapFileWriteHandler(trapArgs, isr & DLX_STATUS_SYSMODE)); break; case TRAP_FILE_SEEK: ProcessSetResult(currentPCB, TrapFileSeekHandler(trapArgs, isr & DLX_STATUS_SYSMODE)); break; // Traps for running OS testing code case TRAP_TESTOS: RunOSTests(); break; default: printf ("Got an unrecognized trap (0x%x) - exiting!\n", cause); GracefulExit (); 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); GracefulExit (); break; case TRAP_ADDRESS: printf ("Exiting after illegal address at iar=0x%x, isr=0x%x\n", iar, isr); GracefulExit (); break; case TRAP_ILLEGALINST: printf ("Exiting after illegal instruction at iar=0x%x, isr=0x%x\n", iar, isr); GracefulExit (); break; case TRAP_PAGEFAULT: printf ("Exiting after page fault at iar=0x%x, isr=0x%x\n", iar, isr); GracefulExit (); break; default: printf ("Got an unrecognized system interrupt (0x%x) - exiting!\n", cause); GracefulExit (); break; } } dbprintf ('t',"About to return from dointerrupt.\n"); // Note that this return may schedule a new process! intrreturn (); }
//-------------------------------------------------------------------- // // int process_create(char *exec_name, ...); // // Here we support reading command-line arguments. Maximum MAX_ARGS // command-line arguments are allowed. Also the total length of the // arguments including the terminating '\0' should be less than or // equal to SIZE_ARG_BUFF. // //-------------------------------------------------------------------- static void TrapProcessCreateHandler(uint32 *trapArgs, int sysMode) { char allargs[SIZE_ARG_BUFF]; // Stores full string of arguments (unparsed) char name[PROCESS_MAX_NAME_LENGTH]; // Local copy of name of executable (100 chars or less) char *username=NULL; // Pointer to user-space address of exec_name string int i=0, j=0; // Loop index variables char *args[MAX_ARGS]; // All parsed arguments (char *'s) int allargs_position = 0; // Index into current "position" in allargs char *userarg = NULL; // Current pointer to user argument string int numargs = 0; // Number of arguments passed on command line dbprintf('p', "TrapProcessCreateHandler: function started\n"); // Initialize allargs string to all NULL's for safety for(i=0;i<SIZE_ARG_BUFF; i++) { allargs[i] = '\0'; } // First deal with user-space addresses if(!sysMode) { dbprintf('p', "TrapProcessCreateHandler: creating user process\n"); // Get the known arguments into the kernel space. // Argument 0: user-space pointer to name of executable MemoryCopyUserToSystem (currentPCB, (trapArgs+0), &username, sizeof(char *)); // Copy the user-space string at user-address "username" into kernel space for(i=0; i<PROCESS_MAX_NAME_LENGTH; i++) { MemoryCopyUserToSystem(currentPCB, (username+i), &(name[i]), sizeof(char)); // Check for end of user-space string if (name[i] == '\0') break; } dbprintf('p', "TrapProcessCreateHandler: just parsed executable name (%s) from trapArgs\n", name); if (i == PROCESS_MAX_NAME_LENGTH) { printf("TrapProcessCreateHandler: length of executable filename longer than allowed!\n"); GracefulExit(); } // Copy the program name into "allargs", since it has to be the first argument (i.e. argv[0]) allargs_position = 0; dstrcpy(&(allargs[allargs_position]), name); allargs_position += dstrlen(name) + 1; // The "+1" is so we're pointing just beyond the NULL // Rest of arguments: a series of char *'s until we hit NULL or MAX_ARGS for(i=0; i<MAX_ARGS; i++) { // First, must copy the char * itself into kernel space in order to read its value MemoryCopyUserToSystem(currentPCB, (trapArgs+1+i), &userarg, sizeof(char *)); // If this is a NULL in the set of char *'s, this is the end of the list if (userarg == NULL) break; // Store a pointer to the kernel-space location where we're copying the string args[i] = &(allargs[allargs_position]); // Copy the string into the allargs, starting where we left off last time through this loop for (j=0; j<SIZE_ARG_BUFF; j++) { MemoryCopyUserToSystem(currentPCB, (userarg+j), &(allargs[allargs_position]), sizeof(char)); // Move current character in allargs to next spot allargs_position++; // Check that total length of arguments is still ok if (allargs_position == SIZE_ARG_BUFF) { printf("TrapProcessCreateHandler: strlen(all arguments) > maximum length allowed!\n"); GracefulExit(); } // Check for end of user-space string if (allargs[allargs_position-1] == '\0') break; } } if (i == MAX_ARGS) { printf("TrapProcessCreateHandler: too many arguments on command line (did you forget to pass a NULL?)\n"); GracefulExit(); } numargs = i+1; // Arguments are now setup } else { // Addresses are already in kernel space, so just copy into our local variables // for simplicity // Argument 0: (char *) name of program dstrncpy(name, (char *)(trapArgs[0]), PROCESS_MAX_NAME_LENGTH); // Copy the program name into "allargs", since it has to be the first argument (i.e. argv[0]) allargs_position = 0; dstrcpy(&(allargs[allargs_position]), name); allargs_position += dstrlen(name) + 1; // The "+1" is so that we are now pointing just beyond the "null" in the name allargs_position = 0; for (i=0; i<MAX_ARGS; i++) { userarg = (char *)(trapArgs[i+1]); if (userarg == NULL) break; // found last argument // Store the address of where we're copying the string args[i] = &(allargs[allargs_position]); // Copy string into allargs for(j=0; j<SIZE_ARG_BUFF; j++) { allargs[allargs_position] = userarg[j]; allargs_position++; if (allargs_position == SIZE_ARG_BUFF) { printf("TrapProcessCreateHandler: strlen(all arguments) > maximum length allowed!\n"); GracefulExit(); } // Check for end of user-space string if (allargs[allargs_position-1] == '\0') break; } } if (i == MAX_ARGS) { printf("TrapProcessCreateHandler: too many arguments on command line (did you forget to pass a NULL?)\n"); GracefulExit(); } numargs = i+1; } ProcessFork(0, (uint32)allargs, name, 1); }