summaryrefslogtreecommitdiff
path: root/kernel/syscalls.c
diff options
context:
space:
mode:
authorFreya Murphy <freya@freyacat.org>2025-04-03 12:31:21 -0400
committerFreya Murphy <freya@freyacat.org>2025-04-03 12:31:21 -0400
commita524eb3846aac4d1b38f08cba49ff3503107042f (patch)
treedbe81fccf975f646a681e4fdcebd227cdfb98774 /kernel/syscalls.c
parentnew libs (diff)
downloadcomus-a524eb3846aac4d1b38f08cba49ff3503107042f.tar.gz
comus-a524eb3846aac4d1b38f08cba49ff3503107042f.tar.bz2
comus-a524eb3846aac4d1b38f08cba49ff3503107042f.zip
move old kernel code (for now) into kernel/old, trying to get long mode
Diffstat (limited to 'kernel/syscalls.c')
-rw-r--r--kernel/syscalls.c803
1 files changed, 0 insertions, 803 deletions
diff --git a/kernel/syscalls.c b/kernel/syscalls.c
deleted file mode 100644
index 92a0a23..0000000
--- a/kernel/syscalls.c
+++ /dev/null
@@ -1,803 +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, false);
- 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);
-}