2025-03-25 17:36:52 -04:00
|
|
|
/**
|
|
|
|
** @file syscalls.c
|
|
|
|
**
|
|
|
|
** @author CSCI-452 class of 20245
|
|
|
|
**
|
|
|
|
** @brief System call implementations
|
|
|
|
*/
|
|
|
|
|
2025-03-27 11:39:12 -04:00
|
|
|
#define KERNEL_SRC
|
2025-03-25 17:36:52 -04:00
|
|
|
|
|
|
|
#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
|
|
|
|
|
2025-03-27 11:39:12 -04:00
|
|
|
#define SYSCALL_ENTER(x) \
|
|
|
|
do { \
|
|
|
|
cio_printf("--> %s, pid %08x", __func__, (uint32_t)(x)); \
|
|
|
|
} while (0)
|
2025-03-25 17:36:52 -04:00
|
|
|
|
|
|
|
#else
|
|
|
|
|
2025-03-27 11:39:12 -04:00
|
|
|
#define SYSCALL_ENTER(x) /* */
|
2025-03-25 17:36:52 -04:00
|
|
|
|
2025-03-27 11:39:12 -04:00
|
|
|
#endif /* TRACING_SYSCALLS */
|
2025-03-25 17:36:52 -04:00
|
|
|
|
|
|
|
#if TRACING_SYSRETS
|
|
|
|
|
2025-03-27 11:39:12 -04:00
|
|
|
#define SYSCALL_EXIT(x) \
|
|
|
|
do { \
|
|
|
|
cio_printf("<-- %s %08x\n", __func__, (uint32_t)(x)); \
|
|
|
|
return; \
|
|
|
|
} while (0)
|
2025-03-25 17:36:52 -04:00
|
|
|
|
|
|
|
#else
|
|
|
|
|
|
|
|
#define SYSCALL_EXIT(x) return
|
|
|
|
|
2025-03-27 11:39:12 -04:00
|
|
|
#endif /* TRACING_SYSRETS */
|
2025-03-25 17:36:52 -04:00
|
|
|
|
|
|
|
/*
|
|
|
|
** 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
|
2025-03-27 11:39:12 -04:00
|
|
|
#define SYSIMPL(x) void sys_##x(pcb_t *pcb)
|
2025-03-25 17:36:52 -04:00
|
|
|
|
|
|
|
/*
|
|
|
|
** 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
|
|
|
|
*/
|
2025-03-27 11:39:12 -04:00
|
|
|
SYSIMPL(exit)
|
|
|
|
{
|
2025-03-25 17:36:52 -04:00
|
|
|
// sanity check
|
2025-03-27 11:39:12 -04:00
|
|
|
assert(pcb != NULL);
|
2025-03-25 17:36:52 -04:00
|
|
|
|
2025-03-27 11:39:12 -04:00
|
|
|
SYSCALL_ENTER(pcb->pid);
|
2025-03-25 17:36:52 -04:00
|
|
|
|
|
|
|
// retrieve the exit status of this process
|
2025-03-27 11:39:12 -04:00
|
|
|
pcb->exit_status = (int32_t)ARG(pcb, 1);
|
2025-03-25 17:36:52 -04:00
|
|
|
|
|
|
|
// 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
|
2025-03-27 11:39:12 -04:00
|
|
|
|
|
|
|
pcb_zombify(pcb);
|
2025-03-25 17:36:52 -04:00
|
|
|
|
|
|
|
// pick a new winner
|
|
|
|
dispatch();
|
|
|
|
|
2025-03-27 11:39:12 -04:00
|
|
|
SYSCALL_EXIT(0);
|
2025-03-25 17:36:52 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
** 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.
|
|
|
|
*/
|
2025-03-27 11:39:12 -04:00
|
|
|
SYSIMPL(waitpid)
|
|
|
|
{
|
2025-03-25 17:36:52 -04:00
|
|
|
// sanity check
|
2025-03-27 11:39:12 -04:00
|
|
|
assert(pcb != NULL);
|
2025-03-25 17:36:52 -04:00
|
|
|
|
2025-03-27 11:39:12 -04:00
|
|
|
SYSCALL_ENTER(pcb->pid);
|
2025-03-25 17:36:52 -04:00
|
|
|
|
|
|
|
/*
|
|
|
|
** 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!
|
2025-03-27 11:39:12 -04:00
|
|
|
uint_t target = ARG(pcb, 1);
|
2025-03-25 17:36:52 -04:00
|
|
|
|
2025-03-27 11:39:12 -04:00
|
|
|
if (target == pcb->pid) {
|
2025-03-25 17:36:52 -04:00
|
|
|
RET(pcb) = E_BAD_PARAM;
|
2025-03-27 11:39:12 -04:00
|
|
|
SYSCALL_EXIT(E_BAD_PARAM);
|
2025-03-25 17:36:52 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
// Good. Now, figure out what we're looking for.
|
|
|
|
|
|
|
|
pcb_t *child = NULL;
|
|
|
|
|
2025-03-27 11:39:12 -04:00
|
|
|
if (target != 0) {
|
2025-03-25 17:36:52 -04:00
|
|
|
// we're looking for a specific child
|
2025-03-27 11:39:12 -04:00
|
|
|
child = pcb_find_pid(target);
|
2025-03-25 17:36:52 -04:00
|
|
|
|
2025-03-27 11:39:12 -04:00
|
|
|
if (child != NULL) {
|
2025-03-25 17:36:52 -04:00
|
|
|
// found the process; is it one of our children:
|
2025-03-27 11:39:12 -04:00
|
|
|
if (child->parent != pcb) {
|
2025-03-25 17:36:52 -04:00
|
|
|
// NO, so we can't wait for it
|
|
|
|
RET(pcb) = E_BAD_PARAM;
|
2025-03-27 11:39:12 -04:00
|
|
|
SYSCALL_EXIT(E_BAD_PARAM);
|
2025-03-25 17:36:52 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
// yes! is this one ready to be collected?
|
2025-03-27 11:39:12 -04:00
|
|
|
if (child->state != STATE_ZOMBIE) {
|
2025-03-25 17:36:52 -04:00
|
|
|
// no, so we'll have to block for now
|
|
|
|
child = NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
} else {
|
|
|
|
// no such child
|
|
|
|
RET(pcb) = E_BAD_PARAM;
|
2025-03-27 11:39:12 -04:00
|
|
|
SYSCALL_EXIT(E_BAD_PARAM);
|
2025-03-25 17:36:52 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
} 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;
|
|
|
|
|
2025-03-27 11:39:12 -04:00
|
|
|
for (int i = 0; i < N_PROCS; ++i, ++curr) {
|
|
|
|
if (curr->parent == pcb) {
|
2025-03-25 17:36:52 -04:00
|
|
|
// found one!
|
|
|
|
found = true;
|
|
|
|
|
|
|
|
// has it already exited?
|
2025-03-27 11:39:12 -04:00
|
|
|
if (curr->state == STATE_ZOMBIE) {
|
2025-03-25 17:36:52 -04:00
|
|
|
// yes, so we're done here
|
|
|
|
child = curr;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2025-03-27 11:39:12 -04:00
|
|
|
if (!found) {
|
2025-03-25 17:36:52 -04:00
|
|
|
// got through the loop without finding a child!
|
|
|
|
RET(pcb) = E_NO_CHILDREN;
|
2025-03-27 11:39:12 -04:00
|
|
|
SYSCALL_EXIT(E_NO_CHILDREN);
|
2025-03-25 17:36:52 -04:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
** 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?
|
2025-03-27 11:39:12 -04:00
|
|
|
if (child == NULL) {
|
2025-03-25 17:36:52 -04:00
|
|
|
// no - mark the parent as "Waiting"
|
|
|
|
pcb->state = STATE_WAITING;
|
2025-03-27 11:39:12 -04:00
|
|
|
assert(pcb_queue_insert(waiting, pcb) == SUCCESS);
|
2025-03-25 17:36:52 -04:00
|
|
|
|
|
|
|
// select a new current process
|
|
|
|
dispatch();
|
2025-03-27 11:39:12 -04:00
|
|
|
SYSCALL_EXIT((uint32_t)current);
|
2025-03-25 17:36:52 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
// found a Zombie; collect its information and clean it up
|
|
|
|
RET(pcb) = child->pid;
|
|
|
|
|
|
|
|
// get "status" pointer from parent
|
2025-03-27 11:39:12 -04:00
|
|
|
int32_t *stat = (int32_t *)ARG(pcb, 2);
|
2025-03-25 17:36:52 -04:00
|
|
|
|
|
|
|
// if stat is NULL, the parent doesn't want the status
|
2025-03-27 11:39:12 -04:00
|
|
|
if (stat != NULL) {
|
2025-03-25 17:36:52 -04:00
|
|
|
// ********************************************************
|
|
|
|
// ** 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
|
2025-03-27 11:39:12 -04:00
|
|
|
pcb_cleanup(child);
|
2025-03-25 17:36:52 -04:00
|
|
|
|
2025-03-27 11:39:12 -04:00
|
|
|
SYSCALL_EXIT(RET(pcb));
|
2025-03-25 17:36:52 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
** 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.
|
|
|
|
*/
|
2025-03-27 11:39:12 -04:00
|
|
|
SYSIMPL(fork)
|
|
|
|
{
|
2025-03-25 17:36:52 -04:00
|
|
|
// sanity check
|
2025-03-27 11:39:12 -04:00
|
|
|
assert(pcb != NULL);
|
2025-03-25 17:36:52 -04:00
|
|
|
|
2025-03-27 11:39:12 -04:00
|
|
|
SYSCALL_ENTER(pcb->pid);
|
2025-03-25 17:36:52 -04:00
|
|
|
|
|
|
|
// Make sure there's room for another process!
|
|
|
|
pcb_t *new;
|
2025-03-27 11:39:12 -04:00
|
|
|
if (pcb_alloc(&new) != SUCCESS || new == NULL) {
|
2025-03-25 17:36:52 -04:00
|
|
|
RET(pcb) = E_NO_PROCS;
|
2025-03-27 11:39:12 -04:00
|
|
|
SYSCALL_EXIT(RET(pcb));
|
2025-03-25 17:36:52 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
// duplicate the memory image of the parent
|
2025-03-27 11:39:12 -04:00
|
|
|
int status = user_duplicate(new, pcb);
|
|
|
|
if (status != SUCCESS) {
|
|
|
|
pcb_free(new);
|
2025-03-25 17:36:52 -04:00
|
|
|
RET(pcb) = status;
|
2025-03-27 11:39:12 -04:00
|
|
|
SYSCALL_EXIT(status);
|
2025-03-25 17:36:52 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
// 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.
|
2025-03-27 11:39:12 -04:00
|
|
|
schedule(new);
|
2025-03-25 17:36:52 -04:00
|
|
|
|
2025-03-27 11:39:12 -04:00
|
|
|
SYSCALL_EXIT(new->pid);
|
2025-03-25 17:36:52 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
** 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
|
2025-03-27 11:39:12 -04:00
|
|
|
assert(pcb != NULL);
|
2025-03-25 17:36:52 -04:00
|
|
|
|
2025-03-27 11:39:12 -04:00
|
|
|
uint_t what = ARG(pcb, 1);
|
|
|
|
const char **args = (const char **)ARG(pcb, 2);
|
2025-03-25 17:36:52 -04:00
|
|
|
|
2025-03-27 11:39:12 -04:00
|
|
|
SYSCALL_ENTER(pcb->pid);
|
2025-03-25 17:36:52 -04:00
|
|
|
|
|
|
|
// locate the requested program
|
2025-03-27 11:39:12 -04:00
|
|
|
prog_t *prog = user_locate(what);
|
|
|
|
if (prog == NULL) {
|
2025-03-25 17:36:52 -04:00
|
|
|
RET(pcb) = E_NOT_FOUND;
|
2025-03-27 11:39:12 -04:00
|
|
|
SYSCALL_EXIT(E_NOT_FOUND);
|
2025-03-25 17:36:52 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
// we have located the program, but before we can load it,
|
|
|
|
// we need to clean up the existing VM hierarchy
|
2025-03-27 11:39:12 -04:00
|
|
|
vm_free(pcb->pdir);
|
2025-03-25 17:36:52 -04:00
|
|
|
pcb->pdir = NULL;
|
|
|
|
|
|
|
|
// "load" it and set up the VM tables for this process
|
2025-03-31 12:41:04 -04:00
|
|
|
int status = user_load(prog, pcb, args, false);
|
2025-03-27 11:39:12 -04:00
|
|
|
if (status != SUCCESS) {
|
2025-03-25 17:36:52 -04:00
|
|
|
RET(pcb) = status;
|
2025-03-27 11:39:12 -04:00
|
|
|
SYSCALL_EXIT(status);
|
2025-03-25 17:36:52 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
** 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.
|
|
|
|
*/
|
|
|
|
|
2025-03-27 11:39:12 -04:00
|
|
|
schedule(pcb);
|
2025-03-25 17:36:52 -04:00
|
|
|
|
|
|
|
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.
|
|
|
|
*/
|
2025-03-27 11:39:12 -04:00
|
|
|
SYSIMPL(read)
|
|
|
|
{
|
2025-03-25 17:36:52 -04:00
|
|
|
// sanity check
|
2025-03-27 11:39:12 -04:00
|
|
|
assert(pcb != NULL);
|
|
|
|
|
|
|
|
SYSCALL_ENTER(pcb->pid);
|
2025-03-25 17:36:52 -04:00
|
|
|
|
|
|
|
// grab the arguments
|
2025-03-27 11:39:12 -04:00
|
|
|
uint_t chan = ARG(pcb, 1);
|
|
|
|
char *buf = (char *)ARG(pcb, 2);
|
|
|
|
uint_t len = ARG(pcb, 3);
|
2025-03-25 17:36:52 -04:00
|
|
|
|
|
|
|
// if the buffer is of length 0, we're done!
|
2025-03-27 11:39:12 -04:00
|
|
|
if (len == 0) {
|
2025-03-25 17:36:52 -04:00
|
|
|
RET(pcb) = 0;
|
2025-03-27 11:39:12 -04:00
|
|
|
SYSCALL_EXIT(0);
|
2025-03-25 17:36:52 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
// try to get the next character(s)
|
|
|
|
int n = 0;
|
|
|
|
|
2025-03-27 11:39:12 -04:00
|
|
|
if (chan == CHAN_CIO) {
|
2025-03-25 17:36:52 -04:00
|
|
|
// console input is non-blocking
|
2025-03-27 11:39:12 -04:00
|
|
|
if (cio_input_queue() < 1) {
|
2025-03-25 17:36:52 -04:00
|
|
|
RET(pcb) = 0;
|
2025-03-27 11:39:12 -04:00
|
|
|
SYSCALL_EXIT(0);
|
2025-03-25 17:36:52 -04:00
|
|
|
}
|
|
|
|
// at least one character
|
2025-03-27 11:39:12 -04:00
|
|
|
n = cio_gets(buf, len);
|
2025-03-25 17:36:52 -04:00
|
|
|
RET(pcb) = n;
|
2025-03-27 11:39:12 -04:00
|
|
|
SYSCALL_EXIT(n);
|
2025-03-25 17:36:52 -04:00
|
|
|
|
2025-03-27 11:39:12 -04:00
|
|
|
} else if (chan == CHAN_SIO) {
|
2025-03-25 17:36:52 -04:00
|
|
|
// SIO input is blocking, so if there are no characters
|
|
|
|
// available, we'll block this process
|
2025-03-27 11:39:12 -04:00
|
|
|
n = sio_read(buf, len);
|
2025-03-25 17:36:52 -04:00
|
|
|
RET(pcb) = n;
|
2025-03-27 11:39:12 -04:00
|
|
|
SYSCALL_EXIT(n);
|
2025-03-25 17:36:52 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
// bad channel code
|
|
|
|
RET(pcb) = E_BAD_PARAM;
|
2025-03-27 11:39:12 -04:00
|
|
|
SYSCALL_EXIT(E_BAD_PARAM);
|
2025-03-25 17:36:52 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
** 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.
|
|
|
|
*/
|
2025-03-27 11:39:12 -04:00
|
|
|
SYSIMPL(write)
|
|
|
|
{
|
2025-03-25 17:36:52 -04:00
|
|
|
// sanity check
|
2025-03-27 11:39:12 -04:00
|
|
|
assert(pcb != NULL);
|
2025-03-25 17:36:52 -04:00
|
|
|
|
2025-03-27 11:39:12 -04:00
|
|
|
SYSCALL_ENTER(pcb->pid);
|
2025-03-25 17:36:52 -04:00
|
|
|
|
|
|
|
// grab the parameters
|
2025-03-27 11:39:12 -04:00
|
|
|
uint_t chan = ARG(pcb, 1);
|
|
|
|
char *buf = (char *)ARG(pcb, 2);
|
|
|
|
uint_t length = ARG(pcb, 3);
|
2025-03-25 17:36:52 -04:00
|
|
|
|
|
|
|
// 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
|
2025-03-27 11:39:12 -04:00
|
|
|
if (length >= 0) {
|
|
|
|
if (chan == CHAN_CIO) {
|
|
|
|
cio_write(buf, length);
|
2025-03-25 17:36:52 -04:00
|
|
|
|
2025-03-27 11:39:12 -04:00
|
|
|
} else if (chan == CHAN_SIO) {
|
|
|
|
sio_write(buf, length);
|
2025-03-25 17:36:52 -04:00
|
|
|
|
|
|
|
} else {
|
|
|
|
rval = E_BAD_CHAN;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
RET(pcb) = rval;
|
|
|
|
|
2025-03-27 11:39:12 -04:00
|
|
|
SYSCALL_EXIT(rval);
|
2025-03-25 17:36:52 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
** sys_getpid - returns the PID of the calling process
|
|
|
|
**
|
|
|
|
** Implements:
|
|
|
|
** uint_t getpid( void );
|
|
|
|
*/
|
2025-03-27 11:39:12 -04:00
|
|
|
SYSIMPL(getpid)
|
|
|
|
{
|
2025-03-25 17:36:52 -04:00
|
|
|
// sanity check!
|
2025-03-27 11:39:12 -04:00
|
|
|
assert(pcb != NULL);
|
2025-03-25 17:36:52 -04:00
|
|
|
|
2025-03-27 11:39:12 -04:00
|
|
|
SYSCALL_ENTER(pcb->pid);
|
2025-03-25 17:36:52 -04:00
|
|
|
|
|
|
|
// return the time
|
|
|
|
RET(pcb) = pcb->pid;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
** sys_getppid - returns the PID of the parent of the calling process
|
|
|
|
**
|
|
|
|
** Implements:
|
|
|
|
** uint_t getppid( void );
|
|
|
|
*/
|
2025-03-27 11:39:12 -04:00
|
|
|
SYSIMPL(getppid)
|
|
|
|
{
|
2025-03-25 17:36:52 -04:00
|
|
|
// sanity check!
|
2025-03-27 11:39:12 -04:00
|
|
|
assert(pcb != NULL);
|
|
|
|
assert(pcb->parent != NULL);
|
2025-03-25 17:36:52 -04:00
|
|
|
|
2025-03-27 11:39:12 -04:00
|
|
|
SYSCALL_ENTER(pcb->pid);
|
2025-03-25 17:36:52 -04:00
|
|
|
|
|
|
|
// return the time
|
|
|
|
RET(pcb) = pcb->parent->pid;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
** sys_gettime - returns the current system time
|
|
|
|
**
|
|
|
|
** Implements:
|
|
|
|
** uint32_t gettime( void );
|
|
|
|
*/
|
2025-03-27 11:39:12 -04:00
|
|
|
SYSIMPL(gettime)
|
|
|
|
{
|
2025-03-25 17:36:52 -04:00
|
|
|
// sanity check!
|
2025-03-27 11:39:12 -04:00
|
|
|
assert(pcb != NULL);
|
2025-03-25 17:36:52 -04:00
|
|
|
|
2025-03-27 11:39:12 -04:00
|
|
|
SYSCALL_ENTER(pcb->pid);
|
2025-03-25 17:36:52 -04:00
|
|
|
|
|
|
|
// return the time
|
|
|
|
RET(pcb) = system_time;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
** sys_getprio - the scheduling priority of the calling process
|
|
|
|
**
|
|
|
|
** Implements:
|
|
|
|
** int getprio( void );
|
|
|
|
*/
|
2025-03-27 11:39:12 -04:00
|
|
|
SYSIMPL(getprio)
|
|
|
|
{
|
2025-03-25 17:36:52 -04:00
|
|
|
// sanity check!
|
2025-03-27 11:39:12 -04:00
|
|
|
assert(pcb != NULL);
|
2025-03-25 17:36:52 -04:00
|
|
|
|
2025-03-27 11:39:12 -04:00
|
|
|
SYSCALL_ENTER(pcb->pid);
|
2025-03-25 17:36:52 -04:00
|
|
|
|
|
|
|
// return the time
|
|
|
|
RET(pcb) = pcb->priority;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
** sys_setprio - sets the scheduling priority of the calling process
|
|
|
|
**
|
|
|
|
** Implements:
|
|
|
|
** int setprio( int new );
|
|
|
|
*/
|
2025-03-27 11:39:12 -04:00
|
|
|
SYSIMPL(setprio)
|
|
|
|
{
|
2025-03-25 17:36:52 -04:00
|
|
|
// sanity check!
|
2025-03-27 11:39:12 -04:00
|
|
|
assert(pcb != NULL);
|
2025-03-25 17:36:52 -04:00
|
|
|
|
2025-03-27 11:39:12 -04:00
|
|
|
SYSCALL_ENTER(pcb->pid);
|
2025-03-25 17:36:52 -04:00
|
|
|
|
|
|
|
// remember the old priority
|
|
|
|
int old = pcb->priority;
|
|
|
|
|
|
|
|
// set the priority
|
2025-03-27 11:39:12 -04:00
|
|
|
pcb->priority = ARG(pcb, 1);
|
2025-03-25 17:36:52 -04:00
|
|
|
|
|
|
|
// 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.
|
|
|
|
*/
|
2025-03-27 11:39:12 -04:00
|
|
|
SYSIMPL(kill)
|
|
|
|
{
|
2025-03-25 17:36:52 -04:00
|
|
|
// sanity check
|
2025-03-27 11:39:12 -04:00
|
|
|
assert(pcb != NULL);
|
2025-03-25 17:36:52 -04:00
|
|
|
|
2025-03-27 11:39:12 -04:00
|
|
|
SYSCALL_ENTER(pcb->pid);
|
2025-03-25 17:36:52 -04:00
|
|
|
|
|
|
|
// who is the victim?
|
2025-03-27 11:39:12 -04:00
|
|
|
uint_t pid = ARG(pcb, 1);
|
2025-03-25 17:36:52 -04:00
|
|
|
|
|
|
|
// if it's this process, convert this into a call to exit()
|
2025-03-27 11:39:12 -04:00
|
|
|
if (pid == pcb->pid) {
|
2025-03-25 17:36:52 -04:00
|
|
|
pcb->exit_status = EXIT_KILLED;
|
2025-03-27 11:39:12 -04:00
|
|
|
pcb_zombify(pcb);
|
2025-03-25 17:36:52 -04:00
|
|
|
dispatch();
|
2025-03-27 11:39:12 -04:00
|
|
|
SYSCALL_EXIT(EXIT_KILLED);
|
2025-03-25 17:36:52 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
// must be a valid "ordinary user" PID
|
|
|
|
// QUESTION: what if it's the idle process?
|
2025-03-27 11:39:12 -04:00
|
|
|
if (pid < FIRST_USER_PID) {
|
2025-03-25 17:36:52 -04:00
|
|
|
RET(pcb) = E_FAILURE;
|
2025-03-27 11:39:12 -04:00
|
|
|
SYSCALL_EXIT(E_FAILURE);
|
2025-03-25 17:36:52 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
// OK, this is an acceptable victim; see if it exists
|
2025-03-27 11:39:12 -04:00
|
|
|
pcb_t *victim = pcb_find_pid(pid);
|
|
|
|
if (victim == NULL) {
|
2025-03-25 17:36:52 -04:00
|
|
|
// nope!
|
|
|
|
RET(pcb) = E_NOT_FOUND;
|
2025-03-27 11:39:12 -04:00
|
|
|
SYSCALL_EXIT(E_NOT_FOUND);
|
2025-03-25 17:36:52 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
// must have a state that is possible
|
2025-03-27 11:39:12 -04:00
|
|
|
assert(victim->state >= FIRST_VIABLE && victim->state < N_STATES);
|
2025-03-25 17:36:52 -04:00
|
|
|
|
|
|
|
// how we perform the kill depends on the victim's state
|
|
|
|
int32_t status = SUCCESS;
|
|
|
|
|
2025-03-27 11:39:12 -04:00
|
|
|
switch (victim->state) {
|
|
|
|
case STATE_KILLED: // FALL THROUGH
|
2025-03-25 17:36:52 -04:00
|
|
|
case STATE_ZOMBIE:
|
|
|
|
// you can't kill it if it's already dead
|
|
|
|
RET(pcb) = SUCCESS;
|
|
|
|
break;
|
|
|
|
|
2025-03-27 11:39:12 -04:00
|
|
|
case STATE_READY: // FALL THROUGH
|
|
|
|
case STATE_SLEEPING: // FALL THROUGH
|
|
|
|
case STATE_BLOCKED: // FALL THROUGH
|
2025-03-25 17:36:52 -04:00
|
|
|
// 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;
|
2025-03-27 11:39:12 -04:00
|
|
|
pcb_zombify(pcb);
|
2025-03-25 17:36:52 -04:00
|
|
|
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;
|
2025-03-27 11:39:12 -04:00
|
|
|
status = pcb_queue_remove_this(waiting, victim);
|
|
|
|
pcb_zombify(victim);
|
2025-03-25 17:36:52 -04:00
|
|
|
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.
|
2025-03-27 11:39:12 -04:00
|
|
|
sprint(b256, "*** kill(): victim %d, odd state %d\n", victim->pid,
|
|
|
|
victim->state);
|
|
|
|
PANIC(0, b256);
|
2025-03-25 17:36:52 -04:00
|
|
|
}
|
|
|
|
|
2025-03-27 11:39:12 -04:00
|
|
|
SYSCALL_EXIT(status);
|
2025-03-25 17:36:52 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
** 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.
|
|
|
|
*/
|
2025-03-27 11:39:12 -04:00
|
|
|
SYSIMPL(sleep)
|
|
|
|
{
|
2025-03-25 17:36:52 -04:00
|
|
|
// sanity check
|
2025-03-27 11:39:12 -04:00
|
|
|
assert(pcb != NULL);
|
2025-03-25 17:36:52 -04:00
|
|
|
|
2025-03-27 11:39:12 -04:00
|
|
|
SYSCALL_ENTER(pcb->pid);
|
2025-03-25 17:36:52 -04:00
|
|
|
|
|
|
|
// get the desired duration
|
2025-03-27 11:39:12 -04:00
|
|
|
uint_t length = ARG(pcb, 1);
|
2025-03-25 17:36:52 -04:00
|
|
|
|
2025-03-27 11:39:12 -04:00
|
|
|
if (length == 0) {
|
2025-03-25 17:36:52 -04:00
|
|
|
// just yield the CPU
|
|
|
|
// sleep duration is 0
|
|
|
|
RET(pcb) = 0;
|
|
|
|
|
|
|
|
// back on the ready queue
|
2025-03-27 11:39:12 -04:00
|
|
|
schedule(pcb);
|
2025-03-25 17:36:52 -04:00
|
|
|
|
|
|
|
} else {
|
|
|
|
// sleep for a while
|
|
|
|
pcb->wakeup = system_time + length;
|
|
|
|
|
2025-03-27 11:39:12 -04:00
|
|
|
if (pcb_queue_insert(sleeping, pcb) != SUCCESS) {
|
2025-03-25 17:36:52 -04:00
|
|
|
// something strange is happening
|
2025-03-27 11:39:12 -04:00
|
|
|
WARNING("sleep pcb insert failed");
|
2025-03-25 17:36:52 -04:00
|
|
|
// if this is the current process, report an error
|
2025-03-27 11:39:12 -04:00
|
|
|
if (current == pcb) {
|
2025-03-25 17:36:52 -04:00
|
|
|
RET(pcb) = -1;
|
|
|
|
}
|
|
|
|
// return without dispatching a new process
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// only dispatch if the current process called us
|
2025-03-27 11:39:12 -04:00
|
|
|
if (pcb == current) {
|
2025-03-25 17:36:52 -04:00
|
|
|
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.
|
|
|
|
*/
|
|
|
|
|
2025-03-27 11:39:12 -04:00
|
|
|
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
|
2025-03-25 17:36:52 -04:00
|
|
|
};
|
|
|
|
|
|
|
|
/**
|
|
|
|
** Name: sys_isr
|
|
|
|
**
|
|
|
|
** System call ISR
|
|
|
|
**
|
|
|
|
** @param vector Vector number for this interrupt
|
|
|
|
** @param code Error code (0 for this interrupt)
|
|
|
|
*/
|
2025-03-27 11:39:12 -04:00
|
|
|
static void sys_isr(int vector, int code)
|
|
|
|
{
|
2025-03-25 17:36:52 -04:00
|
|
|
// keep the compiler happy
|
2025-03-27 11:39:12 -04:00
|
|
|
(void)vector;
|
|
|
|
(void)code;
|
2025-03-25 17:36:52 -04:00
|
|
|
|
|
|
|
// sanity check!
|
2025-03-27 11:39:12 -04:00
|
|
|
assert(current != NULL);
|
|
|
|
assert(current->context != NULL);
|
2025-03-25 17:36:52 -04:00
|
|
|
|
|
|
|
// retrieve the syscall code
|
2025-03-27 11:39:12 -04:00
|
|
|
int num = REG(current, eax);
|
2025-03-25 17:36:52 -04:00
|
|
|
|
|
|
|
#if TRACING_SYSCALLS
|
2025-03-27 11:39:12 -04:00
|
|
|
cio_printf("** --> SYS pid %u code %u\n", current->pid, num);
|
2025-03-25 17:36:52 -04:00
|
|
|
#endif
|
|
|
|
|
|
|
|
// validate it
|
2025-03-27 11:39:12 -04:00
|
|
|
if (num < 0 || num >= N_SYSCALLS) {
|
2025-03-25 17:36:52 -04:00
|
|
|
// bad syscall number
|
|
|
|
// could kill it, but we'll just force it to exit
|
|
|
|
num = SYS_exit;
|
2025-03-27 11:39:12 -04:00
|
|
|
ARG(current, 1) = EXIT_BAD_SYSCALL;
|
2025-03-25 17:36:52 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
// call the handler
|
2025-03-27 11:39:12 -04:00
|
|
|
syscalls[num](current);
|
2025-03-25 17:36:52 -04:00
|
|
|
|
|
|
|
#if TRACING_SYSCALLS
|
2025-03-27 11:39:12 -04:00
|
|
|
cio_printf("** <-- SYS pid %u ret %u\n", current->pid, RET(current));
|
2025-03-25 17:36:52 -04:00
|
|
|
#endif
|
|
|
|
|
|
|
|
// tell the PIC we're done
|
2025-03-27 11:39:12 -04:00
|
|
|
outb(PIC1_CMD, PIC_EOI);
|
2025-03-25 17:36:52 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
** PUBLIC FUNCTIONS
|
|
|
|
*/
|
|
|
|
|
|
|
|
/**
|
|
|
|
** Name: sys_init
|
|
|
|
**
|
|
|
|
** Syscall module initialization routine
|
|
|
|
**
|
|
|
|
** Dependencies:
|
|
|
|
** Must be called after cio_init()
|
|
|
|
*/
|
2025-03-27 11:39:12 -04:00
|
|
|
void sys_init(void)
|
|
|
|
{
|
2025-03-25 17:36:52 -04:00
|
|
|
#if TRACING_INIT
|
2025-03-27 11:39:12 -04:00
|
|
|
cio_puts(" Sys");
|
2025-03-25 17:36:52 -04:00
|
|
|
#endif
|
|
|
|
|
|
|
|
// install the second-stage ISR
|
2025-03-27 11:39:12 -04:00
|
|
|
install_isr(VEC_SYSCALL, sys_isr);
|
2025-03-25 17:36:52 -04:00
|
|
|
}
|