diff options
Diffstat (limited to 'kernel/old/syscalls.c')
-rw-r--r-- | kernel/old/syscalls.c | 829 |
1 files changed, 0 insertions, 829 deletions
diff --git a/kernel/old/syscalls.c b/kernel/old/syscalls.c deleted file mode 100644 index 7176cda..0000000 --- a/kernel/old/syscalls.c +++ /dev/null @@ -1,829 +0,0 @@ -/** -** @file syscalls.c -** -** @author CSCI-452 class of 20245 -** -** @brief System call implementations -*/ - -#define KERNEL_SRC - -#include <common.h> - -#include <cio.h> -#include <clock.h> -#include <procs.h> -#include <sio.h> -#include <syscalls.h> -#include <user.h> -#include <vm.h> -#include <x86/pic.h> - -/* -** PRIVATE DEFINITIONS -*/ - -/* -** Macros to simplify tracing a bit -** -** TRACING_SYSCALLS and TRACING_SYSRETS are defined in debug.h, -** controlled by the TRACE ** macro. If not tracing these, SYSCALL_ENTER -** is a no-op, and SYSCALL_EXIT just does a return. -*/ - -#if TRACING_SYSCALLS - -#define SYSCALL_ENTER(x) do { \ - cio_printf( "--> %s, pid %08x", __func__, (uint32_t) (x) ); \ - } while(0) - -#else - -#define SYSCALL_ENTER(x) /* */ - -#endif /* TRACING_SYSCALLS */ - -#if TRACING_SYSRETS - -#define SYSCALL_EXIT(x) do { \ - cio_printf( "<-- %s %08x\n", __func__, (uint32_t) (x) ); \ - return; \ - } while(0) - -#else - -#define SYSCALL_EXIT(x) return - -#endif /* TRACING_SYSRETS */ - -/* -** PRIVATE DATA TYPES -*/ - -/* -** PUBLIC GLOBAL VARIABLES -*/ - -/* -** IMPLEMENTATION FUNCTIONS -*/ - -// a macro to simplify syscall entry point specification -// we don't declare these static because we may want to call -// some of them from other parts of the kernel -#define SYSIMPL(x) void sys_##x( pcb_t * pcb ) - -/* -** Second-level syscall handlers -** -** All have this prototype: -** -** static void sys_NAME( pcb_t *pcb ); -** -** where the parameter 'pcb' is a pointer to the PCB of the process -** making the system call. -** -** Values being returned to the user are placed into the EAX -** field in the context save area for that process. -*/ - -/** -** sys_exit - terminate the calling process -** -** Implements: -** void exit( int32_t status ); -** -** Does not return -*/ -SYSIMPL(exit) { - - // sanity check - assert( pcb != NULL ); - - SYSCALL_ENTER( pcb->pid ); - - // retrieve the exit status of this process - pcb->exit_status = (int32_t) ARG(pcb,1); - - // now, we need to do the following: - // reparent any children of this process and wake up init if need be - // find this process' parent and wake it up if it's waiting - - pcb_zombify( pcb ); - - // pick a new winner - dispatch(); - - SYSCALL_EXIT( 0 ); -} - -/** -** sys_waitpid - wait for a child process to terminate -** -** Implements: -** int waitpid( uint_t pid, int32_t *status ); -** -** Blocks the calling process until the specified child (or any child) -** of the caller terminates. Intrinsic return is the PID of the child that -** terminated, or an error code; on success, returns the child's termination -** status via 'status' if that pointer is non-NULL. -*/ -SYSIMPL(waitpid) { - - // sanity check - assert( pcb != NULL ); - - SYSCALL_ENTER( pcb->pid ); - - /* - ** We need to do two things here: (1) find out whether or - ** not this process has any children in the system, and (2) - ** find out whether the desired child (or any child, if the - ** target PID is 0) has terminated. - ** - ** To do this, we loop until we find a the requested PID or - ** a Zombie child process, or have gone through all of the - ** slots in the process table. - ** - ** If the target PID is 0, we don't care which child process - ** we reap here; there could be several, but we only need to - ** find one. - */ - - // verify that we aren't looking for ourselves! - uint_t target = ARG(pcb,1); - - if( target == pcb->pid ) { - RET(pcb) = E_BAD_PARAM; - SYSCALL_EXIT( E_BAD_PARAM ); - } - - // Good. Now, figure out what we're looking for. - - pcb_t *child = NULL; - - if( target != 0 ) { - - // we're looking for a specific child - child = pcb_find_pid( target ); - - if( child != NULL ) { - - // found the process; is it one of our children: - if( child->parent != pcb ) { - // NO, so we can't wait for it - RET(pcb) = E_BAD_PARAM; - SYSCALL_EXIT( E_BAD_PARAM ); - } - - // yes! is this one ready to be collected? - if( child->state != STATE_ZOMBIE ) { - // no, so we'll have to block for now - child = NULL; - } - - } else { - - // no such child - RET(pcb) = E_BAD_PARAM; - SYSCALL_EXIT( E_BAD_PARAM ); - - } - - } else { - - // looking for any child - - // we need to find a process that is our child - // and has already exited - - child = NULL; - bool_t found = false; - - // unfortunately, we can't stop at the first child, - // so we need to do the iteration ourselves - register pcb_t *curr = ptable; - - for( int i = 0; i < N_PROCS; ++i, ++curr ) { - - if( curr->parent == pcb ) { - - // found one! - found = true; - - // has it already exited? - if( curr->state == STATE_ZOMBIE ) { - // yes, so we're done here - child = curr; - break; - } - } - } - - if( !found ) { - // got through the loop without finding a child! - RET(pcb) = E_NO_CHILDREN; - SYSCALL_EXIT( E_NO_CHILDREN ); - } - - } - - /* - ** At this point, one of these situations is true: - ** - ** * we are looking for a specific child and found it - ** * we are looking for any child and found one - ** - ** Either way, 'child' will be non-NULL if the selected - ** process has already become a Zombie. If that's the - ** case, we collect its status and clean it up; otherwise, - ** we block this process. - */ - - // did we find one to collect? - if( child == NULL ) { - - // no - mark the parent as "Waiting" - pcb->state = STATE_WAITING; - assert( pcb_queue_insert(waiting,pcb) == SUCCESS ); - - // select a new current process - dispatch(); - SYSCALL_EXIT( (uint32_t) current ); - } - - // found a Zombie; collect its information and clean it up - RET(pcb) = child->pid; - - // get "status" pointer from parent - int32_t *stat = (int32_t *) ARG(pcb,2); - - // if stat is NULL, the parent doesn't want the status - if( stat != NULL ) { - // ******************************************************** - // ** Potential VM issue here! This code assigns the exit - // ** status into a variable in the parent's address space. - // ** This works in the baseline because we aren't using - // ** any type of memory protection. If address space - // ** separation is implemented, this code will very likely - // ** STOP WORKING, and will need to be fixed. - // ******************************************************** - *stat = child->exit_status; - } - - // clean up the child - pcb_cleanup( child ); - - SYSCALL_EXIT( RET(pcb) ); -} - -/** -** sys_fork - create a new process -** -** Implements: -** int fork( void ); -** -** Creates a new process that is a duplicate of the calling process. -** Returns the child's PID to the parent, and 0 to the child, on success; -** else, returns an error code to the parent. -*/ -SYSIMPL(fork) { - - // sanity check - assert( pcb != NULL ); - - SYSCALL_ENTER( pcb->pid ); - - // Make sure there's room for another process! - pcb_t *new; - if( pcb_alloc(&new) != SUCCESS || new == NULL ) { - RET(pcb) = E_NO_PROCS; - SYSCALL_EXIT( RET(pcb) ); - } - - // duplicate the memory image of the parent - int status = user_duplicate( new, pcb ); - if( status != SUCCESS ) { - pcb_free( new ); - RET(pcb) = status; - SYSCALL_EXIT( status ); - } - - // Set the child's identity. - new->pid = next_pid++; - new->parent = pcb; - new->state = STATE_NEW; - - // replicate other things inherited from the parent - new->priority = pcb->priority; - - // Set the return values for the two processes. - RET(pcb) = new->pid; - RET(new) = 0; - - // Schedule the child, and let the parent continue. - schedule( new ); - - SYSCALL_EXIT( new->pid ); -} - -/** -** sys_exec - replace the memory image of a process -** -** Implements: -** void exec( uint_t what, char **args ); -** -** Replaces the memory image of the calling process with that of the -** indicated program. -** -** Returns only on failure. -*/ -SYSIMPL(exec) -{ - // sanity check - assert( pcb != NULL ); - - uint_t what = ARG(pcb,1); - const char **args = (const char **) ARG(pcb,2); - - SYSCALL_ENTER( pcb->pid ); - - // locate the requested program - prog_t *prog = user_locate( what ); - if( prog == NULL ) { - RET(pcb) = E_NOT_FOUND; - SYSCALL_EXIT( E_NOT_FOUND ); - } - - // we have located the program, but before we can load it, - // we need to clean up the existing VM hierarchy - vm_free( pcb->pdir ); - pcb->pdir = NULL; - - // "load" it and set up the VM tables for this process - int status = user_load( prog, pcb, args ); - if( status != SUCCESS ) { - RET(pcb) = status; - SYSCALL_EXIT( status ); - } - - /* - ** Decision: - ** (A) schedule this process and dispatch another, - ** (B) let this one continue in its current time slice - ** (C) reset this one's time slice and let it continue - ** - ** We choose option A. - ** - ** If scheduling the process fails, the exec() has failed. However, - ** all trace of the old process is gone by now, so we can't return - ** an error status to it. - */ - - schedule( pcb ); - - dispatch(); -} - -/** -** sys_read - read into a buffer from an input channel -** -** Implements: -** int read( uint_t chan, void *buffer, uint_t length ); -** -** Reads up to 'length' bytes from 'chan' into 'buffer'. Returns the -** count of bytes actually transferred. -*/ -SYSIMPL(read) { - - // sanity check - assert( pcb != NULL ); - - SYSCALL_ENTER( pcb->pid ); - - // grab the arguments - uint_t chan = ARG(pcb,1); - char *buf = (char *) ARG(pcb,2); - uint_t len = ARG(pcb,3); - - // if the buffer is of length 0, we're done! - if( len == 0 ) { - RET(pcb) = 0; - SYSCALL_EXIT( 0 ); - } - - // try to get the next character(s) - int n = 0; - - if( chan == CHAN_CIO ) { - - // console input is non-blocking - if( cio_input_queue() < 1 ) { - RET(pcb) = 0; - SYSCALL_EXIT( 0 ); - } - // at least one character - n = cio_gets( buf, len ); - RET(pcb) = n; - SYSCALL_EXIT( n ); - - } else if( chan == CHAN_SIO ) { - - // SIO input is blocking, so if there are no characters - // available, we'll block this process - n = sio_read( buf, len ); - RET(pcb) = n; - SYSCALL_EXIT( n ); - - } - - // bad channel code - RET(pcb) = E_BAD_PARAM; - SYSCALL_EXIT( E_BAD_PARAM ); -} - -/** -** sys_write - write from a buffer to an output channel -** -** Implements: -** int write( uint_t chan, const void *buffer, uint_t length ); -** -** Writes 'length' bytes from 'buffer' to 'chan'. Returns the -** count of bytes actually transferred. -*/ -SYSIMPL(write) { - - // sanity check - assert( pcb != NULL ); - - SYSCALL_ENTER( pcb->pid ); - - // grab the parameters - uint_t chan = ARG(pcb,1); - char *buf = (char *) ARG(pcb,2); - uint_t length = ARG(pcb,3); - - // this is almost insanely simple, but it does separate the - // low-level device access fromm the higher-level syscall implementation - - // assume we write the indicated amount - int rval = length; - - // simplest case - if( length >= 0 ) { - - if( chan == CHAN_CIO ) { - - cio_write( buf, length ); - - } else if( chan == CHAN_SIO ) { - - sio_write( buf, length ); - - } else { - - rval = E_BAD_CHAN; - - } - - } - - RET(pcb) = rval; - - SYSCALL_EXIT( rval ); -} - -/** -** sys_getpid - returns the PID of the calling process -** -** Implements: -** uint_t getpid( void ); -*/ -SYSIMPL(getpid) { - - // sanity check! - assert( pcb != NULL ); - - SYSCALL_ENTER( pcb->pid ); - - // return the time - RET(pcb) = pcb->pid; -} - -/** -** sys_getppid - returns the PID of the parent of the calling process -** -** Implements: -** uint_t getppid( void ); -*/ -SYSIMPL(getppid) { - - // sanity check! - assert( pcb != NULL ); - assert( pcb->parent != NULL ); - - SYSCALL_ENTER( pcb->pid ); - - // return the time - RET(pcb) = pcb->parent->pid; -} - -/** -** sys_gettime - returns the current system time -** -** Implements: -** uint32_t gettime( void ); -*/ -SYSIMPL(gettime) { - - // sanity check! - assert( pcb != NULL ); - - SYSCALL_ENTER( pcb->pid ); - - // return the time - RET(pcb) = system_time; -} - -/** -** sys_getprio - the scheduling priority of the calling process -** -** Implements: -** int getprio( void ); -*/ -SYSIMPL(getprio) { - - // sanity check! - assert( pcb != NULL ); - - SYSCALL_ENTER( pcb->pid ); - - // return the time - RET(pcb) = pcb->priority; -} - -/** -** sys_setprio - sets the scheduling priority of the calling process -** -** Implements: -** int setprio( int new ); -*/ -SYSIMPL(setprio) { - - // sanity check! - assert( pcb != NULL ); - - SYSCALL_ENTER( pcb->pid ); - - // remember the old priority - int old = pcb->priority; - - // set the priority - pcb->priority = ARG(pcb,1); - - // return the old value - RET(pcb) = old; -} - -/** -** sys_kill - terminate a process with extreme prejudice -** -** Implements: -** int32_t kill( uint_t pid ); -** -** Marks the specified process (or the calling process, if PID is 0) -** as "killed". Returns 0 on success, else an error code. -*/ -SYSIMPL(kill) { - - // sanity check - assert( pcb != NULL ); - - SYSCALL_ENTER( pcb->pid ); - - // who is the victim? - uint_t pid = ARG(pcb,1); - - // if it's this process, convert this into a call to exit() - if( pid == pcb->pid ) { - pcb->exit_status = EXIT_KILLED; - pcb_zombify( pcb ); - dispatch(); - SYSCALL_EXIT( EXIT_KILLED ); - } - - // must be a valid "ordinary user" PID - // QUESTION: what if it's the idle process? - if( pid < FIRST_USER_PID ) { - RET(pcb) = E_FAILURE; - SYSCALL_EXIT( E_FAILURE ); - } - - // OK, this is an acceptable victim; see if it exists - pcb_t *victim = pcb_find_pid( pid ); - if( victim == NULL ) { - // nope! - RET(pcb) = E_NOT_FOUND; - SYSCALL_EXIT( E_NOT_FOUND ); - } - - // must have a state that is possible - assert( victim->state >= FIRST_VIABLE && victim->state < N_STATES ); - - // how we perform the kill depends on the victim's state - int32_t status = SUCCESS; - - switch( victim->state ) { - - case STATE_KILLED: // FALL THROUGH - case STATE_ZOMBIE: - // you can't kill it if it's already dead - RET(pcb) = SUCCESS; - break; - - case STATE_READY: // FALL THROUGH - case STATE_SLEEPING: // FALL THROUGH - case STATE_BLOCKED: // FALL THROUGH - // here, the process is on a queue somewhere; mark - // it as "killed", and let the scheduler deal with it - victim->state = STATE_KILLED; - RET(pcb) = SUCCESS; - break; - - case STATE_RUNNING: - // we have met the enemy, and it is us! - pcb->exit_status = EXIT_KILLED; - pcb_zombify( pcb ); - status = EXIT_KILLED; - // we need a new current process - dispatch(); - break; - - case STATE_WAITING: - // similar to the 'running' state, but we don't need - // to dispatch a new process - victim->exit_status = EXIT_KILLED; - status = pcb_queue_remove_this( waiting, victim ); - pcb_zombify( victim ); - RET(pcb) = status; - break; - - default: - // this is a really bad potential problem - we have an - // unexpected or bogus process state, but we didn't - // catch that earlier. - sprint( b256, "*** kill(): victim %d, odd state %d\n", - victim->pid, victim->state ); - PANIC( 0, b256 ); - } - - SYSCALL_EXIT( status ); -} - - -/** -** sys_sleep - put the calling process to sleep for some length of time -** -** Implements: -** uint_t sleep( uint_t ms ); -** -** Puts the calling process to sleep for 'ms' milliseconds (or just yields -** the CPU if 'ms' is 0). ** Returns the time the process spent sleeping. -*/ -SYSIMPL(sleep) { - - // sanity check - assert( pcb != NULL ); - - SYSCALL_ENTER( pcb->pid ); - - // get the desired duration - uint_t length = ARG( pcb, 1 ); - - if( length == 0 ) { - - // just yield the CPU - // sleep duration is 0 - RET(pcb) = 0; - - // back on the ready queue - schedule( pcb ); - - } else { - - // sleep for a while - pcb->wakeup = system_time + length; - - if( pcb_queue_insert(sleeping,pcb) != SUCCESS ) { - // something strange is happening - WARNING( "sleep pcb insert failed" ); - // if this is the current process, report an error - if( current == pcb ) { - RET(pcb) = -1; - } - // return without dispatching a new process - return; - } - } - - // only dispatch if the current process called us - if( pcb == current ) { - current = NULL; - dispatch(); - } -} - -/* -** PRIVATE FUNCTIONS GLOBAL VARIABLES -*/ - -/* -** The system call jump table -** -** Initialized using designated initializers to ensure the entries -** are correct even if the syscall code values should happen to change. -** This also makes it easy to add new system call entries, as their -** position in the initialization list is irrelevant. -*/ - -static void (* const syscalls[N_SYSCALLS])( pcb_t * ) = { - [ SYS_exit ] = sys_exit, - [ SYS_waitpid ] = sys_waitpid, - [ SYS_fork ] = sys_fork, - [ SYS_exec ] = sys_exec, - [ SYS_read ] = sys_read, - [ SYS_write ] = sys_write, - [ SYS_getpid ] = sys_getpid, - [ SYS_getppid ] = sys_getppid, - [ SYS_gettime ] = sys_gettime, - [ SYS_getprio ] = sys_getprio, - [ SYS_setprio ] = sys_setprio, - [ SYS_kill ] = sys_kill, - [ SYS_sleep ] = sys_sleep -}; - -/** -** Name: sys_isr -** -** System call ISR -** -** @param vector Vector number for this interrupt -** @param code Error code (0 for this interrupt) -*/ -static void sys_isr( int vector, int code ) { - - // keep the compiler happy - (void) vector; - (void) code; - - // sanity check! - assert( current != NULL ); - assert( current->context != NULL ); - - // retrieve the syscall code - int num = REG( current, eax ); - -#if TRACING_SYSCALLS - cio_printf( "** --> SYS pid %u code %u\n", current->pid, num ); -#endif - - // validate it - if( num < 0 || num >= N_SYSCALLS ) { - // bad syscall number - // could kill it, but we'll just force it to exit - num = SYS_exit; - ARG(current,1) = EXIT_BAD_SYSCALL; - } - - // call the handler - syscalls[num]( current ); - -#if TRACING_SYSCALLS - cio_printf( "** <-- SYS pid %u ret %u\n", current->pid, RET(current) ); -#endif - - // tell the PIC we're done - outb( PIC1_CMD, PIC_EOI ); -} - -/* -** PUBLIC FUNCTIONS -*/ - -/** -** Name: sys_init -** -** Syscall module initialization routine -** -** Dependencies: -** Must be called after cio_init() -*/ -void sys_init( void ) { - -#if TRACING_INIT - cio_puts( " Sys" ); -#endif - - // install the second-stage ISR - install_isr( VEC_SYSCALL, sys_isr ); -} |