Explaination (operating systems)
project2/design.txt
1) 32 semaphores are available 2) 64 locks are available 3) Interrupts are disabled, and then SemCreate looks through the array of semaphores for an unused semaphore, returning invalid if there isn't one. It then calls semInit, which initializes the semaphore queue, and the count of the semaphore. SemCreate then returns the pointer to the semaphore. 4)SemWait: Disables interrupts, then decrements sem->count. If count is now below 0, then it allocates a link to the current PCB, then adds that link to the end of the queue. Then the process goes to sleep, and interrupts are restored. SemSignal: Disables interrupts, then increments the count. If the count is less than or equal to 0, it sets the link l to the first in the waiting queue, then removes that from the queue. Then, that process wakes up, and the link is freed. Then interrupts are restored. 5) We don't want the user to be able to access the implementation of the semaphore, so we resolve this issue by using a handle. A handle is an index from the beginning of the pool in memory where semaphores are implemented. We pass the user the location of the handle instead of the actual memory address of the semaphore, so they can't eff it up. To implement locks, we first followed the trap from usertraps.s back to traps.h and discovered the system call was being made to create a handle and return it to the user. We then edited synch.h with our definition of a lock should be, namely a PID for the owning process and a queue of waiting processes. We then implemented the calls to make a handle in synch.c by having it check for a valid handle, then computing the actual location in memory of the kernel. From there, we implemented the lock by having it lock to a single process and have all other processes wait in the queue until it was empty and they could execute.
project2/src/consumer.c
#include "lab2-api.h" typedef struct DB { //make sure this struct matches in all files char buffer[5]; int bufferValCount; } DB; int main(int argc, char *argv[]) { int i; DB *db; char print; uint32 handle; sem_t spage, sem1, sem2; lock_t lock; handle = dstrtol(argv[1], NULL, 10); spage = dstrtol(argv[2], NULL, 10); lock = dstrtol(argv[3], NULL, 10); sem1 = dstrtol( argv[4], NULL, 10);//blocks when buffer is full sem2 = dstrtol(argv[5], NULL, 10);//block when buffer is empty db = (DB*)shmat(handle); //Took this out because we want to ensure that a producer starts first // if(sem_signal(spage)) { // Printf("Could not map virtual address to memory"); // exit(); // } while(db->bufferValCount != 0 || sem1 != 0 || sem2 != 5) { lock_acquire(lock); Printf("\nconsumer while loop\n"); Printf("%c", db->buffer[db->bufferValCount-1]);//print a char db->buffer[db->bufferValCount-1] = NULL;//remove printed char from buffer db->bufferValCount--;//decrement the buffer count lock_release(lock); sem_signal(sem2); sem_wait(sem1); } Printf("\nconsumer done\n"); return 0; }
project2/src/dlx.h
// // Definitions of flags and other things defined by the DLX processor. // #ifndef _dlx_h_ #define _dlx_h_ #define DLX_STATUS_INTRMASK 0x0f // up to 16 interrupt levels #define DLX_STATUS_FPTRUE 0x20 // Set if last FP comparison was true #define DLX_STATUS_SYSMODE 0x40 // Set if CPU is in system mode #define DLX_STATUS_PAGE_TABLE 0x100 // Set -> use a page table #define DLX_STATUS_TLB 0x200 // Set -> use a software-loaded TLB #endif // _dlx_h_
project2/src/dlxos.h
// // Routines used by the entire operating system. // #ifndef _dlxos_h_ #define _dlxos_h_ #include "misc.h" typedef unsigned int uint32; #define DLX_PROCESS_QUANTUM 10000 // in microseconds extern void SetTimer (int); extern char debugstr[]; #define ASSERT(cond,s) (cond ? 0 : printf ("%s: %s\n", __FUNCTION__,s)) // dbprintf() is a VERY useful macro. It gets used as follows: // dbprintf ('x', "This prints %d and %x\n", val1, val2); // This will print the expected string only if the debugging options contain // the letter 'x'. This allows users to turn on different debugging // statements at different times by using different letters. For example, // process debugging statements could use 'p', and memory 'm'. Specifying // a '+' in the debugging string will turn on all debugging printfs. #define dbprintf(flag, format, args...) \ if ((dindex(debugstr,flag)!=(char *)0) || \ (dindex(debugstr,'+')!=(char *)0)) { \ printf (format, ## args); \ } extern int CurrentIntrs (); extern int SetIntrs (int); extern void KbdModuleInit (); extern void intrreturn (); inline int DisableIntrs () { return (SetIntrs (0xf)); } inline int EnableIntrs () { return (SetIntrs (0x0)); } inline int RestoreIntrs (int intrs) { return (SetIntrs (intrs)); } #endif // _dlxos_h_
project2/src/dlxos.s
;;; ;;; Initialization code for programs running in the DLX simulator. ;;; Copyright (c) 1999 by Ethan Miller ;;; ;;; .text .align 2 ;;;---------------------------------------------------------------------- ;;; osinit ;;; ;;; This is the first function called by the simulator, even before main. ;;; Since it's called BEFORE any "real" routines, we can use any registers ;;; we want as long as we don't mess up the stack or frame pointers. ;;; ;;; This routine loads the interrupt vector to point to the interrupt ;;; handler later in this file. It should never return, because _main ;;; should call exitsim() or exit() rather than returning here. ;;; ;;; After initialization is done, this routine jumps to _main to start the ;;; C code portion of the operating system. ;;;---------------------------------------------------------------------- .proc _osinit .global _osinit _osinit: ;; Set up the interrupt handler lhi r1,(_intrhandler>>16)&0xffff addui r1,r1,_intrhandler&0xffff movi2s intrvec,r1 ;; Never returns because exitsim is called first j _main .endproc _osinit ;;;---------------------------------------------------------------------- ;;; intrhandler ;;; ;;; Called when an interrupt or trap is received by the CPU. It stores the ;;; current register set on the system stack. It then pushes the parameters ;;; to the C interrupt handler onto the stack: the ISR, IAR, and CAUSE ;;; registers along with the original stack pointer. The C interrupt ;;; handler can then copy arguments from the original stack, performing ;;; user -> system translations if necessary. ;;; ;;;---------------------------------------------------------------------- .proc _intrhandler .global _intrhandler _intrhandler: ;; We can use r31 as scratch space because its value was saved in ;; ir31. However, we must save the "real" value of r31 on the ;; stack. ;; Always store the registers on the system stack. This way, we don't ;; have to worry about translating things from user -> system. We ;; use r31 as the base register because its value was saved in ir31. ;; If this was a user process, load r29 with the current system ;; stack pointer. If it was a system process, just use the ;; current stack pointer. movs2i r31,isr andi r31,r31,0x40 bnez r31,intrSystem lhi r31,(_currentPCB>>16)&0xffff addui r31,r31,_currentPCB&0xffff lw r31,(r31) lw r31,4(r31) ;; Save the original (user) stack pointer sw -184(r31),r29 ; we haven't yet bumped SP, and 156-340 = -184 ;; Copy the system stack pointer into r29 (current stack pointer) ori r29,r31,0 beqz r0,intrSaveReg ; skip over the system part.... intrSystem: ;; Use the stack pointer we're already using ;; Save r29 because we won't save it later sw -184(r29),r29 ; we haven't yet bumped SP, and 156-340 = -184 intrSaveReg: ;; Adjust stack pointer for all the stuff we're going to push. This ;; is a bit more space than we need currently, but it leaves room ;; for more stuff if needed. subui r29,r29,#340 ;; Push all the stuff onto the stack sw 44(r29),r1 sw 48(r29),r2 sw 52(r29),r3 sw 56(r29),r4 sw 60(r29),r5 sw 64(r29),r6 sw 68(r29),r7 sw 72(r29),r8 sw 76(r29),r9 sw 80(r29),r10 sw 84(r29),r11 sw 88(r29),r12 sw 92(r29),r13 sw 96(r29),r14 sw 100(r29),r15 sw 104(r29),r16 sw 108(r29),r17 sw 112(r29),r18 sw 116(r29),r19 sw 120(r29),r20 sw 124(r29),r21 sw 128(r29),r22 sw 132(r29),r23 sw 136(r29),r24 sw 140(r29),r25 sw 144(r29),r26 sw 148(r29),r27 sw 152(r29),r28 ;; Skip r29 - stored earlier! sw 160(r29),r30 ;; Load the value of r31 from the special register and then save it movs2i r3,ir31 sw 164(r29),r3 ;; Store the floating-point registers sd 168(r29),f0 sd 176(r29),f2 sd 184(r29),f4 sd 192(r29),f6 sd 200(r29),f8 sd 208(r29),f10 sd 216(r29),f12 sd 224(r29),f14 sd 232(r29),f16 sd 240(r29),f18 sd 248(r29),f20 sd 256(r29),f22 sd 264(r29),f24 sd 272(r29),f26 sd 280(r29),f28 sd 288(r29),f30 ;; NOTE: we don't save the interrupt vector register because it ;; doesn't change from process to process. ;; NOTE: we don't save the status register because most of the flags ;; are the same from process to process if they're in the interrupt ;; handler. Of course, we DO save the ISR. movs2i r4,iar sw 296(r29),r4 movs2i r5,isr sw 300(r29),r5 movs2i r6,cause sw 304(r29),r6 movs2i r3,fault sw 308(r29),r3 movs2i r3,ptbase sw 312(r29),r3 movs2i r3,ptsize sw 316(r29),r3 movs2i r3,ptbits sw 320(r29),r3 ;; Push the interrupt information onto the stack sw 0(r29),r6 ; push CAUSE sw 4(r29),r4 ; push IAR sw 8(r29),r5 ; push ISR ;; Get the original stack pointer lw r1,156(r29) sw 12(r29),r1 ;; Save the previous interrupt stack frame address in the current frame lhi r1,(_currentPCB>>16)&0xffff addui r1,r1,_currentPCB&0xffff lw r1,(r1) lw r2,0(r1) sw 40(r29),r2 ;; Save this frame address in the PCB. This is used so the OS can ;; easily access the current interrupt save frame sw 0(r1), r29 ;; Call the "real" interrupt handler. This will possibly switch ;; contexts. This call never returns; instead, a separate routine ;; (_intrreturn) is called to return from interrupts after restoring ;; the current context. j _dointerrupt nop .endproc _intrhandler � ;;;---------------------------------------------------------------------- ;;; intrreturn ;;; ;;; Return from an interrupt or trap. This restores all of the previously ;;; saved registers and then returns to where the program left off. The ;;; current contents of the registers are destroyed. This routine uses ;;; the saved interrupt frame pointer, so the stack pointer need not ;;; be correct. Note, though, that the register contents from the previous ;;; process must have previously been saved - in other words, call this ;;; routine from a trap or interrupt handler. ;;;---------------------------------------------------------------------- .proc _intrreturn .global _intrreturn _intrreturn: ;; Disable interrupts - this routine must be atomic, and interrupts ;; may not be currently disabled. Don't worry about saving registers ;; because we're about to reload them anyway. jal _DisableIntrs ;; Get our interrupt stack frame location and load it into the stack ;; pointer. lhi r1,(_currentPCB>>16)&0xffff addui r1,r1,_currentPCB&0xffff lw r1,0(r1) lw r29,0(r1) ;; Get the previous interrupt stack frame location and make it the ;; current interrupt save frame. lw r2,40(r29) sw 0(r1), r2 ;; Reload the registers for the new process. We don't have to ;; load in the exact opposite order as long as we're careful to ;; get the right values back in. lw r3,296(r29) movi2s iar,r3 lw r3,300(r29) movi2s isr,r3 lw r3,304(r29) movi2s cause,r3 lw r3,308(r29) movi2s fault,r3 lw r3,312(r29) movi2s ptbase,r3 lw r3,316(r29) movi2s ptsize,r3 lw r3,320(r29) movi2s ptbits,r3 ;; Reload the floating point registers ld f0,168(r29) ld f2,176(r29) ld f4,184(r29) ld f6,192(r29) ld f8,200(r29) ld f10,208(r29) ld f12,216(r29) ld f14,224(r29) ld f16,232(r29) ld f18,240(r29) ld f20,248(r29) ld f22,256(r29) ld f24,264(r29) ld f26,272(r29) ld f28,280(r29) ld f30,288(r29) ;; Reload the integer registers. We don't reload r0 because it's ;; always 0. We won't reload r29 here because we're using it as ;; the stack pointer. The same goes for r1, which we'll use as ;; scratch so we can store r29. ;; Skip r1 - restored later lw r2,48(r29) lw r3,52(r29) lw r4,56(r29) lw r5,60(r29) lw r6,64(r29) lw r7,68(r29) lw r8,72(r29) lw r9,76(r29) lw r10,80(r29) lw r11,84(r29) lw r12,88(r29) lw r13,92(r29) lw r14,96(r29) lw r15,100(r29) lw r16,104(r29) lw r17,108(r29) lw r18,112(r29) lw r19,116(r29) lw r20,120(r29) lw r21,124(r29) lw r22,128(r29) lw r23,132(r29) lw r24,136(r29) lw r25,140(r29) lw r26,144(r29) lw r27,148(r29) lw r28,152(r29) ;; Skip r29 - restored later lw r30,160(r29) lw r31,164(r29) addui r29,r29,#340 ;; Save the current value of the stack pointer after adjusting it ;; Note that this will "destroy" the stack values below this interrupt ;; stack frame. This is exactly what we want! sw 4(r1),r29 ori r1,r29,#0 lw r29,-184(r1) ; 156-340 = -184 lw r1,-296(r1) ; 44-340 = -296 rfe .endproc _intrreturn � ;;;---------------------------------------------------------------------- ;;; SetIntrs ;;; ;;; This routine sets the interrupt level to the value passed (0 -> all ;;; interrupts enabled; 0xf -> all interrupts disabled). It returns the ;;; former value for the interrupt flags. ;;;---------------------------------------------------------------------- .proc _SetIntrs .global _SetIntrs _SetIntrs: subui r29,r29,#16 sw 12(r29),r2 ; save r2 lw r2,16(r29) ; Get the new interrupt level andi r2,r2,#0x0f ; Mask off interrupt levels movs2i r1,status sw 8(r29),r1 ; Store the old interrupt values andi r1,r1,#0xfff0 ; Mask off old interrupt level or r1,r2,r1 ; OR in new interrupt level movi2s status,r1 lw r1,8(r29) ; Get back the original interrupt level andi r1,r1,#0x0f ; Mask off all but interrupt levels lw r2,12(r29) ; restore r2 addui r29,r29,#16 ; restore stack pointer jr r31 nop .endproc _SetIntrs .proc _CurrentIntrs .global _CurrentIntrs _CurrentIntrs: movs2i r1,status andi r1,r1,#0xf jr r31 nop .endproc _CurrentIntrs ;;;---------------------------------------------------------------------- ;;; _ProcessSleep ;;; ;;; If a context switch from elsewhere in the kernel is desired, take a ;;; trap and call this routine from the trap handler. ;;;---------------------------------------------------------------------- .proc _ProcessSleep .global _ProcessSleep _ProcessSleep: trap #0x410 ; This is a process sleep trap nop jr r31 nop .endproc _ProcessSleep
project2/src/filesys.c
// // filesys.c // // This file has handlers for the file systems supported by the DLX // operating system. These include both the basic, simulator-provided // file system and the more complex file system from the file system // assignment. static char rcsid[] = "$Id: filesys.c,v 1.1 2000/09/20 01:50:19 elm Exp elm $"; #include "dlxos.h" #include "process.h" #include "filesys.h" // One entry for each type of file system. Currently, the file systems // are native Unix (0) and DLX (1). static Fs fs[2]; // One entry for each open file in the operating system. static FsOpenFile openfiles[FS_MAX_OPEN_FILES]; //---------------------------------------------------------------------- // // FdValid // // Return 1 if the file descriptor is valid, 0 otherwise. // //---------------------------------------------------------------------- static inline int FdValid (int fd) { return ((fd > 0) && (fd < FS_MAX_OPEN_FILES) && (openfiles[fd].flags != 0)); } //---------------------------------------------------------------------- // // FsFreeEntry // // Free an entry in the open files table. // //---------------------------------------------------------------------- static inline void FsFreeEntry (int x) { openfiles[x].flags = 0; } � //---------------------------------------------------------------------- // // FsOpen // // Open a file. The name of the file is passed, along with the file // mode (read or write). // // This function figures out which file system is desired using a // simple heuristic. Basically, if the file starts with "dlx:", it's // a DLX file system file. Otherwise, it's a UNIX file. // // Once the file system is figured out, this routine allocates a // file descriptor and calls the file-system specific open routine. // //---------------------------------------------------------------------- int FsOpen (const char *name, int mode) { int i, retval; dbprintf ('f', "Attepmting to open %s mode=%d.\n", name, mode); // Mask off all but the mode bits mode &= FS_MODE_RW; // ERROR if the caller hasn't specified a file mode. if (mode == 0) { return (-1); } for (i = 0; i < FS_MAX_OPEN_FILES; i++) { if (openfiles[i].flags == 0) { break; } } if (i >= FS_MAX_OPEN_FILES) { return (-1); } openfiles[i].flags = mode; // If file name starts with "dlx:", it's a DLX file system file. Pass // the remainder of the file name to FsDlxOpen. if (!dstrncmp (name, "dlx:", 4)) { name += 4; // Skip past the "dlx:" openfiles[i].fs = 1; } else { openfiles[i].fs = 0; } dbprintf ('f', "File %s opening in file system %d.\n",name,openfiles[i].fs); retval = fs[openfiles[i].fs].Open (i, name, mode); if (retval < 0) { // Open failed, so return error code FsFreeEntry (i); return (retval); } dbprintf ('f', "Opened %s in FS %d, mode=%d slot=%d.\n", name, openfiles[i].fs, mode, i); return (i); } //---------------------------------------------------------------------- // // FsClose // // Close an open file. Call the FS-specific routine, and then mark // the file table entry unused. // //---------------------------------------------------------------------- int FsClose (int fd) { int retval; if (!FdValid (fd)) { return (-1); } retval = fs[openfiles[fd].fs].Close (fd); FsFreeEntry (fd); return (retval); } � //---------------------------------------------------------------------- // // FsRead // // Read data from a file. // //---------------------------------------------------------------------- int FsRead (int fd, char *buf, int n) { if (!FdValid (fd)) { return (-1); } return (fs[openfiles[fd].fs].Read (fd, buf, n)); } //---------------------------------------------------------------------- // // FsWrite // // Write data to a file. // //---------------------------------------------------------------------- int FsWrite (int fd, char *buf, int n) { if (!FdValid (fd)) { return (-1); } return (fs[openfiles[fd].fs].Write (fd, buf, n)); } //---------------------------------------------------------------------- // // FsSeek // // Seek in a file. // //---------------------------------------------------------------------- int FsSeek (int fd, int offset, int whence) { if (!FdValid (fd)) { return (-1); } return (fs[openfiles[fd].fs].Seek (fd, offset, whence)); } //---------------------------------------------------------------------- // // FsDelete // // Delete a file. // //---------------------------------------------------------------------- int FsDelete (const char *name) { // If file name starts with "dlx:", it's a DLX file system file. Pass // the remainder of the file name to FsDlxDelete. if (!dstrncmp (name, "dlx:", 4)) { name += 4; // Skip past the "dlx:" return (fs[1].Delete (name)); } else { return (fs[0].Delete (name)); } } � //---------------------------------------------------------------------- // // FsUnixOpen // FsUnixRead // FsUnixWrite // FsUnixSeek // FsUnixClose // // Unix file I/O routines. These are pretty simple, and just call // the simulator traps directly using the file descriptor stored in // the open file table. // //---------------------------------------------------------------------- int FsUnixOpen (int x, const char *name, int mode) { dbprintf ('f', "Attempting to open file %s into desc %d, mode %d.\n", name, x, mode); if ((openfiles[x].u.Unix.fd = open (name, mode)) < 0) { return (openfiles[x].u.Unix.fd); } return (1); } int FsUnixRead (int x, char *buf, int n) { return (read (openfiles[x].u.Unix.fd, buf, n)); } int FsUnixWrite (int x, char *buf, int n) { return (write (openfiles[x].u.Unix.fd, buf, n)); } int FsUnixSeek (int x, int offset, int where) { return (lseek (openfiles[x].u.Unix.fd, offset, where)); } int FsUnixClose (int x) { int retval; retval = close (openfiles[x].u.Unix.fd); return (retval); } int FsUnixDelete (const char *name) { // Refuse to delete Unix files via the simulator. This is for safety // reasons. return (-1); } � //---------------------------------------------------------------------- // // FsDlxIo // // Perform a read or a write on a DLX file. // //---------------------------------------------------------------------- int FsDlxIo (int fd, char *buf, int n, int which) { // Your code to read or write a DLX file goes here. return (1); } //---------------------------------------------------------------------- // // FsDlxRead // FsDlxWrite // // This is just a stub that calls FsDlxIo to do either a read or a // write in the DLX file system. // //---------------------------------------------------------------------- int FsDlxRead (int fd, char *buf, int n) { return (FsDlxIo (fd, buf, n, 0)); } int FsDlxWrite (int fd, char *buf, int n) { return (FsDlxIo (fd, buf, n, 1)); } � //---------------------------------------------------------------------- // // FsDlxOpen // // Open a file in the DLX file system. Currently, this just means // to set current position to 0. However, it should also check to // make sure that the file exists. // //---------------------------------------------------------------------- int FsDlxOpen (int f, const char *name, int mode) { openfiles[f].u.Dlx.curpos = 0; // You may want to put additional code here (checking to see if the // file exists, etc.) return (1); } //---------------------------------------------------------------------- // // FsDlxSeek // // Seek in a DLX file. This just requires adjusting the current // position as recorded in the open files table. // //---------------------------------------------------------------------- int FsDlxSeek (int f, int offset, int whence) { switch (whence) { case FS_SEEK_SET: break; case FS_SEEK_CUR: break; case FS_SEEK_END: break; }; return (openfiles[f].u.Dlx.curpos); } //---------------------------------------------------------------------- // // FsDlxClose // // Close a DLX file system file. //---------------------------------------------------------------------- int FsDlxClose (int f) { // You may want to add code to this routine. return (1); } � //---------------------------------------------------------------------- // // FsDlxDelete // // Delete a file in the DLX file system. // //---------------------------------------------------------------------- int FsDlxDelete (const char *file) { // Your code goes here. return (1); } � //---------------------------------------------------------------------- // // FsModuleInit // // Initialize the file system module. This consists simply of marking // all the file slots as available. // //---------------------------------------------------------------------- void FsModuleInit () { int i; for (i = 0; i < FS_MAX_OPEN_FILES; i++) { openfiles[i].flags = 0; } fs[0].Open = FsUnixOpen; fs[0].Close = FsUnixClose; fs[0].Read = FsUnixRead; fs[0].Write = FsUnixWrite; fs[0].Seek = FsUnixSeek; fs[0].Delete = FsUnixDelete; fs[1].Open = FsDlxOpen; fs[1].Close = FsDlxClose; fs[1].Read = FsDlxRead; fs[1].Write = FsDlxWrite; fs[1].Seek = FsDlxSeek; fs[1].Delete = FsDlxDelete; }
project2/src/filesys.h
// // filesys.h // // Includes for the file system. Includes the definition of the file // system interface structure. // // Copyright 1999 by Ethan L. Miller // University of Maryland Baltimore County // // $Id: filesys.h,v 1.1 2000/09/20 01:50:19 elm Exp elm $ // #ifndef _filesys_h_ #define _filesys_h_ #define FS_FS_BUILTIN 1 #define FS_FS_DLXOS 2 #define FS_MODE_READ 0x1 #define FS_MODE_WRITE 0x2 #define FS_MODE_RW (FS_MODE_READ | FS_MODE_WRITE) // If this flag is set, a new file is created if no file already exists. // This flag only works with the DLX file system. #define FS_MODE_CREATE 0x4 // IMPORTANT: These must match the underlying UNIX seek parameters. // These values are correct for Linux and should work elsewhere, but // be careful.... #define FS_SEEK_SET 0 #define FS_SEEK_CUR 1 #define FS_SEEK_END 2 #define FS_BUFFER_SIZE 256 #define FS_MAX_OPEN_FILES 32 typedef struct Fs { int (*Open)(int, const char *, int); int (*Read)(int, char *, int); int (*Write)(int, char *, int); int (*Seek)(int, int, int); int (*Close)(int); int (*Delete)(const char *); } Fs; typedef struct FsUnixInfo { int fd; // Unix file descriptor } FsUnixInfo; typedef struct FsDlxInfo { int curpos; // What's our current position in the file? } FsDlxInfo; typedef struct FsOpenFile { int fs; // which file system is this file in? int flags; char buffer[FS_BUFFER_SIZE]; union { FsUnixInfo Unix; FsDlxInfo Dlx; } u; } FsOpenFile; extern int FsOpen (const char *, int); extern int FsRead (int, char *, int); extern int FsWrite (int, char *, int); extern int FsSeek (int, int, int); extern int FsClose (int); extern int FsDelete (const char *); #endif // _filesys_h_
project2/src/lab2-api.h
/***************************************************************** * * Various APIs for Lab 2 of OS469 * Copyright: Uday Savagaonkar, Jan 2002 * *****************************************************************/ #ifndef _LAB2_API_H_ #define _LAB2_API_H_ #define NULL (void *)0x0 #define INVALID_SEM -1 #define BUFFERSIZE 5 typedef unsigned int uint32; typedef int sem_t; typedef int lock_t; typedef int cond_t; //Related to processes uint32 getpid(); //trap 0x431 void process_create(char *arg1, ...); //trap 0x432 //Related to shared memory uint32 shmget(); //trap 0x440 void *shmat(uint32 handle); //trap 0x441 //Related to semaphores sem_t sem_create(int count); //trap 0x450 Calls SemCreate() int sem_wait(sem_t sem); //trap 0x451 Calls SemHandleWait() int sem_signal(sem_t sem); //trap 0x452 Calls SemHandleSignal() //----------------------------------------------------------------------- // You need to write OS handler routines for the following functions // We have already provided the trap interface. All you need to do is fill in // the missing parts in synch.c and synch.h. The routines that need to be // modified are also given in front of each system call. For detailed // description of thses routines, see synch.c. //------------------------------------------------------------------------ lock_t lock_create(); //trap 0x452 Calls LockCreate() int lock_acquire(lock_t lock); //trap 0x453 Calls LockHandleAcquire() int lock_release(lock_t lock); //trap 0x454 Calls LockHandleRelease() cond_t cond_create(lock_t lock); //trap 0x455 Calls CondCreate() int cond_wait(cond_t cond); //trap 0x456 Calls CondHandleWait() int cond_signal(cond_t cond); //trap 0x457 Calls CondHandleSignal() int cond_broadcast(cond_t cond); //trap 0x458 Calls CondHandleBroadcast() #endif _LAB2_API_H_
project2/src/Makefile
# # This is a makefile for the DLX OS projects. # CC = gcc-dlx AS = dlxasm CFLAGS = -mtraps -O3 #INCS = $(wildcard *.h) #SRCS = $(wildcard *.c) #OBJS = $(addsuffix .o, $(basename $(wildcard *.c))) \ # $(addsuffix .o, $(basename $(wildcard *.s))) INCS = dlxos.h traps.h filesys.h memory.h misc.h process.h queue.h \ synch.h syscall.h share_memory.h SRCS = filesys.c memory.c misc.c process.c queue.c synch.c traps.c sysproc.c OBJS = $(addsuffix .o, $(basename $(SRCS))) all: os.dlx.obj userprog userprog2 userprog3 userprog4 run producer consumer os.dlx.obj: os.dlx $(AS) -i _osinit -l os.lst os.dlx mv os.dlx.obj ../execs os.dlx: $(OBJS) dlxos.o trap_random.o osend.o share_memory.o $(CC) -mtraps -O3 dlxos.o trap_random.o share_memory.o $(OBJS) osend.o -o os.dlx share_memory.o: cp share_memory.api share_memory.o osend.o: osend.s $(CC) -c osend.s trap_random.o: trap_random.s $(CC) -c trap_random.s dlxos.o: dlxos.s $(CC) -c dlxos.s usertraps.o: usertraps.s $(CC) -c usertraps.s userprog : userprog.o usertraps.o misc.o $(CC) -mtraps -O3 userprog.o usertraps.o misc.o -o userprog.dlx $(AS) -l userprog.lst userprog.dlx mv userprog.dlx.obj ../execs userprog2 : userprog2.o usertraps.o misc.o $(CC) -mtraps -O3 userprog2.o usertraps.o misc.o -o userprog2.dlx $(AS) -l userprog2.lst userprog2.dlx mv userprog2.dlx.obj ../execs userprog3 : userprog3.o usertraps.o misc.o $(CC) -mtraps -O3 userprog3.o usertraps.o misc.o -o userprog3.dlx $(AS) -l userprog3.lst userprog3.dlx mv userprog3.dlx.obj ../execs userprog4 : userprog4.o usertraps.o misc.o $(CC) -mtraps -O3 userprog4.o usertraps.o misc.o -o userprog4.dlx $(AS) -l userprog4.lst userprog4.dlx mv userprog4.dlx.obj ../execs run : run.o usertraps.o misc.o $(CC) -mtraps -O3 run.o usertraps.o misc.o -o run.dlx $(AS) -l run.lst run.dlx mv run.dlx.obj ../execs producer : producer.o usertraps.o misc.o $(CC) -mtraps -O3 producer.o usertraps.o misc.o -o producer.dlx $(AS) -l producer.lst producer.dlx mv producer.dlx.obj ../execs consumer : consumer.o usertraps.o misc.o $(CC) -mtraps -O3 consumer.o usertraps.o misc.o -o consumer.dlx $(AS) -l consumer.lst consumer.dlx mv consumer.dlx.obj ../execs Makefile.depend: depend depend: $(SRCS) $(INCS) $(CC) -MM $(SRCS) > Makefile.depend clean: /bin/rm -f *.o *.dlx *.lst *.obj Makefile.depend vm include Makefile.depend
project2/src/Makefile.depend
filesys.o: filesys.c dlxos.h misc.h process.h queue.h filesys.h memory.o: memory.c dlxos.h misc.h memory.h process.h queue.h misc.o: misc.c misc.h process.o: process.c dlxos.h misc.h process.h queue.h synch.h memory.h \ filesys.h queue.o: queue.c dlxos.h misc.h queue.h synch.o: synch.c dlxos.h misc.h process.h queue.h synch.h traps.o: traps.c dlx.h dlxos.h misc.h traps.h process.h queue.h sysproc.o: sysproc.c process.h dlxos.h misc.h queue.h synch.h
project2/src/memory.c
// // memory.c // // Routines for dealing with memory management. static char rcsid[] = "$Id: memory.c,v 1.1 2000/09/20 01:50:19 elm Exp elm $"; #include "dlxos.h" #include "memory.h" #include "process.h" #include "queue.h" static uint32 pagestart; static int freemapmax; static int nfreepages; static uint32 freepages[MEMORY_MAX_PAGES/32]; static uint32 negativeone = 0xffffffff; //---------------------------------------------------------------------- // // This silliness is required because the compiler believes that // it can invert a number by subtracting it from zero and subtracting // an additional 1. This works unless you try to negate 0x80000000, // which causes an overflow when subtracted from 0. Simply // trying to do an XOR with 0xffffffff results in the same code // being emitted. // //---------------------------------------------------------------------- static inline uint32 invert (uint32 n) { return (n ^ negativeone); } //---------------------------------------------------------------------- // // MemoryGetSize // // Return the total size of memory in the simulator. This is // available by reading a special location. // //---------------------------------------------------------------------- int MemoryGetSize () { return (*((int *)DLX_MEMSIZE_ADDRESS)); } //---------------------------------------------------------------------- // // MemorySetFreemap // //---------------------------------------------------------------------- inline void MemorySetFreemap (int p, int b) { uint32 wd = p / 32; uint32 bitnum = p % 32; freepages[wd] = (freepages[wd] & invert(1 << bitnum)) | (b << bitnum); dbprintf ('m', "Set freemap entry %d to 0x%x.\n", wd, freepages[wd]); } � //---------------------------------------------------------------------- // // MemoryInitModule // // Initialize the memory module of the operating system. // //---------------------------------------------------------------------- void MemoryModuleInit () { int i; int maxpage = MemoryGetSize () / MEMORY_PAGE_SIZE; int curpage; pagestart = (lastosaddress + MEMORY_PAGE_SIZE - 4) / MEMORY_PAGE_SIZE; freemapmax = (maxpage+31) / 32; dbprintf ('m', "Map has %d entries, memory size is 0x%x.\n", freemapmax, maxpage); dbprintf ('m', "Free pages start with page # 0x%x.\n", pagestart); for (i = 0; i < freemapmax; i++) { // Initially, all pages are considered in use. This is done to make // sure we don't have any partially initialized freemap entries. freepages[i] = 0; } nfreepages = 0; for (curpage = pagestart; curpage < maxpage; curpage++) { nfreepages += 1; MemorySetFreemap (curpage, 1); } dbprintf ('m', "Initialized %d free pages.\n", nfreepages); } � //---------------------------------------------------------------------- // // MemoryAllocPage // // Allocate a page of memory. // //---------------------------------------------------------------------- inline int MemoryAllocPage () { static int mapnum = 0; int bitnum; uint32 v; if (nfreepages == 0) { printf("ERROR AT THIS POINT\n"); return (0); } dbprintf ('m', "Allocating memory, starting with page %d\n", mapnum); while (freepages[mapnum] == 0) { mapnum += 1; if (mapnum >= freemapmax) { mapnum = 0; } } v = freepages[mapnum]; for (bitnum = 0; (v & (1 << bitnum)) == 0; bitnum++) { } freepages[mapnum] &= invert(1 << bitnum); v = (mapnum * 32) + bitnum; dbprintf ('m', "Allocated memory, from map %d, page %d, map=0x%x.\n", mapnum, v, freepages[mapnum]); nfreepages -= 1; return (v); } //---------------------------------------------------------------------- // // MemoryFreePage // // Free a page of memory. // //---------------------------------------------------------------------- void MemoryFreePage(uint32 page) { MemorySetFreemap (page, 1); nfreepages += 1; dbprintf ('m',"Freed page 0x%x, %d remaining.\n", page, nfreepages); } � //---------------------------------------------------------------------- // // MemoryTranslateUserToSystem // // Translate a user address (in the process referenced by pcb) // into an OS (physical) address. This works for simple one-level // page tables, but will have to be modified for two-level page // tables. // //---------------------------------------------------------------------- uint32 MemoryTranslateUserToSystem (PCB *pcb, uint32 addr) { int page = addr / MEMORY_PAGE_SIZE; int offset = addr % MEMORY_PAGE_SIZE; if (page > pcb->npages) { return (0); } return ((pcb->pagetable[page] & MEMORY_PTE_MASK) + offset); } � //---------------------------------------------------------------------- // // moveBetweenSpaces // // Copy data between user and system spaces. This is done page by // page by: // * Translating the user address into system space. // * Copying all of the data in that page // * Repeating until all of the data is copied. // A positive direction means the copy goes from system to user // space; negative direction means the copy goes from user to system // space. // // This routine returns the number of bytes copied. Note that this // may be less than the number requested if there were unmapped pages // in the user range. If this happens, the copy stops at the // first unmapped address. // //---------------------------------------------------------------------- static int moveBetweenSpaces (PCB *pcb, unsigned char *s, unsigned char *u, int n, int dir) { unsigned char *curUser; int bytesCopied = 0; int bytesToCopy; while (n > 0) { // Translate current user page to system address. If this fails, return // the number of bytes copied so far. curUser = (unsigned char *)MemoryTranslateUserToSystem (pcb, (uint32)u); if (curUser == (unsigned char *)0) { // Leave the loop if translation fails. break; } // Calculate the number of bytes to copy this time. This is the minimum // of the bytes left on this page and the bytes left to copy. bytesToCopy = MEMORY_PAGE_SIZE - ((uint32)curUser % MEMORY_PAGE_SIZE); if (bytesToCopy > n) { bytesToCopy = n; } // Perform the copy. if (dir >= 0) { bcopy (s, curUser, bytesToCopy); } else { bcopy (curUser, s, bytesToCopy); } // Keep track of bytes copied and adjust addresses appropriately. n -= bytesToCopy; bytesCopied += bytesToCopy; s += bytesToCopy; u += bytesToCopy; } return (bytesCopied); } //---------------------------------------------------------------------- // // These two routines copy data between user and system spaces. // They call a common routine to do the copying; the only difference // between the calls is the actual call to do the copying. Everything // else is identical. // //---------------------------------------------------------------------- int MemoryCopySystemToUser (PCB *pcb, unsigned char *from,unsigned char *to, int n) { return (moveBetweenSpaces (pcb, from, to, n, 1)); } int MemoryCopyUserToSystem (PCB *pcb, unsigned char *from,unsigned char *to, int n) { return (moveBetweenSpaces (pcb, to, from, n, -1)); } � //---------------------------------------------------------------------- // // MemorySetupPte // // Set up a PTE given a page number. // //---------------------------------------------------------------------- uint32 MemorySetupPte (uint32 page) { return ((page * MEMORY_PAGE_SIZE) | MEMORY_PTE_VALID); } //---------------------------------------------------------------------- // // MemoryFreePte // // Free a page given its PTE. // //---------------------------------------------------------------------- void MemoryFreePte (uint32 pte) { MemoryFreePage ((pte & MEMORY_PTE_MASK) / MEMORY_PAGE_SIZE); } //---------------------------------------------------------------------- // // MemoryPteToPage // // Given a PTE, return the base address for the (virtual) page the // PTE references. // //---------------------------------------------------------------------- uint32 MemoryPteToPage (uint32 pte) { return (pte & MEMORY_PTE_MASK); } � //---------------------------------------------------------------------- // // MemoryGetOperandAddress // // This routine takes an instruction and a pointer to a PCB, and // returns the address that the instruction is trying to use // for its memory operand. If the instruction doesn't access memory, // it returns 0xffffffff. Note that this means it's impossible to // distinguish between a valid access to 0xffffffff and an instruction // without an operand.... // //---------------------------------------------------------------------- uint32 MemoryGetOperandAddress (PCB *pcb, uint32 instr) { static uint32 invalidOps = 0x3404; uint32 opcode = (instr >> 26) & 0x3f; uint32 reg; uint32 addr; uint32 offset; uint32 regValue; if ((opcode<0x20) || (opcode>0x2f) || ((invalidOps >> (opcode-0x20) & 1))) { return (0xffffffff); } reg = (instr >> 21) & 0x1f; offset = instr & 0xffff; if (offset & 0x8000) { offset |= 0xffff0000; } regValue = pcb->currentSavedFrame[PROCESS_STACK_IREG+reg]; addr = regValue + offset; dbprintf ('m', "Operand for instruction %08x is 0x%x (reg=<%d,0x%x>)\n", instr, addr, reg, regValue); return (addr); }
project2/src/memory.h
// // memory.h // // Definitions for dealing with memory. // // // $Id: memory.h,v 1.1 2000/09/20 01:50:19 elm Exp elm $ #ifndef _memory_h_ #define _memory_h_ // We can read this address in I/O space to figure out how much memory // is available on the system. #define DLX_MEMSIZE_ADDRESS 0xffff0000 // We currently support 64 KB pages. This is done as 64 KB pages in // the level 1 page table and no level 2 page table // These two constants should be set as follows: // L1_PAGE_SIZE_BITS -> amount mapped by a single entry in the L1 table // L2_PAGE_SIZE_BITS -> amount mapped by a single entry in the L2 table // // For example, if you have 4KB pages and each L2 page table has 512 entries, // you'd have 2MB per entry in a L1 table. That would mean L1_BITS=21 and // L2_BITS=12. #define MEMORY_L1_PAGE_SIZE_BITS 16 // each entry in L1 is 64 KB #define MEMORY_L2_PAGE_SIZE_BITS 16 // if L1 == L2, there's no L2 tables #define MEMORY_PAGE_SIZE (1 << MEMORY_L2_PAGE_SIZE_BITS) #define MEMORY_PAGE_MASK (MEMORY_PAGE_SIZE-1) #define MEMORY_MAX_PAGES 0x10000 // The PTE is valid if this bit is set in the PTE! #define MEMORY_PTE_VALID 0x00000001 // The page is dirty if this bit is set in the PTE #define MEMORY_PTE_DIRTY 0x00000002 // The page has been referenced if this bit is set in the PTE #define MEMORY_PTE_REFERENCED 0x00000004 #define MEMORY_PTE_MASK (~(MEMORY_PTE_VALID|MEMORY_PTE_DIRTY|MEMORY_PTE_REFERENCED)) extern int lastosaddress; // Defined in an assembly file extern int MemoryGetSize (); extern int MemoryAllocPage (); extern void MemoryFreePage (uint32 page); extern uint32 MemorySetupPte (uint32 page); extern void MemoryFreePte (uint32 pte); extern uint32 MemoryPteToPage (); extern void MemoryModuleInit (); extern uint32 MemoryTranslateUserToSystem (); extern int MemoryCopySystemToUser (); extern int MemoryCopyUserToSystem (); #endif // _memory_h_
project2/src/misc.c
// // misc.c // // Miscellaneous routines for the operating system. These include // basic routines that would be in libc if we had access to it. // #include "misc.h" //---------------------------------------------------------------------- // // dstrcpy // dstrncpy // dstrcat // // These three functions do exactly what their counterparts in the // standard C library do. // //---------------------------------------------------------------------- char* dstrcpy (char *to, const char *from) { char *tokeep = to; char last; do { last = *(to++) = *(from++); } while (last != '\0'); return (tokeep); } char* dstrncpy (char *to, const char *from, int n) { char *tokeep = to; char last; int cur = 0; do { // Stop if we're exceeded the maximum string length if (n-- <= 0) { break; } last = *(to++) = *(from++); } while (last != '\0'); return (tokeep); } char * dstrcat (char *onto, const char *addn) { char *konto = onto; while (*onto != '\0') { onto++; } dstrcpy (onto, addn); return (konto); } int dstrncmp (const char *s1, const char *s2, int n) { int i; for (i = 0; i < n; i++) { // If they don't match, end the loop if (*s2 == '\0') { // If the second string is NULL, we're at the end of the string and // have a match. return (0); } else if (*s1 != *s2) { break; } s1++; s2++; } if (i == n) { return (0); } else { return (((*s1 - *s2) < 0) ? -1 : ((*s1 == *s2) ? 0 : 1)); } } int dstrlen (const char *s) { int i = 0; while (*(s++) != '\0') { i++; } return (i); } const char * dstrstr (const char *buf, const char *pattern) { int n = dstrlen (pattern); while (*buf != '\0') { if (dstrncmp (buf, pattern, n) == 0) { return (buf); } buf++; } return ((char *)0); } � //---------------------------------------------------------------------- // // dindex // dmindex // // dindex is identical to the libc function index. dmindex is // similar to dindex, but it reports the index in the string of the // first match for any one of the characters passed (rather than // matching only on a single specific character). // //---------------------------------------------------------------------- const char * dmindex (const char *s, const char *match) { const char *c; while (*s != '\0') { for (c = match; *c != '\0'; c++) { if (*s == *c) { return (s); } } } return ((const char *)0); } const char* dindex (const char *s, int c) { while (*s != '\0') { if (*s == c) { return (s); } s++; } return ((char *)0); } � //---------------------------------------------------------------------- // // ditoa: Convert an integer into an ASCII number. // dstrtol: Convert ASCII into an integer // //---------------------------------------------------------------------- void ditoa (int n, char *buf) { int k; if (n < 0) { *(buf++) = '-'; n = -n; } else if (n == 0) { *(buf++) = '0'; } else { for (k = 1; k <= n; k *= 10) { } do { k /= 10; *(buf++) = (n / k) + '0'; n %= k; } while (k > 1); } *buf = '\0'; } int dstrtol (char *c, char **newpos, int base) { int v = 0; int curdigit; int sign; while ((*c == ' ') || (*c == '\t') || (*c == '\n')) { c++; } if (newpos != (char **)0) { *newpos = c; } if (*c == '\0') { return (v); } if (*c == '-') { sign = -1; c++; } else { sign = 1; } if (base == 0) { if (*c == '0') { c++; if ((*c == 'x') || (*c == 'X')) { base = 16; c++; } else { base = 8; } } else { base = 10; } } if ((base <= 0) || (base > 16)) { return (v); } do { if ((*c >= '0') && (*c <= '9')) { curdigit = (*c) - '0'; } else if ((*c >= 'a') && (*c <= 'z')) { curdigit = (*c) + 10 - 'a'; } else if ((*c >= 'A') && (*c <= 'Z')) { curdigit = (*c) + 10 - 'A'; } else { curdigit = 1000; } if (curdigit < base) { v *= base; v += curdigit; c++; } } while (curdigit < base); if (newpos != (char **)0) { *newpos = c; } v *= sign; return (v); } � //---------------------------------------------------------------------- // // bcopy: Copy bytes from one location to another. // bzero: Set all the bytes in a region to zero. // //---------------------------------------------------------------------- void bcopy (char *src, char *dst, int count) { while (count-- > 0) { *(dst++) = *(src++); } } void bzero (char *dst, int count) { while (count-- > 0) { *(dst++) = 0; } }
project2/src/misc.h
// // Routines used by the entire operating system. // #ifndef _misc_h_ #define _misc_h_ extern char* dstrcpy(char*, const char*); extern char* dstrncpy(char*, const char*, int n); extern const char *dstrstr (const char *, const char *); extern int dstrlen (const char *); extern int dstrncmp (const char *, const char *, int); extern int dstrtol(char*, char**, int); extern const char* dindex(const char*, int); extern const char* dmindex(const char*, const char *); inline int isspace (char c) { return ((c == ' ') || (c == '\t') || (c == '\n')); } inline int isxdigit (char c) { return (((c >= '0') && (c <= '9')) || ((c >= 'a') && (c <= 'f')) || ((c >= 'A') && (c <= 'A'))); } #endif // !_misc_h_
project2/src/osend.s
;;; This file just includes a single variable that contains the last address ;;; in the operating system. The rest of memory is available for use by ;;; processes. .data .align 2 .global _lastosaddress _lastosaddress: .word _lastosaddress+8
project2/src/process.c
// // process.c // // This file defines routines for dealing with processes. This // includes the "main" routine for the OS, which creates a process // for the initial thread of execution. It also includes // code to create and delete processes, as well as context switch // code. Note, however, that the actual context switching is // done in assembly language elsewhere. #include "dlxos.h" #include "process.h" #include "synch.h" #include "memory.h" #include "filesys.h" // Pointer to the current PCB. This is used by the assembly language // routines for context switches. PCB *currentPCB; // List of free PCBs. static Queue freepcbs; // List of processes that are ready to run (ie, not waiting for something // to happen). static Queue runQueue; // List of processes that are waiting for something to happen. There's no // reason why this must be a single list; there could be many lists for many // different conditions. static Queue waitQueue; // List of processes waiting to be deleted. See below for a description of // the reason that we need a separate queue for processes about to die. static Queue zombieQueue; // Static area for all process control blocks. This is necessary because // we can't use malloc() inside the OS. static PCB pcbs[PROCESS_MAX_PROCS]; // Default value for scheduler quantum. This could be set to any value. // In fact, it could even be dynamic, though that would require modifying // the timer trap handler.... static processQuantum = DLX_PROCESS_QUANTUM; // String listing debugging options to print out. char debugstr[200]; � //---------------------------------------------------------------------- // // 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', "Entering ProcessModuleInit\n"); QueueInit (&freepcbs); QueueInit (&runQueue); QueueInit (&waitQueue); QueueInit (&zombieQueue); for (i = 0; i < PROCESS_MAX_PROCS; i++) { dbprintf ('p', "Initializing PCB %d @ 0x%x.\n", i, &(pcbs[i])); pcbs[i].flags = PROCESS_STATUS_FREE; QueueLinkInit (&(pcbs[i].l), (void *)&pcbs[i]); QueueInsertFirst(&freepcbs, &(pcbs[i].l)); } currentPCB = NULL; dbprintf ('p', "Leaving ProcessModuleInit\n"); } � //---------------------------------------------------------------------- // // ProcessSetStatus // // Set the status of a process. // //---------------------------------------------------------------------- void ProcessSetStatus (PCB *pcb, int status) { pcb->flags &= ~PROCESS_STATUS_MASK; pcb->flags |= status; } //---------------------------------------------------------------------- // // ProcessFreeResources // // Free the resources associated with a process. This assumes the // process isn't currently on any queue. // //---------------------------------------------------------------------- void ProcessFreeResources (PCB *pcb) { int i; int npages; QueueInsertLast (&freepcbs, &pcb->l); // Free the process's memory. This is easy with a one-level page // table, but could get more complex with two-level page tables. npages = pcb->npages; for (i=0; i<npages; i++) { MemoryFreeSharedPte(pcb, i); // *MUST* be called before calling // MemoryFreePte. MemoryFreePte does not know // anything about shared pages, and hence it // might screw up big time } 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); } //---------------------------------------------------------------------- // // ProcessSetResult // // Set the result returned to a process. This is done by storing // the value into the current register save area for r1. When the // context is restored, r1 will contain the return value. This // routine should only be called from a trap. Calling it at other // times (such as an interrupt handler) will cause unpredictable // results. // //---------------------------------------------------------------------- void ProcessSetResult (PCB * pcb, uint32 result) { pcb->currentSavedFrame[PROCESS_STACK_IREG+1] = result; } � //---------------------------------------------------------------------- // // 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; int i; dbprintf ('p', "Now entering ProcessSchedule (cur=0x%x, %d ready)\n", currentPCB, QueueLength (&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 (QueueEmpty (&runQueue)) { printf ("No runnable processes - exiting!\n"); exitsim (); // NEVER RETURNS } // Move the front of the queue to the end. The running process was the // one in front. pcb = (PCB *)((QueueFirst (&runQueue))->object); QueueRemove (&pcb->l); QueueInsertLast (&runQueue, &pcb->l); // Now, run the one at the head of the queue. pcb = (PCB *)((QueueFirst (&runQueue))->object); currentPCB = pcb; dbprintf ('p',"About to switch to PCB 0x%x,flags=0x%x @ 0x%x\n", pcb, pcb->flags, 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 (!QueueEmpty (&zombieQueue)) { pcb = (PCB *)(QueueFirst (&zombieQueue)->object); dbprintf ('p', "Freeing zombie PCB 0x%x.\n", pcb); QueueRemove (&pcb->l); ProcessFreeResources (pcb); } // Set the timer so this process gets at most a fixed quantum of time. TimerSet (processQuantum); dbprintf ('p', "Leaving ProcessSchedule (cur=0x%x)\n", currentPCB); } � //---------------------------------------------------------------------- // // 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', "Suspending PCB 0x%x (%s).\n", suspend, suspend->name); ASSERT (suspend->flags & PROCESS_STATUS_RUNNABLE, "Trying to suspend a non-running process!\n"); ProcessSetStatus (suspend, PROCESS_STATUS_WAITING); QueueRemove (&suspend->l); QueueInsertLast (&waitQueue, &suspend->l); } //---------------------------------------------------------------------- // // 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 PCB 0x%x.\n", wakeup); // 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); QueueRemove (&wakeup->l); QueueInsertLast (&runQueue, &wakeup->l); } � //---------------------------------------------------------------------- // // 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', "Entering ProcessDestroy for 0x%x.\n", pcb); ProcessSetStatus (pcb, PROCESS_STATUS_ZOMBIE); QueueRemove (&pcb->l); QueueInsertFirst (&zombieQueue, &pcb->l); dbprintf('p', "Leaving ProcessDestroy for 0x%x.\n", pcb); } //---------------------------------------------------------------------- // // ProcessExit // // This routine is called to exit from a system process. It simply // calls an exit trap, which will be caught to exit the process. // //---------------------------------------------------------------------- static void ProcessExit () { exit (); } � //---------------------------------------------------------------------- // // 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, 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; //---------------------------------------------------------------------- // 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, 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); } � //---------------------------------------------------------------------- // // getxvalue // // Convert a hex digit into an actual value. // //---------------------------------------------------------------------- static inline int getxvalue (int x) { if ((x >= '0') && (x <= '9')) { return (x - '0'); } else if ((x >= 'a') && (x <= 'f')) { return (x + 10 - 'a'); } else if ((x >= 'A') && (x <= 'F')) { return (x + 10 - 'A'); } else { return (0); } } � //---------------------------------------------------------------------- // // ProcessGetCodeSizes // // Get the code sizes (stack & data) for a file. A file descriptor // for the named file is returned. This descriptor MUST be closed // (presumably by the caller) at some point. // //---------------------------------------------------------------------- int ProcessGetCodeInfo (const char *file, uint32 *startAddr, uint32 *codeStart, uint32 *codeSize, uint32 *dataStart, uint32 *dataSize) { int fd; int totalsize; char buf[100]; char *pos; // Open the file for reading. If it returns a negative number, the open // didn't work. if ((fd = FsOpen (file, FS_MODE_READ)) < 0) { dbprintf ('f', "ProcessGetCodeInfo: open of %s failed (%d).\n", file, fd); return (-1); } dbprintf ('f', "File descriptor is now %d.\n", fd); if ((totalsize = FsRead (fd, buf, sizeof (buf))) != sizeof (buf)) { dbprintf ('f', "ProcessGetCodeInfo: read got %d (not %d) bytes from %s\n", totalsize, sizeof (buf), file); FsClose (fd); return (-1); } if (dstrstr (buf, "start:") == NULL) { dbprintf ('f', "ProcessGetCodeInfo: %s missing start line (not a DLX executable?)\n", file); return (-1); } pos = (char *)dindex (buf, ':') + 1; // Get the start address and overall size *startAddr = dstrtol (pos, &pos, 16); totalsize = dstrtol (pos, &pos, 16); // Get code & data section start & sizes *codeStart = dstrtol (pos, &pos, 16); *codeSize = dstrtol (pos, &pos, 16); *dataStart = dstrtol (pos, &pos, 16); *dataSize = dstrtol (pos, &pos, 16); // Seek to start of first real line FsSeek (fd, 1 + dindex (buf, '\n') - buf, 0); return (fd); } � //---------------------------------------------------------------------- // // ProcessGetFromFile // // Inputs: // addr - points to an integer that contains the address of // the byte past that previously returned. If this is the // first call to this routine, *addr should be set to 0. // fd - File descriptor from which to read data. The file format // is the same as that used by the DLX simulator. // buf - points to a buffer that will receive data from the input // file. Note that the data is NOT 0-terminated, and may // include any number of 0 bytes. // max - maximum length of data to return. The routine collects data // until either the address changes or it has read max bytes. // // Returns the number of bytes actually stored into buf. In addition, // *addr is updated to point to the byte following the last byte in // the buffer. // // Load a file into memory. The file format consists of a // leading address, followed by a colon, followed by the data // to go at that address. If the address is omitted, the data // follows that from the previous line of the file. // //---------------------------------------------------------------------- int ProcessGetFromFile (int fd, unsigned char *buf, uint32 *addr, int max) { char localbuf[204]; int nbytes; int seekpos; unsigned char *pos = buf; char *lpos = localbuf; // Remember our position at the start of the routine so we can adjust // it later. seekpos = FsSeek (fd, 0, FS_SEEK_CUR); // The maximum number of characters we could read is limited to the // maximum buffer space available to the caller * 2 because each 2 // characters in the input file result in a single byte of program // info being read in. max = max * 2; // If the callers maximum is greater than the available buffer space, // limit the buffer space further. if (max > (sizeof(localbuf)-4)) { max = sizeof(localbuf)-4; } if ((nbytes = FsRead (fd, localbuf, max)) <= 0) { return (0); } // 'Z' is unused in load file, so use it to mark the end of the buffer // Back up until just after the last newline in the data we read. dbprintf ('f', "Got %d bytes at offset %d ...", nbytes, seekpos); while (localbuf[--nbytes] != '\n') { } localbuf[nbytes+1] = 'Z'; localbuf[nbytes+2] = '\0'; dbprintf ('f', " terminated at %d.\n", nbytes); dbprintf ('f', "Buffer is '%s'\n", localbuf); nbytes = 0; while (dindex (lpos, 'Z') != NULL) { if (dindex (lpos, ':') == NULL) { break; } if (*lpos != ':') { // If we're going to go to a new address, we break out of the // loop and return what we've got already. if (nbytes > 0) { break; } *addr = dstrtol (lpos, &lpos, 16); dbprintf ('f', "New address is 0x%x.\n", *addr); } if (*lpos != ':') { break; } lpos++; // skip past colon while (1) { while (((*lpos) == ' ') || (*lpos == '\t')) { lpos++; } if (*lpos == '\n') { lpos++; break; } else if (!(isxdigit (*lpos) && isxdigit (*(lpos+1)))) { // Exit loop if at least one digit isn't a hex digit. break; } pos[nbytes++] = (getxvalue(*lpos) * 16) + getxvalue(*(lpos+1)); lpos += 2; (*addr)++; } } // Seek to just past the last line we read. FsSeek (fd, seekpos + lpos - localbuf, FS_SEEK_SET); dbprintf ('f', "Seeking to %d and returning %d bytes!\n", seekpos + lpos - localbuf, nbytes); return (nbytes); } � //---------------------------------------------------------------------- // // 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. // //---------------------------------------------------------------------- main (int argc, char *argv[]) { int i, j; int n; char buf[120]; char *userprog = (char *)0; static PCB temppcb; uint32 addr; extern void SysprocCreateProcesses (); char *param[12]={NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL}; int base; debugstr[0] = '\0'; printf ("Got %d arguments.\n", argc); printf ("Available memory: 0x%x -> 0x%x.\n", lastosaddress, MemoryGetSize ()); printf ("Argument count is %d.\n", argc); for (i = 0; i < argc; i++) { printf ("Argument %d is %s.\n", i, argv[i]); } // *((int *)0xfff00100) = 't'; 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; break; default: printf ("Option %s not recognized.\n", argv[i]); break; } if(userprog) break; } } dbprintf ('i', "About to initialize queues.\n"); QueueModuleInit (); dbprintf ('i', "After initializing queues.\n"); MemoryModuleInit (); dbprintf ('i', "After initializing memory.\n"); ProcessModuleInit (); dbprintf ('i', "After initializing processes.\n"); ShareModuleInit (); dbprintf ('i', "After initializing shared memory.\n"); SynchModuleInit (); dbprintf ('i', "After initializing synchronization tools.\n"); KbdModuleInit (); dbprintf ('i', "After initializing keyboard.\n"); 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); if (userprog != (char *)0) { for(i=base;i<argc&&i-base<11; i++) { param[i-base] = argv[i]; } process_create(param[0], param[1], param[2], param[3], param[4], param[5], param[6], param[7], param[8], param[9], param[10], param[11]); // ProcessFork (0, (uint32)"Help Me man!", userprog, 1); } SysprocCreateProcesses (); dbprintf ('i', "Created processes - about to set timer quantum.\n"); TimerSet (processQuantum); dbprintf ('i', "Set timer quantum to %d, about to run first process.\n", processQuantum); intrreturn (); // Should never be called because the scheduler exits when there // are no runnable processes left. exitsim(); // NEVER RETURNS! } unsigned GetCurrentPid() { return (unsigned)(currentPCB - pcbs); } unsigned findpid(PCB *pcb) { return (unsigned)(pcb - pcbs); } uint32 get_argument(char *string) { static char *str; static int location=0; int location2; if(string) { str=string; location = 0; } location2 = location; if(str[location]=='\0'||location>=99) return 0; for(;location<100;location++) { if(str[location]=='\0') { location++; break; } } return location2; } void process_create(char *name, ...) { char **args; int i, j, k; char allargs[1000]; args = &name; k=0; for(i=0; args[i]!=NULL; i++) { j=0; do { allargs[k] = args[i][j]; j++; k++; } while(args[i][j-1]!='\0'); } allargs[k] = allargs[k+1] = 0; ProcessFork(0, (uint32)allargs, name, 1); }
project2/src/process.h
// // process.h // // Definitions for process creation and manipulation. These include // the process control block (PCB) structure as well as information // about the stack format for a saved process. // #ifndef _process_h_ #define _process_h_ #include "dlxos.h" #include "queue.h" //#include "share_memory.h" #define PROCESS_MAX_PROCS 32 // Maximum number of active processes #define PROCESS_INIT_ISR_SYS 0x140 // Initial status reg value for system processes #define PROCESS_INIT_ISR_USER 0x100 // Initial status reg value for user processes #define PROCESS_STATUS_FREE 0x1 #define PROCESS_STATUS_RUNNABLE 0x2 #define PROCESS_STATUS_WAITING 0x4 #define PROCESS_STATUS_STARTING 0x8 #define PROCESS_STATUS_ZOMBIE 0x10 #define PROCESS_STATUS_MASK 0x3f #define PROCESS_TYPE_SYSTEM 0x100 #define PROCESS_TYPE_USER 0x200 typedef void (*VoidFunc)(); // Process control block typedef struct PCB { uint32 *currentSavedFrame; // -> current saved frame. MUST BE 1ST! uint32 *sysStackPtr; // Current system stack pointer. MUST BE 2ND! uint32 sysStackArea; // System stack area for this process unsigned int flags; char name[80]; // Process name uint32 pagetable[16]; // Statically allocated page table int npages; // Number of pages allocated to this process Link l; // Used for keeping PCB in queues } PCB; // Offsets of various registers from the stack pointer in the register // save frame. Offsets are in WORDS (4 byte chunks) #define PROCESS_STACK_IREG 10 // Offset of r0 (grows upwards) // NOTE: r0 isn't actually stored! This is for convenience - r1 is the // first stored register, and is at location PROCESS_STACK_IREG+1 #define PROCESS_STACK_FREG (PROCESS_STACK_IREG+32) // Offset of f0 #define PROCESS_STACK_IAR (PROCESS_STACK_FREG+32) // Offset of IAR #define PROCESS_STACK_ISR (PROCESS_STACK_IAR+1) #define PROCESS_STACK_CAUSE (PROCESS_STACK_IAR+2) #define PROCESS_STACK_FAULT (PROCESS_STACK_IAR+3) #define PROCESS_STACK_PTBASE (PROCESS_STACK_IAR+4) #define PROCESS_STACK_PTSIZE (PROCESS_STACK_IAR+5) #define PROCESS_STACK_PTBITS (PROCESS_STACK_IAR+6) #define PROCESS_STACK_PREV_FRAME 10 // points to previous interrupt frame #define PROCESS_STACK_FRAME_SIZE 85 // interrupt frame is 85 words #define SIZE_ARG_BUFF 1024 // Max number of characters in the // command-line arguments #define MAX_ARGS 128 // Max number of command-line // arguments extern PCB *currentPCB; extern int ProcessFork (VoidFunc, unsigned int, char *, int); extern void ProcessSchedule (); extern void ContextSwitch(void *, void *, int); extern void ProcessSuspend (PCB *); extern void ProcessWakeup (PCB *); extern void ProcessSetResult (PCB *, uint32); extern void ProcessSleep (); extern unsigned GetCurrentPid(); extern unsigned getpid(); void process_create(char *name, ...); #endif /* _process_h_ */
project2/src/producer.c
#include "lab2-api.h" typedef struct DB { //make sure this struct matches in all files char buffer[5]; int bufferValCount; } DB; int main(int argc, char *argv[]) { int i; DB *db; char hw[10] = {'h','e','l','l','o','w','o','r','l','d'}; uint32 handle; sem_t spage, sem1, sem2; lock_t lock; handle = dstrtol(argv[1], NULL, 10); spage = dstrtol(argv[2], NULL, 10); lock = dstrtol(argv[3], NULL, 10); sem1 = dstrtol( argv[4], NULL, 10);//blocks when buffer is full sem2 = dstrtol(argv[5], NULL, 10);//block when buffer is empty db = (DB*)shmat(handle); if(sem_signal(spage)) { Printf("Could not map virtual address to memory"); exit(); } for(i = 0; i < 10; i++) { lock_acquire(lock); Printf("\nproducer for loop\n"); db->buffer[db->bufferValCount] = hw[i];//add char to buffer db->bufferValCount++;//up buffer count lock_release(lock); sem_signal(sem1); sem_wait(sem2); } return 0; }
project2/src/queue.c
// // queue.c // // Variables for queues, and a routine to initialize the pool of links. // // #include "dlxos.h" #include "queue.h" Queue freeLinks; static Link linkpool[QUEUE_MAX_LINKS]; void QueueModuleInit () { int i; QueueInit (&freeLinks); for (i = 0; i < QUEUE_MAX_LINKS; i++) { dbprintf ('i', "Initializing queue link %d.\n", i); linkpool[i].next = NULL; QueueFreeLink (&(linkpool[i])); } } void QueueInit (Queue *q) { q->first = (Link *)q; q->last = (Link *)q; q->nitems = 0; } void QueueFreeLink (Link *l) { extern Queue freeLinks; // Make sure the link has already been freed! ASSERT ((l->next == NULL), "Link not empty"); QueueInsertLast (&freeLinks, l); } Link * QueueAllocLink () { extern Queue freeLinks; Link *l; if (! QueueEmpty (&freeLinks)) { l = QueueFirst (&freeLinks); QueueRemove (l); } else { l = NULL; } ASSERT ((l != NULL), "Link not allocated!"); return (l); }
project2/src/queue.h
// // queue.h // // Definitions for routines used to manage queues. The queues are // doubly-linked lists so it's easier to add and remove from them. // Also, the links themselves have pointers to objects so that objects // may be on more than one queue. // #ifndef _queue_h_ #define _queue_h_ #ifndef NULL #define NULL ((void *)0) #endif #define QUEUE_MAX_LINKS 400 typedef struct Link { struct Link *next; struct Link *prev; struct Queue *queue; void *object; } Link; typedef struct Queue { struct Link *first; struct Link *last; int nitems; } Queue; inline void QueueLinkInit (Link *l, void *obj) { l->next = NULL; l->object = obj; } inline Link * QueueNext (Link *l) { return (l->next); } inline Link * QueuePrev (Link *l) { return (l->prev); } inline Link * QueueFirst (Queue *q) { return (q->first); } inline Link * QueueLast (Queue *q) { return (q->last); } inline void QueueInsertAfter (Queue *q, Link *after, Link *l) { l->queue = q; l->prev = after; l->next = after->next; after->next = l; l->next->prev = l; q->nitems += 1; } inline void QueueInsertFirst (Queue *q, Link *l) { QueueInsertAfter (q, (Link *)q, l); } inline void QueueInsertLast (Queue *q, Link *l) { QueueInsertAfter (q, QueueLast(q), l); } inline void QueueRemove (Link *l) { if (l->queue->nitems > 0) { l->prev->next = l->next; l->next->prev = l->prev; l->queue->nitems -= 1; } l->next = NULL; } inline int QueueLength (Queue *q) { return (q->nitems); } inline int QueueEmpty (Queue *q) { return (QueueLength (q) == 0); } extern void QueueModuleInit (); extern void QueueFreeLink (Link *l); extern Link *QueueAllocLink (); extern void QueueInit (Queue *q); #endif // _queue_h_
project2/src/run.c
#include "lab2-api.h" typedef struct DB { char buffer[5]; int bufferValCount; } DB; int main (int argc, char* argv[]) { uint32 handle; int i, num_p, num_c; DB *db; sem_t sem1, sem2, spage; lock_t lock; char handle_str[10], spage_str[10], lock_str[10], sem1_str[10], sem2_str[10]; db-> bufferValCount = 0;//initialize buffer counter to 0 handle = shmget();//get handle to shared mem db = (DB*)shmat(handle);//get address of shared mem if(argc != 3) { exit(); } num_p = dstrtol(argv[1],NULL,10);//get # of producers from args num_c = dstrtol(argv[2],NULL,10);//get # consumers from args // Printf("p:%d,c:%d",num_p, num_c); if(db == NULL) { Printf("Could not map shared page to a virtual page."); exit(); } // for(i = 0; i < buffer_size; i++) { // db->buffer[i] = shared_buffer[i]; // } spage = sem_create(0); sem1 = sem_create(0);//^ when letter added, v when letter removed sem2 = sem_create(5);//v when letter added, ^ when letter removed lock = lock_create(); ditoa(handle, handle_str); ditoa(spage, spage_str); ditoa(lock, lock_str); ditoa(sem1, sem1_str); ditoa(sem2, sem2_str); Printf("\nrun.c before procs are made\n"); for(i = 0; i < num_p; i++){//make producer procs process_create("producer.dlx.obj", handle_str, spage_str, lock_str, sem1_str, sem2_str, NULL); } for(i = 0; i < num_c; i++) {//make consumer procs process_create("consumer.dlx.obj", handle_str, spage_str, lock_str, sem1_str, sem2_str, NULL); } Printf("\nrun.c after procs are made\n"); sem_wait(spage);//wait for someone (a producer) to grab the page return 0; }
project2/src/share_memory.api
; Compiled by GCC .text .align 2 .proc _isspace .global _isspace _isspace: ; Function 'isspace'; 0 bytes of locals, 1 regs to save. sw -4(r29),r30 ; push fp add r30,r0,r29 ; fp = sp sw -8(r29),r31 ; push ret addr subui r29,r29,#16 ; alloc local storage sw 0(r29),r2 lb r2,3(r30) addi r31,r0,#0 andi r1,r2,#0x00ff seqi r1,r1,#32 bnez r1,L6 nop ; not filled. addi r1,r2,#-9 andi r1,r1,#0x00ff sleui r1,r1,#1 beqz r1,L5 nop ; not filled. L6: addi r31,r0,#1 L5: add r1,r0,r31 lw r2,0(r29) lw r31,-8(r30) add r29,r0,r30 lw r30,-4(r30) jr r31 nop .endproc _isspace .align 2 .proc _isxdigit .global _isxdigit _isxdigit: ; Function 'isxdigit'; 0 bytes of locals, 1 regs to save. sw -4(r29),r30 ; push fp add r30,r0,r29 ; fp = sp sw -8(r29),r31 ; push ret addr subui r29,r29,#16 ; alloc local storage sw 0(r29),r2 lb r31,3(r30) addi r2,r0,#0 addi r1,r31,#-48 andi r1,r1,#0x00ff sleui r1,r1,#9 bnez r1,L12 nop ; not filled. addi r1,r31,#-97 andi r1,r1,#0x00ff sleui r1,r1,#5 bnez r1,L12 nop ; not filled. andi r1,r31,#0x00ff snei r1,r1,#65 bnez r1,L11 nop ; not filled. L12: addi r2,r0,#1 L11: add r1,r0,r2 lw r2,0(r29) lw r31,-8(r30) add r29,r0,r30 lw r30,-4(r30) jr r31 nop .endproc _isxdigit .align 2 .proc _DisableIntrs .global _DisableIntrs _DisableIntrs: ; Function 'DisableIntrs'; 0 bytes of locals, 1 regs to save. sw -4(r29),r30 ; push fp add r30,r0,r29 ; fp = sp sw -8(r29),r31 ; push ret addr subui r29,r29,#16 ; alloc local storage sw 0(r29),r2 addi r29,r29,#-8 addi r2,r0,#15 sw (r29),r2 jal _SetIntrs nop ; not filled. addi r29,r29,#8 lw r2,0(r29) lw r31,-8(r30) add r29,r0,r30 lw r30,-4(r30) jr r31 nop .endproc _DisableIntrs .align 2 .proc _EnableIntrs .global _EnableIntrs _EnableIntrs: ; Function 'EnableIntrs'; 0 bytes of locals, 1 regs to save. sw -4(r29),r30 ; push fp add r30,r0,r29 ; fp = sp sw -8(r29),r31 ; push ret addr subui r29,r29,#16 ; alloc local storage sw 0(r29),r2 addi r29,r29,#-8 addi r2,r0,#0 sw (r29),r2 jal _SetIntrs nop ; not filled. addi r29,r29,#8 lw r2,0(r29) lw r31,-8(r30) add r29,r0,r30 lw r30,-4(r30) jr r31 nop .endproc _EnableIntrs .align 2 .proc _RestoreIntrs .global _RestoreIntrs _RestoreIntrs: ; Function 'RestoreIntrs'; 0 bytes of locals, 1 regs to save. sw -4(r29),r30 ; push fp add r30,r0,r29 ; fp = sp sw -8(r29),r31 ; push ret addr subui r29,r29,#16 ; alloc local storage sw 0(r29),r2 addi r29,r29,#-8 lw r2,(r30) sw (r29),r2 jal _SetIntrs nop ; not filled. addi r29,r29,#8 lw r2,0(r29) lw r31,-8(r30) add r29,r0,r30 lw r30,-4(r30) jr r31 nop .endproc _RestoreIntrs .align 2 .proc _QueueLinkInit .global _QueueLinkInit _QueueLinkInit: ; Function 'QueueLinkInit'; 0 bytes of locals, 1 regs to save. sw -4(r29),r30 ; push fp add r30,r0,r29 ; fp = sp sw -8(r29),r31 ; push ret addr subui r29,r29,#16 ; alloc local storage sw 0(r29),r2 lw r1,(r30) lw r2,4(r30) addi r31,r0,#0 sw (r1),r31 sw 12(r1),r2 lw r2,0(r29) lw r31,-8(r30) add r29,r0,r30 lw r30,-4(r30) jr r31 nop .endproc _QueueLinkInit .align 2 .proc _QueueNext .global _QueueNext _QueueNext: ; Function 'QueueNext'; 0 bytes of locals, 0 regs to save. sw -4(r29),r30 ; push fp add r30,r0,r29 ; fp = sp sw -8(r29),r31 ; push ret addr subui r29,r29,#8 ; alloc local storage lw r1,(r30) lw r1,(r1) lw r31,-8(r30) add r29,r0,r30 lw r30,-4(r30) jr r31 nop .endproc _QueueNext .align 2 .proc _QueuePrev .global _QueuePrev _QueuePrev: ; Function 'QueuePrev'; 0 bytes of locals, 0 regs to save. sw -4(r29),r30 ; push fp add r30,r0,r29 ; fp = sp sw -8(r29),r31 ; push ret addr subui r29,r29,#8 ; alloc local storage lw r1,(r30) lw r1,4(r1) lw r31,-8(r30) add r29,r0,r30 lw r30,-4(r30) jr r31 nop .endproc _QueuePrev .align 2 .proc _QueueFirst .global _QueueFirst _QueueFirst: ; Function 'QueueFirst'; 0 bytes of locals, 0 regs to save. sw -4(r29),r30 ; push fp add r30,r0,r29 ; fp = sp sw -8(r29),r31 ; push ret addr subui r29,r29,#8 ; alloc local storage lw r1,(r30) lw r1,(r1) lw r31,-8(r30) add r29,r0,r30 lw r30,-4(r30) jr r31 nop .endproc _QueueFirst .align 2 .proc _QueueLast .global _QueueLast _QueueLast: ; Function 'QueueLast'; 0 bytes of locals, 0 regs to save. sw -4(r29),r30 ; push fp add r30,r0,r29 ; fp = sp sw -8(r29),r31 ; push ret addr subui r29,r29,#8 ; alloc local storage lw r1,(r30) lw r1,4(r1) lw r31,-8(r30) add r29,r0,r30 lw r30,-4(r30) jr r31 nop .endproc _QueueLast .align 2 .proc _QueueInsertAfter .global _QueueInsertAfter _QueueInsertAfter: ; Function 'QueueInsertAfter'; 0 bytes of locals, 2 regs to save. sw -4(r29),r30 ; push fp add r30,r0,r29 ; fp = sp sw -8(r29),r31 ; push ret addr subui r29,r29,#16 ; alloc local storage sw 0(r29),r2 sw 4(r29),r3 lw r3,(r30) lw r2,4(r30) lw r1,8(r30) sw 8(r1),r3 sw 4(r1),r2 lw r31,(r2) sw (r1),r31 sw (r2),r1 lw r2,(r1) sw 4(r2),r1 lw r1,8(r3) addi r1,r1,#1 sw 8(r3),r1 lw r2,0(r29) lw r3,4(r29) lw r31,-8(r30) add r29,r0,r30 lw r30,-4(r30) jr r31 nop .endproc _QueueInsertAfter .align 2 .proc _QueueInsertFirst .global _QueueInsertFirst _QueueInsertFirst: ; Function 'QueueInsertFirst'; 0 bytes of locals, 2 regs to save. sw -4(r29),r30 ; push fp add r30,r0,r29 ; fp = sp sw -8(r29),r31 ; push ret addr subui r29,r29,#16 ; alloc local storage sw 0(r29),r2 sw 4(r29),r3 lw r3,(r30) lw r1,4(r30) sw 8(r1),r3 sw 4(r1),r3 lw r31,(r3) sw (r1),r31 sw (r3),r1 lw r2,(r1) sw 4(r2),r1 lw r1,8(r3) addi r1,r1,#1 sw 8(r3),r1 lw r2,0(r29) lw r3,4(r29) lw r31,-8(r30) add r29,r0,r30 lw r30,-4(r30) jr r31 nop .endproc _QueueInsertFirst .align 2 .proc _QueueInsertLast .global _QueueInsertLast _QueueInsertLast: ; Function 'QueueInsertLast'; 0 bytes of locals, 2 regs to save. sw -4(r29),r30 ; push fp add r30,r0,r29 ; fp = sp sw -8(r29),r31 ; push ret addr subui r29,r29,#16 ; alloc local storage sw 0(r29),r2 sw 4(r29),r3 lw r3,(r30) lw r1,4(r30) lw r2,4(r3) sw 8(r1),r3 sw 4(r1),r2 lw r31,(r2) sw (r1),r31 sw (r2),r1 lw r2,(r1) sw 4(r2),r1 lw r1,8(r3) addi r1,r1,#1 sw 8(r3),r1 lw r2,0(r29) lw r3,4(r29) lw r31,-8(r30) add r29,r0,r30 lw r30,-4(r30) jr r31 nop .endproc _QueueInsertLast .align 2 .proc _QueueRemove .global _QueueRemove _QueueRemove: ; Function 'QueueRemove'; 0 bytes of locals, 2 regs to save. sw -4(r29),r30 ; push fp add r30,r0,r29 ; fp = sp sw -8(r29),r31 ; push ret addr subui r29,r29,#16 ; alloc local storage sw 0(r29),r2 sw 4(r29),r3 lw r31,(r30) lw r1,8(r31) lw r1,8(r1) sgti r1,r1,#0 beqz r1,L44 nop ; not filled. lw r1,4(r31) lw r3,(r31) sw (r1),r3 lw r1,(r31) lw r3,4(r31) sw 4(r1),r3 lw r2,8(r31) lw r1,8(r2) addi r1,r1,#-1 sw 8(r2),r1 L44: addi r3,r0,#0 sw (r31),r3 lw r2,0(r29) lw r3,4(r29) lw r31,-8(r30) add r29,r0,r30 lw r30,-4(r30) jr r31 nop .endproc _QueueRemove .align 2 .proc _QueueLength .global _QueueLength _QueueLength: ; Function 'QueueLength'; 0 bytes of locals, 0 regs to save. sw -4(r29),r30 ; push fp add r30,r0,r29 ; fp = sp sw -8(r29),r31 ; push ret addr subui r29,r29,#8 ; alloc local storage lw r1,(r30) lw r1,8(r1) lw r31,-8(r30) add r29,r0,r30 lw r30,-4(r30) jr r31 nop .endproc _QueueLength .align 2 .proc _QueueEmpty .global _QueueEmpty _QueueEmpty: ; Function 'QueueEmpty'; 0 bytes of locals, 0 regs to save. sw -4(r29),r30 ; push fp add r30,r0,r29 ; fp = sp sw -8(r29),r31 ; push ret addr subui r29,r29,#8 ; alloc local storage lw r1,(r30) lw r1,8(r1) seqi r1,r1,#0 lw r31,-8(r30) add r29,r0,r30 lw r30,-4(r30) jr r31 nop .endproc _QueueEmpty .data .align 2 LC0: .ascii "Entering ProcessModuleInit\n\000" .align 2 LC1: .ascii "Leaving ProcessModuleInit\n\000" .text .align 2 .proc _ShareModuleInit .global _ShareModuleInit _ShareModuleInit: ; Function 'ShareModuleInit'; 0 bytes of locals, 4 regs to save. sw -4(r29),r30 ; push fp add r30,r0,r29 ; fp = sp sw -8(r29),r31 ; push ret addr subui r29,r29,#24 ; alloc local storage sw 0(r29),r2 sw 4(r29),r3 sw 8(r29),r4 sw 12(r29),r5 addi r29,r29,#-8 lhi r2,((_debugstr)>>16)&0xffff addui r2,r2,(_debugstr)&0xffff sw (r29),r2 addi r5,r0,#112 sw 4(r29),r5 jal _dindex nop ; not filled. addi r29,r29,#8 snei r1,r1,#0 bnez r1,L64 nop ; not filled. addi r29,r29,#-8 sw (r29),r2 addi r5,r0,#43 sw 4(r29),r5 jal _dindex nop ; not filled. addi r29,r29,#8 snei r1,r1,#0 beqz r1,L63 nop ; not filled. L64: addi r29,r29,#-8 lhi r5,((LC0)>>16)&0xffff addui r5,r5,(LC0)&0xffff sw (r29),r5 jal _printf nop ; not filled. addi r29,r29,#8 L63: lhi r2,((_spages)>>16)&0xffff addui r2,r2,(_spages)&0xffff addi r4,r2,#248 L68: addi r5,r0,#0 sw (r2),r5 add r31,r0,r2 add r3,r0,r2 L72: addi r1,r31,#4 addi r5,r0,#0 sw (r1),r5 add r31,r0,r1 sle r1,r31,r3 bnez r1,L72 nop ; not filled. addi r2,r2,#8 sle r1,r2,r4 bnez r1,L68 nop ; not filled. addi r29,r29,#-8 lhi r2,((_debugstr)>>16)&0xffff addui r2,r2,(_debugstr)&0xffff sw (r29),r2 addi r5,r0,#112 sw 4(r29),r5 jal _dindex nop ; not filled. addi r29,r29,#8 snei r1,r1,#0 bnez r1,L76 nop ; not filled. addi r29,r29,#-8 sw (r29),r2 addi r5,r0,#43 sw 4(r29),r5 jal _dindex nop ; not filled. addi r29,r29,#8 snei r1,r1,#0 beqz r1,L75 nop ; not filled. L76: addi r29,r29,#-8 lhi r5,((LC1)>>16)&0xffff addui r5,r5,(LC1)&0xffff sw (r29),r5 jal _printf nop ; not filled. addi r29,r29,#8 L75: lw r2,0(r29) lw r3,4(r29) lw r4,8(r29) lw r5,12(r29) lw r31,-8(r30) add r29,r0,r30 lw r30,-4(r30) jr r31 nop .endproc _ShareModuleInit .data .align 2 LC2: .ascii "FATAL: couldn\'t allocate memory - no free pages!\n\000" .text .align 2 .proc _MemoryCreateSharedPage .global _MemoryCreateSharedPage _MemoryCreateSharedPage: ; Function 'MemoryCreateSharedPage'; 0 bytes of locals, 9 regs to save. sw -4(r29),r30 ; push fp add r30,r0,r29 ; fp = sp sw -8(r29),r31 ; push ret addr subui r29,r29,#48 ; alloc local storage sw 0(r29),r2 sw 4(r29),r3 sw 8(r29),r4 sw 12(r29),r5 sw 16(r29),r6 sw 20(r29),r7 sw 24(r29),r8 sw 28(r29),r9 sw 32(r29),r10 lw r6,(r30) addi r29,r29,#-8 sw (r29),r6 jal _findpid nop ; not filled. add r5,r0,r1 srli r8,r5,#0x5 slli r1,r8,#0x5 sub r5,r5,r1 addi r29,r29,#8 addi r29,r29,#-8 addi r10,r0,#15 sw (r29),r10 jal _SetIntrs nop ; not filled. add r7,r0,r1 addi r29,r29,#8 lw r1,160(r6) seqi r1,r1,#16 bnez r1,L92 nop ; not filled. addi r3,r0,#0 lhi r9,((_spages)>>16)&0xffff addui r9,r9,(_spages)&0xffff add r2,r0,r9 L84: lw r1,(r2) snei r1,r1,#0 beqz r1,L82 nop ; not filled. addi r2,r2,#8 addi r3,r3,#1 slei r1,r3,#31 bnez r1,L84 nop ; not filled. L82: seqi r1,r3,#32 beqz r1,L87 nop ; not filled. L92: addi r29,r29,#-8 sw (r29),r7 jal _SetIntrs nop ; not filled. addi r29,r29,#8 addi r1,r0,#0 j L91 nop ; not filled. L87: jal _MemoryAllocPage nop ; not filled. add r4,r0,r1 snei r1,r4,#0 bnez r1,L89 nop ; not filled. addi r29,r29,#-8 lhi r10,((LC2)>>16)&0xffff addui r10,r10,(LC2)&0xffff sw (r29),r10 jal _printf nop ; not filled. jal _exitsim nop ; not filled. addi r29,r29,#8 L89: slli r1,r3,#0x3 add r2,r1,r9 sw (r2),r4 slli r1,r8,#0x2 add r2,r1,r2 addi r1,r0,#1 sll r1,r1,r5 sw 4(r2),r1 addi r29,r29,#-8 sw (r29),r4 jal _MemorySetupPte nop ; not filled. lw r2,160(r6) slli r2,r2,#0x2 add r2,r2,r6 sw 96(r2),r1 lw r1,160(r6) addi r2,r1,#1 sw 160(r6),r2 lw r1,8(r6) addui r1,r1,#65164 sw 316(r1),r2 addi r29,r29,#8 addi r29,r29,#-8 sw (r29),r7 jal _SetIntrs nop ; not filled. addi r29,r29,#8 add r1,r0,r4 L91: lw r2,0(r29) lw r3,4(r29) lw r4,8(r29) lw r5,12(r29) lw r6,16(r29) lw r7,20(r29) lw r8,24(r29) lw r9,28(r29) lw r10,32(r29) lw r31,-8(r30) add r29,r0,r30 lw r30,-4(r30) jr r31 nop .endproc _MemoryCreateSharedPage .align 2 .proc _mmap .global _mmap _mmap: ; Function 'mmap'; 0 bytes of locals, 9 regs to save. sw -4(r29),r30 ; push fp add r30,r0,r29 ; fp = sp sw -8(r29),r31 ; push ret addr subui r29,r29,#48 ; alloc local storage sw 0(r29),r2 sw 4(r29),r3 sw 8(r29),r4 sw 12(r29),r5 sw 16(r29),r6 sw 20(r29),r7 sw 24(r29),r8 sw 28(r29),r9 sw 32(r29),r10 lw r6,(r30) lw r7,4(r30) snei r1,r7,#0 bnez r1,L94 nop ; not filled. addi r1,r0,#0 j L115 nop ; not filled. L94: lw r1,8(r6) addui r9,r1,#65164 addi r29,r29,#-8 addi r10,r0,#15 sw (r29),r10 jal _SetIntrs nop ; not filled. add r8,r0,r1 addi r29,r29,#8 addi r3,r0,#0 lhi r2,((_spages)>>16)&0xffff addui r2,r2,(_spages)&0xffff L99: lw r1,(r2) seq r1,r1,r7 bnez r1,L97 nop ; not filled. addi r2,r2,#8 addi r3,r3,#1 sleui r1,r3,#31 bnez r1,L99 nop ; not filled. L97: seqi r1,r3,#32 bnez r1,L116 nop ; not filled. slli r31,r7,#0x10 addi r5,r0,#0 lw r4,160(r6) slt r1,r5,r4 beqz r1,L105 nop ; not filled. add r2,r0,r6 L107: lw r1,96(r2) addi r10,r0,#-8 and r1,r1,r10 seq r1,r1,r31 bnez r1,L105 nop ; not filled. addi r2,r2,#4 addi r5,r5,#1 slt r1,r5,r4 bnez r1,L107 nop ; not filled. L105: lw r2,160(r6) seq r1,r5,r2 beqz r1,L110 nop ; not filled. slei r1,r2,#15 bnez r1,L111 nop ; not filled. L116: addi r29,r29,#-8 sw (r29),r8 jal _SetIntrs nop ; not filled. addi r29,r29,#8 addi r1,r0,#0 j L115 nop ; not filled. L111: addi r29,r29,#-8 sw (r29),r7 jal _MemorySetupPte nop ; not filled. lw r2,160(r6) slli r2,r2,#0x2 add r2,r2,r6 sw 96(r2),r1 lw r1,160(r6) addi r1,r1,#1 sw 160(r6),r1 sw 316(r9),r1 addi r29,r29,#8 L110: addi r29,r29,#-8 sw (r29),r6 jal _findpid nop ; not filled. add r2,r0,r1 srli r4,r2,#0x5 slli r1,r4,#0x5 sub r2,r2,r1 slli r3,r3,#0x3 slli r1,r4,#0x2 add r1,r3,r1 lhi r10,((_spages+4)>>16)&0xffff addui r10,r10,(_spages+4)&0xffff add r3,r1,r10 addi r1,r0,#1 sll r2,r1,r2 lw r1,(r3) or r1,r1,r2 sw (r3),r1 addi r29,r29,#8 addi r29,r29,#-8 sw (r29),r8 jal _SetIntrs nop ; not filled. addi r29,r29,#8 slli r1,r5,#0x10 L115: lw r2,0(r29) lw r3,4(r29) lw r4,8(r29) lw r5,12(r29) lw r6,16(r29) lw r7,20(r29) lw r8,24(r29) lw r9,28(r29) lw r10,32(r29) lw r31,-8(r30) add r29,r0,r30 lw r30,-4(r30) jr r31 nop .endproc _mmap .data .align 2 LC3: .ascii "FATAL: Attempted to free memory page 0\n\000" .text .align 2 .proc _MemoryFreeSharedPte .global _MemoryFreeSharedPte _MemoryFreeSharedPte: ; Function 'MemoryFreeSharedPte'; 0 bytes of locals, 9 regs to save. sw -4(r29),r30 ; push fp add r30,r0,r29 ; fp = sp sw -8(r29),r31 ; push ret addr subui r29,r29,#48 ; alloc local storage sw 0(r29),r2 sw 4(r29),r3 sw 8(r29),r4 sw 12(r29),r5 sw 16(r29),r6 sw 20(r29),r7 sw 24(r29),r8 sw 28(r29),r9 sw 32(r29),r10 lw r8,(r30) lw r2,4(r30) addi r29,r29,#-8 addi r10,r0,#15 sw (r29),r10 jal _SetIntrs nop ; not filled. add r9,r0,r1 addi r29,r29,#8 slli r1,r2,#0x2 add r1,r1,r8 lhu r7,96(r1) snei r1,r7,#0 bnez r1,L119 nop ; not filled. addi r29,r29,#-8 lhi r10,((LC3)>>16)&0xffff addui r10,r10,(LC3)&0xffff sw (r29),r10 jal _printf nop ; not filled. jal _exitsim nop ; not filled. addi r29,r29,#8 L119: addi r4,r0,#0 lhi r5,((_spages)>>16)&0xffff addui r5,r5,(_spages)&0xffff add r2,r0,r5 L123: lw r1,(r2) seq r1,r1,r7 bnez r1,L121 nop ; not filled. addi r2,r2,#8 addi r4,r4,#1 slei r1,r4,#31 bnez r1,L123 nop ; not filled. L121: seqi r1,r4,#32 beqz r1,L126 nop ; not filled. addi r29,r29,#-8 sw (r29),r9 jal _SetIntrs nop ; not filled. addi r29,r29,#8 addi r1,r0,#-1 j L141 nop ; not filled. L126: addi r29,r29,#-8 sw (r29),r8 jal _findpid nop ; not filled. add r2,r0,r1 srli r3,r1,#0x5 slli r1,r3,#0x5 sub r2,r2,r1 slli r1,r4,#0x3 add r4,r1,r5 slli r1,r3,#0x2 add r1,r1,r4 addi r3,r1,#4 addi r1,r0,#1 sll r1,r1,r2 addi r10,r0,#-1 xor r2,r1,r10 lw r1,(r3) and r1,r1,r2 sw (r3),r1 addi r29,r29,#8 addi r6,r0,#0 addi r5,r0,#0 L132: addi r3,r0,#1 addi r2,r0,#0 lw r31,4(r4) L136: and r1,r31,r3 snei r1,r1,#0 add r6,r6,r1 slli r3,r3,#0x1 addi r2,r2,#1 slei r1,r2,#31 bnez r1,L136 nop ; not filled. addi r4,r4,#4 addi r5,r5,#1 slei r1,r5,#0 bnez r1,L132 nop ; not filled. lw r1,160(r8) addi r2,r1,#-1 sw 160(r8),r2 lw r1,8(r8) addui r1,r1,#65164 sw 316(r1),r2 snei r1,r6,#0 bnez r1,L139 nop ; not filled. addi r29,r29,#-8 sw (r29),r7 jal _MemoryFreePage nop ; not filled. addi r29,r29,#8 L139: addi r29,r29,#-8 sw (r29),r9 jal _SetIntrs nop ; not filled. addi r29,r29,#8 add r1,r0,r6 L141: lw r2,0(r29) lw r3,4(r29) lw r4,8(r29) lw r5,12(r29) lw r6,16(r29) lw r7,20(r29) lw r8,24(r29) lw r9,28(r29) lw r10,32(r29) lw r31,-8(r30) add r29,r0,r30 lw r30,-4(r30) jr r31 nop .endproc _MemoryFreeSharedPte .data .align 2 _spages: .space 256
project2/src/share_memory.h
/*********************************************************************** * * share_memory.h * Copyright: Uday Savagaonkar (2002). All rights reserved. * Share memory API for dlxos * ***********************************************************************/ #ifndef _SHARE_MEMORY_H_ #define _SHARE_MEMORY_H_ #include "dlxos.h" #include "memory.h" #include "process.h" #define MEMORY_MAX_SHARED_PAGES 32 //Maximum number of pages that can be //shared amongst different processes #define PROCESS_MAX_PAGES 16 //Maximum number of pages allowed in Level //1 page table void SharedInitModule(); //Turns on the shared memory module uint32 MemoryCreateSharedPage(PCB *pcb); //Creates a shared page in the memory void *mmap(PCB *pcb, uint32 handle); //Maps a shared page to the virtual address //space int MemoryFreeSharedPage(PCB *pcb, uint32 handle); //Releases a shared page. The page is not //actually deallocated untill all the process //release it. #endif _SHARE_MEMORY_H_
project2/src/synch.c
// // synch.c // // Routines for synchronization // // #include "dlxos.h" #include "process.h" #include "synch.h" static Sem sems[MAX_SEMS];//All semaphores in the system static Lock locks[MAX_LOCKS];//All locks in the system extern struct PCB *currentPCB; //---------------------------------------------------------------------- // SynchModuleInit // // Initializes the synchronization primitives: the semaphores //---------------------------------------------------------------------- void SynchModuleInit() { int i; dbprintf ('p', "Entering SynchModuleInit\n"); for(i=0; i<MAX_SEMS; i++) { sems[i].inuse = 0; } for(i=0; i<MAX_LOCKS; i++) { locks[i].inuse = 0; } for(i=0; i<MAX_CONDS; i++) { // ignore this } dbprintf ('p', "Leaving SynchModuleInit\n"); } //--------------------------------------------------------------------- // // SemInit // // Initialize a semaphore to a particular value. This just means // initting the process queue and setting the counter. // //---------------------------------------------------------------------- void SemInit (Sem *sem, int count) { QueueInit (&sem->waiting); sem->count = count; } //---------------------------------------------------------------------- // 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 //---------------------------------------------------------------------- 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 INVALID_SEM; SemInit(&sems[sem], count); return sem; } � //---------------------------------------------------------------------- // // SemWait // // Wait on a semaphore. As described in Section 6.4 of _OSC_, // OR section 2.3 of Modern Operating Systems, // 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 SemHandleWait(sem_t sem) { if(sem>=0&&sem<MAX_SEMS) { if(sems[sem].inuse) { SemWait(&sems[sem]); return 0; } return 1; } else { return 1; } } � //---------------------------------------------------------------------- // // SemSignal // // Signal on a semaphore. Again, details are in Section 6.4 of // _OSC_ OR section 2.3 of Modern Operating Systems. // //---------------------------------------------------------------------- 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); } int SemHandleSignal(sem_t sem) { if(sem>=0&&sem<MAX_SEMS) { if(sems[sem].inuse) { SemSignal(&sems[sem]); return 0; } return 1; } else { return 1; } } � //---------------------------------------------------------------------- // // Your assignment is to implement locks and condition variables // using Mesa-style monitor synchronization. Fill in the details in // synch.c before making any changes here. // //---------------------------------------------------------------------- //----------------------------------------------------------------------- // 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() { uint32 intrval; lock_t lock; //Disable interupts intrval = DisableIntrs(); for(lock = 0; lock < MAX_LOCKS; lock++) { if (locks[lock].inuse == 0) { locks[lock].inuse = 1; break; } } //Re-enable interrupts EnableIntrs(intrval); if (lock == MAX_LOCKS) { return INVALID_LOCK; } LockInit(&locks[lock]); return lock; } void LockInit(Lock* lock) { QueueInit(&lock->waiting); lock->owner = NULL; } //--------------------------------------------------------------------------- // 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 a zero. Otherwise the routine returns a 1. // // Your implementation should be such that if a process that already owns // the lock calls LockHandleAcquire for that lock, it should not block. //--------------------------------------------------------------------------- int LockHandleAcquire(lock_t lock) { if (lock >=0 && lock < MAX_LOCKS) { if (locks[lock].inuse == 1) { LockAquire(&locks[lock]); return 0; } return 1; } return 1; } void LockAquire(Lock *lock) { Link *l; int intrs; //Disable interrupts intrs = DisableIntrs(); dbprintf('s', "Proc 0x%x aquiring lock 0x%x.\n", currentPCB, lock); if(lock->owner == NULL) { lock->owner = currentPCB; } else if (lock->owner != currentPCB) { l = QueueAllocLink(); QueueLinkInit (l, (void *)currentPCB); dbprintf('s', "Suspending current proc (0x%x).\n", currentPCB); QueueInsertLast(&lock->waiting, l); ProcessSleep(); } //Re-enable interrupts; RestoreIntrs(intrs); } //--------------------------------------------------------------------------- // 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 1. // If the lock is a valid lock, it should check whether the calling // process actually holds the lock. If not it returns 1. Otherwise it // releases the lock, and returns 0. //--------------------------------------------------------------------------- int LockHandleRelease(lock_t lock) { if (lock >=0 && lock < MAX_LOCKS) { if (locks[lock].inuse == 1 && locks[lock].owner == currentPCB) { LockRelease(&locks[lock]); return 0; } return 1; } return 1; } void LockRelease(Lock *lock) { Link *l; int intrs; //Disable interrupts intrs = DisableIntrs(); dbprintf('s', "Releasing lock 0x%x.\n", lock); lock -> owner = NULL; if(!QueueEmpty(&lock->waiting)) { l = QueueFirst(&lock->waiting); QueueRemove(l); dbprintf('s', "Waking up PCB 0x%x.\n", l->object); ProcessWakeup ((PCB *)(l->object)); QueueFreeLink(l); } RestoreIntrs(intrs); } //-------------------------------------------------------------------------- // 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) { // ignore this } //--------------------------------------------------------------------------- // 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 CondHandleWait(cond_t cond) { // ignore this } //--------------------------------------------------------------------------- // 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 CondHandleSignal(cond_t cond) { // ignore this } //--------------------------------------------------------------------------- // CondHandleBroadcast // // This function is very similar to CondHandleSignal. But instead of // waking only one process, it wakes up all the processes waiting on the // condition variable. For this call to succeed, the calling process must // have acquired the lock associated with the condition variable. This // function should be written in such a way that the calling process // should retain the lock even after call completion. // // 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 CondHandleBroadcast // must explicitly release the lock after the call completion. //--------------------------------------------------------------------------- int CondHandleBroadcast(cond_t cond) { // ignore this }
project2/src/synch.h
// // synch.h // // Include file for synchronization stuff. The synchronization // primitives include: // Semaphore // Lock // Condition // // Semaphores are the only "native" synchronization primitive. // Condition variables and locks are implemented using semaphores. // #ifndef _synch_h_ #define _synch_h_ #include "queue.h" #define MAX_SEMS 32 //Maximum 32 semaphores allowed in the system #define MAX_LOCKS 64 //Maximum 64 locks allowed in the system //This is because condition vars also use //locks from the same pool #define MAX_CONDS 32 //Maximum 32 conds allowed in the system typedef int sem_t; typedef int lock_t; typedef int cond_t; #define INVALID_SEM -1 #define INVALID_LOCK -1 #define INVALID_PROC -1 #define INVALID_COND -1 typedef struct Sem { Queue waiting; int count; uint32 inuse; //indicates whether the semaphore is being //used by any process } Sem; extern void SemInit (Sem *, int); extern void SemWait (Sem *); extern void SemSignal (Sem *); typedef struct Lock { Queue waiting; PCB *owner; uint32 inuse; } Lock; extern void LockInit(Lock*); extern void LockAquire(Lock*); extern void LockRelease(Lock*); typedef struct Cond { //ignore this } Cond; #endif //_synch_h_
project2/src/syscall.h
// // syscall.h // // This file declares all of the system calls available as builtins // in the simulator. // #ifndef _syscall_h_ #define _syscall_h_ extern int open(const char *, int); extern int read(int, char *, int); extern int write(int, char *, int); extern int lseek(int, int, int); extern int close(int); #endif // _syscall_h_
project2/src/sysproc.c
// // sysproc.c // // This file defines system processes run by the OS. It also // includes a routine to initialize system processes. #include "process.h" #include "synch.h" //---------------------------------------------------------------------- // // doSomething // // A useless little function that does some small stuff to test // process switching and the like. // //---------------------------------------------------------------------- static Sem mysems[10]; static void doSomething (int me) { int i; int j; int mygrp = me % 10; char buf[40]; char buf2[40]; if (me < 100) { SemInit (&(mysems[mygrp]), 1); for (i = 1; i <= 6; i++) { dstrcpy (buf, "Process #"); ditoa (i, buf2); dstrcat (buf, buf2); ProcessFork (&doSomething, i * me * 1000 + me, buf, 0); } } SemWait (&mysems[mygrp]); for (i = 0, j = 0; i < (me * 2); i++) { if ((i % 1000) == 0) { printf ("Running process %d (iteration %d)!\n", me, i); } j += (i & 3); } SemSignal (&mysems[mygrp]); } � //---------------------------------------------------------------------- // // chaseTail // // A little function that chases its tail, forking off other // processes before exiting. This can be used to test resource // allocation and reclamation. // //---------------------------------------------------------------------- static void chaseTail (int me) { char *str = "chasetail"; printf ("Chasing tail %d.\n", me); printf ("Open of (%s,0x%x) returns 0x%x.\n", str, me+0x2000, Open (str, me + 0x2000)); if (me < 40) { ProcessFork (&chaseTail, me + 1, "ChaseTail", 0); } } //---------------------------------------------------------------------- // // emptyLoop // // This is an empty loop. It's used to test running code. A similar // loop could be used as the "idle" process. // //---------------------------------------------------------------------- static void emptyLoop (int me) { int i; int limit = me * 50000; i = 0; while (1) { i += 1; if (i > limit) { i = 0; printf ("%d ", me); } } } � //---------------------------------------------------------------------- // // randomStuff // // This routine tests random number usage in DLXOS. // //---------------------------------------------------------------------- static void randomStuff (int seed) { int i,rn; extern int random(),srandom(); srandom (seed); for (i = 0; i < 40; i++) { rn = random (); printf ("Random number %02i is 0x%08x.\n", i, rn); } } //---------------------------------------------------------------------- // // SysprocCreateProcesses // // This is the routine that is called by main to create processes. // It must create at least one process, and there must always be // at least one runnable process. If there isn't, the OS will // exit. // //---------------------------------------------------------------------- void SysprocCreateProcesses () { int i; for (i = 0; i < 10; i++) { SemInit (&mysems[i], 1); } // ProcessFork (&emptyLoop, 1, "Loop1", 0); // ProcessFork (&emptyLoop, 2, "Loop1", 0); // ProcessFork (&emptyLoop, 3, "Loop1", 0); // ProcessFork (&doSomething, 1, "Creator1", 0); // ProcessFork (&doSomething, 2, "Creator2", 0); // ProcessFork (&randomStuff, 1234, "randomStuff", 0); // ProcessFork (&chaseTail, 10, "ChaseTail", 0); }
project2/src/traps.c
// // traps.c // // This file handles the low-level trap stuff for an operating system // running under DLX. static char rcsid[] = "$Id: traps.c,v 1.1 2000/09/20 01:50:19 elm Exp elm $"; #include "dlx.h" #include "dlxos.h" #include "traps.h" #include "process.h" //---------------------------------------------------------------------- // // TimerSet // // Set the timer to go off after a particular number of microseconds. // The number of microseconds is passed to the routine. // //---------------------------------------------------------------------- void TimerSet (int us) { *((int *)DLX_TIMER_ADDRESS) = us; } � //---------------------------------------------------------------------- // // KbdModuleInit // // Initialize the keyboard module. This involves turning on // interrupts for the keyboard. // //---------------------------------------------------------------------- void KbdModuleInit () { *((uint32 *)DLX_KBD_INTR) = 1; } //-------------------------------------------------------------------- // GetUintFromTrapArg(uint32 *trapArgs, int sysmode) //-------------------------------------------------------------------- static uint32 GetUintFromTrapArg(uint32 *trapArgs, int sysmode) { uint32 arg; if(!sysmode) { MemoryCopyUserToSystem (currentPCB, trapArgs, &arg, sizeof (arg)); } else { bcopy (trapArgs, &arg, sizeof (arg)); } return arg; } //-------------------------------------------------------------------- // GetIntFromTrapArg(uint32 *trapArgs, int sysmode) //-------------------------------------------------------------------- static int GetIntFromTrapArg(uint32 *trapArgs, int sysmode) { int arg; if(!sysmode) { MemoryCopyUserToSystem (currentPCB, trapArgs, &arg, sizeof (arg)); } else { bcopy (trapArgs, &arg, sizeof (arg)); } return arg; } //-------------------------------------------------------------------- // Here we support reading the arguments // Maximum 10 command-line arguments are allowed // Also the total length of the arguments including the terminating // '\0' should be less than or equal to 100 //-------------------------------------------------------------------- static void TrapProcessCreateHandler(uint32 *trapArgs, int sysmode) { char allargs[SIZE_ARG_BUFF]; char name[100]; int i=0, j=0, k=0; uint32 args[MAX_ARGS]; char *destination; // The first argument is the name of the executable file i=0; for(i=0;i<100; i++) allargs[i] = 0; i=0; if(!sysmode) { //Get the arguments into the sytem space MemoryCopyUserToSystem (currentPCB, trapArgs, args, sizeof (args)); do { MemoryCopyUserToSystem (currentPCB,((char*)args[0])+i,name+i,1); i++; } while ((i < sizeof (name)) && (name[i-1] != '\0')); } else { bcopy (trapArgs, args, sizeof (args)); dstrncpy ((char *)args[0], name, sizeof (name)); } name[sizeof(name)-1] = '\0'; // null terminate the name i=0; if(!sysmode) { //Copy the rest of the arguments to the system space for(j=0; (j<11)&&(args[j]!=0); j++) { k=0; do { MemoryCopyUserToSystem (currentPCB,((char*)args[j])+k,allargs+i,1); i++; k++; } while ((i<sizeof(allargs)) && (allargs[i-1]!='\0')); } } else { destination = &allargs[0]; for(j=0; (j<11)&&(args[j]!=0); j++) { k = dstrlen((char *)args[j]); //length of the argument if(&destination[k]-allargs>100) { printf("Fatal: Cumulative length of all arguments > 100\n"); exitsim(); } dstrcpy(destination, (char *)args[j]); destination[k] = '\0'; } } allargs[sizeof(allargs)-1] = '\0'; // null terminate the name ProcessFork(0, (uint32)allargs, name, 1); } � //---------------------------------------------------------------------- // // TrapPrintfHandler // // Handle a printf trap.here. This printf code will correctly // handle integers but will not correctly handle strings (yet). // Also, note that the maximum format string length is 79 characters. // Exceeding this length will cause the format to be truncated. // //---------------------------------------------------------------------- static void TrapPrintfHandler (uint32 *trapArgs, int sysMode) { char formatstr[80]; int i = 0; uint32 printfArgs[10]; uint32 args[10]; int nargs = 0; char *c; // The first argument is the print format string. Copy it to system // space, truncating if necessary. i = 0; if (!sysMode) { // Get the arguments themselves into system space MemoryCopyUserToSystem (currentPCB, trapArgs, args, sizeof (args)); do { MemoryCopyUserToSystem (currentPCB,((char*)args[0])+i,formatstr+i,1); i++; } while ((i < sizeof (formatstr)) && (formatstr[i-1] != '\0')); } else { bcopy (trapArgs, args, sizeof (args)); dstrncpy ((char *)args[0], formatstr, sizeof (formatstr)); } formatstr[sizeof(formatstr)-1] = '\0'; // null terminate the fmt str for (c = formatstr; *c != '\0'; c++) { if (*c == '%') { // if this is a %%, skip past second % if (*(c+1) == '%') { c++; continue; } // Get the current argument off the stack printfArgs[nargs] = args[nargs+1]; // dbprintf ('t', "Argument %d at 0x%x is %d (0x%x).\n", nargs, // args[nargs], args[nargs]); while (1) { c++; if (*c == 's') { // Handle strings here. They don't work for user programs (yet...) break; } else if (*c == 'l') { continue; } else if ((*c == 'f') || (*c == 'g') || (*c == 'e')) { // If it's a floating point number, it'll be passed as // a double, so grab the second word also. nargs += 1; printfArgs[nargs] = args[nargs+1]; break; } else if ((*c >= 'a') && (*c <= 'z')) { // If it's another formatting character, it's not // a string, but we can leave the loop anyway. break; } } nargs += 1; } } printf (formatstr,printfArgs[0],printfArgs[1],printfArgs[2], printfArgs[3], printfArgs[4], printfArgs[5], printfArgs[6], printfArgs[7]); } � //---------------------------------------------------------------------- // // 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, 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", 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_SHARE_CREATE_PAGE: handle = MemoryCreateSharedPage(currentPCB); ProcessSetResult(currentPCB, handle); break; case TRAP_SHARE_MAP_PAGE: handle = GetUintFromTrapArg(trapArgs, isr & DLX_STATUS_SYSMODE); handle = (uint32)mmap(currentPCB, handle); ProcessSetResult(currentPCB, handle); 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; 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 (); }
project2/src/traps.h
// // dlxtraps.h // // Traps used by the DLX simulator. These traps have to be handled by the // operating system. // // Copyright 1999 by Ethan L. Miller // University of Maryland Baltimore County // // $Id: traps.h,v 1.1 2000/09/20 01:50:19 elm Exp elm $ // #ifndef _dlxtraps_h_ #define _dlxtraps_h_ #define TRAP_ILLEGALINST 0x1 // Illegal instruction #define TRAP_ADDRESS 0x2 // Bad address #define TRAP_ACCESS 0x3 // Attempted to access illegal memory #define TRAP_OVERFLOW 0x4 // Math overflow #define TRAP_DIV0 0x5 // Divide by 0 #define TRAP_PRIVILEGE 0x6 // Instruction must be executed as sys #define TRAP_FORMAT 0x7 // Instruction is malformed #define TRAP_PAGEFAULT 0x20 #define TRAP_TLBFAULT 0x30 #define TRAP_TIMER 0x40 // timer interrupt #define TRAP_KBD 0x48 // keyboard interrupt // This bit is set in CAUSE if the interrupt was a trap instruction #define TRAP_TRAP_INSTR 0x08000000 // The following traps are all defined in the basic traps library. To // call TRAP_READ, use a Read() call from a user-level program. This applies // to every trap except EXIT, which is called via exit() [no uppercase]. #define TRAP_PRINTF 0x201 #define TRAP_READ 0x210 #define TRAP_WRITE 0x211 #define TRAP_LSEEK 0x212 #define TRAP_SEEK TRAP_LSEEK // SEEK and LSEEK are synonyms #define TRAP_OPEN 0x213 #define TRAP_CLOSE 0x214 #define TRAP_DELETE 0x580 #define TRAP_EXIT 0x300 // Following are user-defined traps. Traps should be in the range // 0x400 - 0xfff #define TRAP_CONTEXT_SWITCH 0x400 //#define TRAP_SEM_WAIT 0x401 //#define TRAP_SEM_SIGNAL 0x402 #define TRAP_PROCESS_SLEEP 0x410 #define TRAP_PROCESS_WAKEUP 0x420 #define TRAP_PROCESS_FORK 0x430 #define TRAP_PROCESS_GETPID 0x431 #define TRAP_PROCESS_CREATE 0x432 #define TRAP_SHARE_CREATE_PAGE 0x440 #define TRAP_SHARE_MAP_PAGE 0x441 #define TRAP_SEM_CREATE 0x450 #define TRAP_SEM_WAIT 0x451 #define TRAP_SEM_SIGNAL 0x452 #define TRAP_LOCK_CREATE 0x453 #define TRAP_LOCK_ACQUIRE 0x454 #define TRAP_LOCK_RELEASE 0x455 #define TRAP_COND_CREATE 0x456 #define TRAP_COND_WAIT 0x457 #define TRAP_COND_SIGNAL 0x458 #define TRAP_COND_BROADCAST 0x459 // The following are special I/O addresses for DLX. #define DLX_TIMER_ADDRESS 0xfff00010 #define DLX_KBD_PUTCHAR 0xfff00100 #define DLX_KBD_NCHARSOUT 0xfff00120 #define DLX_KBD_GETCHAR 0xfff00180 #define DLX_KBD_NCHARSIN 0xfff001a0 #define DLX_KBD_INTR 0xfff001c0 #define TRAP_STACK_SIZE 0x800 // interrupt stack is 2K words #endif /* _dlxtraps_h_ */
project2/src/trap_random.s
; ; Stub functions for DLX traps. ; ; Aaron Sawdey 1996; released to the Public Domain. ; .text .align 2 .proc _random .global _random _random: trap #0x2020 jr r31 nop .endproc _random .proc _srandom .global _srandom _srandom: trap #0x2021 jr r31 nop .endproc _srandom
project2/src/userprog.c
#include "lab2-api.h" typedef struct DB { int some_int; char some_char; } DB; main (int argc, char *argv[]) { int number, i; DB *db; uint32 handle; sem_t spage; sem_t semaphore; char handle_str[10], spage_str[10], semaphore_str[10]; switch(argc) { case 2: number = dstrtol(argv[1], NULL, 10); Printf("Setting number = %d\n", number); break; default: Printf("Usage: "); Printf(argv[0]); Printf(" number\n"); exit(); } handle = shmget(); //Get a shared memory page db = (DB *)shmat(handle); if(db==NULL) { Printf("Could not map the shared page to virtual address, exiting..\n"); exit(); } db->some_int = number; db->some_char = 'A'; spage = sem_create(0); //Get a semaphore to wait till this memory //page is mapped to some other process's //virtual address space semaphore = sem_create(5); ditoa(handle, handle_str); //Convert the shared page handle to a string ditoa(spage, spage_str); //Convert the semaphore spage to a string ditoa(semaphore, semaphore_str); //Convert the semaphore to a string for(i=0; i<number; i++) process_create("userprog2.dlx.obj", handle_str, spage_str, semaphore_str, NULL); sem_wait(spage); //Wait till at least one process grabs the //shared memory page. Otherwise the page could //be released before anyone grabs it! }
project2/src/userprog2.c
#include "lab2-api.h" typedef struct DB { int some_int; char some_char; } DB; main (int argc, char *argv[]) { int i; DB *db; uint32 handle; //Handle of the shared page sem_t spage, semaphore; //Various semaphores if(argc!=4) { Printf("Usage: "); Printf(argv[0]); Printf(" handle_str spage_str semaphore_str\n"); exit(); } handle = dstrtol(argv[1], NULL, 10); //Get the handle from the arguments spage = dstrtol(argv[2], NULL, 10); //Get semaphore spage semaphore = dstrtol(argv[2], NULL, 10); //Get semaphore db = (DB *)shmat(handle); //Map the shared page to address spac if(db == NULL) { Printf("Could not map the virtual address to the memory, exiting...\n"); exit(); } //Now let us wake up the calling process so that it can quit if(sem_signal(spage)) { Printf("Bad semaphore spage.... Exiting!\n"); exit(); } //Now let us print the message Printf("This is one of the %d instances you created\n", db->some_int); Printf("The shared character is %c\n", db->some_char); Printf("My PID is %d\n", getpid()); }
project2/src/userprog3.c
#include "lab2-api.h" int main(int argc, char* argv[]) { int number, i; int *count; uint32 handle; lock_t lock; sem_t spage; char handle_str[10], spage_str[10], lock_str[10]; switch(argc) { case 2: number = dstrtol(argv[1], NULL, 10); Printf("Setting number = %d\n", number); break; default: Printf("Usage: "); Printf(argv[0]); Printf(" number\n"); exit(); } handle = shmget(); count = (int *)shmat(handle); if(count == NULL) { Printf("Could not map the shared page to virtual address, exiting..\n"); exit(); } *count = 0; spage = sem_create(0); lock = lock_create(); if(lock < 0) { Printf("Lock handle is bad, exiting...\n"); } ditoa(handle, handle_str); ditoa(spage, spage_str); ditoa(lock, lock_str); for(i=0; i<number; i++) { process_create("userprog4.dlx.obj", handle_str, spage_str, lock_str, NULL); } sem_wait(spage); return 0; }
project2/src/userprog4.c
#include "lab2-api.h" int main(int argc, char *argv[]) { int i; int *count; uint32 handle; sem_t spage; lock_t lock; if(argc!=4) { Printf("Usage: "); Printf(argv[0]); Printf(" handle_str spage_str lock_str\n"); exit(); } handle = dstrtol(argv[1], NULL, 10); spage = dstrtol(argv[2], NULL, 10); lock = dstrtol(argv[3], NULL, 10); count = (int*)shmat(handle); if (count == NULL) { Printf("Could not map the virtual address to the memory, exiting...\n"); exit(); } if (sem_signal(spage)) { Printf("Bad semaphore spage... Exiting!\n"); exit(); } //If this goes well, should print 1 -> number in order lock_acquire(&lock); //Get the lock (*count)++; //Increment the count Printf("%d\n", *count); //Print the count lock_release(&lock); //Release the lock return 0; }
project2/src/usertraps.s
;;; ;;; Stub functions for DLX traps. ;;; ;;; Ethan L. Miller, 1999. Released to the public domain. ;;; ;;; The traps such as Open and Close ;;; (note the capital letters) are for use by user programs. The traps ;;; with names such as open and close (lower case) are for use by the OS. ;;; .text .align 2 ;;; The following are the traps to be used by user-level programs ;;; .text .align 2 .proc _Open .global _Open _Open: ;;; Note that trap #0x213 actually causes a trap vector in the OS of ;;; 0x1213. The same is true for other user traps. Thus, a user trap ;;; with trap #0x240 will result in a trap vector of 0x1240 in the OS. trap #0x213 jr r31 nop .endproc _Open .proc _Close .global _Close _Close: trap #0x214 jr r31 nop .endproc _Close .proc _Read .global _Read _Read: trap #0x210 jr r31 nop .endproc _Read .proc _Write .global _Write _Write: trap #0x211 jr r31 nop .endproc _Write .proc _Lseek .global _Lseek _Lseek: trap #0x212 jr r31 nop .endproc _Lseek .proc _Putchar .global _Putchar _Putchar: trap #0x280 jr r31 nop .endproc _Putchar .proc _Printf .global _Printf _Printf: trap #0x201 jr r31 nop .endproc _Printf .proc _getpid .global _getpid _getpid: trap #0x431 jr r31 nop .endproc _getpid .proc _process_create .global _process_create _process_create: trap #0x432 jr r31 nop .endproc _process_create .proc _shmget .global _shmget _shmget: trap #0x440 jr r31 nop .endproc _shmget .proc _shmat .global _shmat _shmat: trap #0x441 jr r31 nop .endproc _shmat .proc _sem_create .global _sem_create _sem_create: trap #0x450 jr r31 nop .endproc _sem_create .proc _sem_wait .global _sem_wait _sem_wait: trap #0x451 jr r31 nop .endproc _sem_wait .proc _sem_signal .global _sem_signal _sem_signal: trap #0x452 jr r31 nop .endproc _sem_signal .proc _lock_create .global _lock_create _lock_create: trap #0x453 jr r31 nop .endproc _lock_create .proc _lock_acquire .global _lock_acquire _lock_acquire: trap #0x454 jr r31 nop .endproc _lock_acquire .proc _lock_release .global _lock_release _lock_release: trap #0x455 jr r31 nop .endproc _lock_release .proc _cond_create .global _cond_create _cond_create: trap #0x456 jr r31 nop .endproc _cond_create .proc _cond_wait .global _cond_wait _cond_wait: trap #0x457 jr r31 nop .endproc _cond_wait .proc _cond_signal .global _cond_signal _cond_signal: trap #0x458 jr r31 nop .endproc _cond_signal .proc _cond_broadcast .global _cond_broadcast _cond_broadcast: trap #0x459 jr r31 nop .endproc _cond_broadcast