561 lines
11 KiB
C
561 lines
11 KiB
C
|
|
#include <stdbool.h>
|
|
#include <stdint.h>
|
|
#include <string.h>
|
|
|
|
#include <errno.h>
|
|
|
|
#include <sys/kassert.h>
|
|
#include <sys/kmem.h>
|
|
#include <sys/ktime.h>
|
|
#include <sys/ktimer.h>
|
|
#include <sys/thread.h>
|
|
#include <sys/loader.h>
|
|
#include <sys/syscall.h>
|
|
#include <sys/disk.h>
|
|
#include <sys/vfs.h>
|
|
#include <sys/vfsuio.h>
|
|
#include <sys/nic.h>
|
|
|
|
Handle *Console_OpenHandle();
|
|
|
|
uint64_t
|
|
Syscall_Time()
|
|
{
|
|
return KTime_GetEpochNS();
|
|
}
|
|
|
|
uint64_t
|
|
Syscall_GetPID()
|
|
{
|
|
Thread *cur = Sched_Current();
|
|
uint64_t pid = cur->proc->pid;
|
|
|
|
Thread_Release(cur);
|
|
|
|
return pid;
|
|
}
|
|
|
|
void
|
|
Syscall_Exit(uint64_t status)
|
|
{
|
|
Thread *cur = Sched_Current();
|
|
|
|
// Request each thread to exit
|
|
|
|
// Wait for all threads to exit
|
|
|
|
// Write exit code
|
|
cur->proc->exitCode = status;
|
|
|
|
// Exit this thread
|
|
Sched_SetZombie(cur);
|
|
Thread_Release(cur);
|
|
Sched_Scheduler();
|
|
|
|
// Should not return
|
|
Panic("Returned to exited thread!\n");
|
|
|
|
return;
|
|
}
|
|
|
|
uint64_t
|
|
Syscall_Spawn(uint64_t user_path)
|
|
{
|
|
int status;
|
|
char path[512];
|
|
void *pg;
|
|
VNode *file;
|
|
Process *proc;
|
|
Thread *thr;
|
|
Thread *cur;
|
|
|
|
status = CopyStrIn(user_path, &path, sizeof(path));
|
|
if (status != 0)
|
|
return status;
|
|
|
|
Log(syscall, "Spawn(%s)\n", path);
|
|
|
|
pg = PAlloc_AllocPage();
|
|
if (!pg) {
|
|
return ENOMEM;
|
|
}
|
|
|
|
file = VFS_Lookup(path);
|
|
if (!file) {
|
|
// Free
|
|
return ENOENT;
|
|
}
|
|
|
|
status = VFS_Open(file);
|
|
if (status < 0) {
|
|
Log(syscall, "Error VFS_Open\n");
|
|
// Release & free
|
|
return status;
|
|
}
|
|
|
|
status = VFS_Read(file, pg, 0, 1024);
|
|
if (status < 0) {
|
|
Log(syscall, "Error VFS_Read\n");
|
|
// Release & free
|
|
return status;
|
|
}
|
|
|
|
cur = Sched_Current();
|
|
proc = Process_Create(cur->proc, path);
|
|
thr = Thread_Create(proc);
|
|
Thread_Release(cur);
|
|
Log(syscall, "SPAWN %lx\n", thr);
|
|
|
|
Handle *handle = Console_OpenHandle();
|
|
Handle_Add(proc, handle);
|
|
handle = Console_OpenHandle();
|
|
Handle_Add(proc, handle);
|
|
handle = Console_OpenHandle();
|
|
Handle_Add(proc, handle);
|
|
|
|
Loader_Load(thr, file, pg, 1024);
|
|
|
|
VFS_Close(file);
|
|
|
|
Sched_SetRunnable(thr);
|
|
|
|
return proc->pid;
|
|
}
|
|
|
|
uint64_t
|
|
Syscall_Wait(uint64_t pid)
|
|
{
|
|
uint64_t status;
|
|
Thread *cur = Sched_Current();
|
|
|
|
status = Process_Wait(cur->proc, pid);
|
|
Thread_Release(cur);
|
|
|
|
return status;
|
|
}
|
|
|
|
uint64_t
|
|
Syscall_MMap(uint64_t addr, uint64_t len, uint64_t prot)
|
|
{
|
|
Thread *cur = Sched_Current();
|
|
uint64_t pgs = (len + PGSIZE - 1) / PGSIZE;
|
|
bool status;
|
|
|
|
status = PMap_AllocMap(cur->space, addr, pgs, PTE_W);
|
|
Thread_Release(cur);
|
|
if (!status) {
|
|
// XXX: Need to unmap PMap_Unmap(cur->space, addr, pgs);
|
|
return 0;
|
|
} else {
|
|
return addr;
|
|
}
|
|
}
|
|
|
|
uint64_t
|
|
Syscall_MUnmap(uint64_t addr, uint64_t len)
|
|
{
|
|
Thread *cur = Sched_Current();
|
|
uint64_t p;
|
|
|
|
for (p = 0; p < len; p += PGSIZE)
|
|
{
|
|
// Free page
|
|
}
|
|
|
|
PMap_Unmap(cur->space, addr, len /= PGSIZE);
|
|
Thread_Release(cur);
|
|
|
|
return 0;
|
|
}
|
|
|
|
uint64_t
|
|
Syscall_MProtect(uint64_t addr, uint64_t len, uint64_t prot)
|
|
{
|
|
//Thread *cur = Sched_Current();
|
|
NOT_IMPLEMENTED();
|
|
return 0;
|
|
}
|
|
|
|
uint64_t
|
|
Syscall_Read(uint64_t fd, uint64_t addr, uint64_t off, uint64_t length)
|
|
{
|
|
uint64_t status;
|
|
Thread *cur = Sched_Current();
|
|
Handle *handle = Handle_Lookup(cur->proc, fd);
|
|
|
|
if (handle == NULL) {
|
|
status = -EBADF;
|
|
} else {
|
|
status = (handle->read)(handle, (void *)addr, off, length);
|
|
}
|
|
|
|
Thread_Release(cur);
|
|
|
|
return status;
|
|
}
|
|
|
|
uint64_t
|
|
Syscall_Write(uint64_t fd, uint64_t addr, uint64_t off, uint64_t length)
|
|
{
|
|
uint64_t status;
|
|
Thread *cur = Sched_Current();
|
|
Handle *handle = Handle_Lookup(cur->proc, fd);
|
|
|
|
if (handle == NULL) {
|
|
status = -EBADF;
|
|
} else {
|
|
status = (handle->write)(handle, (void *)addr, off, length);
|
|
}
|
|
|
|
Thread_Release(cur);
|
|
|
|
return status;
|
|
}
|
|
|
|
uint64_t
|
|
Syscall_Flush(uint64_t fd)
|
|
{
|
|
uint64_t status;
|
|
Thread *cur = Sched_Current();
|
|
Handle *handle = Handle_Lookup(cur->proc, fd);
|
|
|
|
if (handle == NULL) {
|
|
status = -EBADF;
|
|
} else {
|
|
status = (handle->flush)(handle);
|
|
}
|
|
|
|
Thread_Release(cur);
|
|
|
|
return status;
|
|
}
|
|
|
|
// XXX: Cleanup
|
|
Handle *Console_OpenHandle();
|
|
|
|
uint64_t
|
|
Syscall_Open(uint64_t user_path, uint64_t flags)
|
|
{
|
|
uint64_t handleNo;
|
|
Thread *cur = Sched_Current();
|
|
int status;
|
|
char path[256];
|
|
|
|
status = CopyStrIn(user_path, &path, sizeof(path));
|
|
if (status != 0) {
|
|
Thread_Release(cur);
|
|
return status;
|
|
}
|
|
|
|
if (strncmp("/dev/", path, 5) == 0) {
|
|
if (strcmp("/dev/console", path) == 0) {
|
|
Handle *handle = Console_OpenHandle();
|
|
handleNo = Handle_Add(cur->proc, handle);
|
|
Thread_Release(cur);
|
|
return handleNo;
|
|
}
|
|
|
|
Thread_Release(cur);
|
|
return -ENOENT;
|
|
}
|
|
|
|
Handle *handle;
|
|
status = VFSUIO_Open(path, &handle);
|
|
if (status != 0) {
|
|
Thread_Release(cur);
|
|
return status;
|
|
}
|
|
|
|
handleNo = Handle_Add(cur->proc, handle);
|
|
Thread_Release(cur);
|
|
return handleNo;
|
|
}
|
|
|
|
uint64_t
|
|
Syscall_Close(uint64_t fd)
|
|
{
|
|
uint64_t status;
|
|
Thread *cur = Sched_Current();
|
|
Handle *handle = Handle_Lookup(cur->proc, fd);
|
|
|
|
if (handle == NULL) {
|
|
status = -EBADF;
|
|
} else {
|
|
status = (handle->close)(handle);
|
|
}
|
|
|
|
Thread_Release(cur);
|
|
|
|
return status;
|
|
}
|
|
|
|
uint64_t
|
|
Syscall_Stat(uint64_t user_path, uint64_t user_stat)
|
|
{
|
|
int status;
|
|
char path[256];
|
|
struct stat sb;
|
|
|
|
status = CopyStrIn(user_path, &path, sizeof(path));
|
|
if (status != 0) {
|
|
return status;
|
|
}
|
|
|
|
// VFS_Stat
|
|
status = VFS_Stat(path, &sb);
|
|
if (status != 0) {
|
|
return status;
|
|
}
|
|
|
|
status = CopyOut(&sb, user_stat, sizeof(struct stat));
|
|
if (status != 0) {
|
|
return status;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
uint64_t
|
|
Syscall_ReadDir(uint64_t fd, char *user_buf, size_t len, uintptr_t user_off)
|
|
{
|
|
int status, rstatus;
|
|
Thread *cur = Sched_Current();
|
|
Handle *handle = Handle_Lookup(cur->proc, fd);
|
|
uint64_t offset;
|
|
|
|
if (handle == NULL) {
|
|
Thread_Release(cur);
|
|
return -EBADF;
|
|
}
|
|
|
|
status = CopyIn(user_off, &offset, sizeof(offset));
|
|
if (status != 0) {
|
|
Thread_Release(cur);
|
|
return status;
|
|
}
|
|
|
|
if (handle->type != HANDLE_TYPE_FILE) {
|
|
Thread_Release(cur);
|
|
return -ENOTDIR;
|
|
}
|
|
|
|
rstatus = VFS_ReadDir(handle->vnode, user_buf, len, &offset);
|
|
if (rstatus < 0) {
|
|
Thread_Release(cur);
|
|
return rstatus;
|
|
}
|
|
|
|
status = CopyOut(&offset, user_off, sizeof(offset));
|
|
if (status != 0) {
|
|
Thread_Release(cur);
|
|
return status;
|
|
}
|
|
|
|
Thread_Release(cur);
|
|
|
|
return rstatus;
|
|
}
|
|
|
|
uint64_t
|
|
Syscall_ThreadCreate(uint64_t rip, uint64_t arg)
|
|
{
|
|
uint64_t threadId;
|
|
Thread *curThread = Sched_Current();
|
|
Thread *newThread = Thread_UThreadCreate(curThread, rip, arg);
|
|
|
|
Thread_Release(curThread);
|
|
if (newThread == NULL) {
|
|
return -1;
|
|
}
|
|
|
|
threadId = newThread->tid;
|
|
Sched_SetRunnable(newThread);
|
|
|
|
return threadId;
|
|
}
|
|
|
|
void
|
|
Syscall_ThreadExit(uint64_t status)
|
|
{
|
|
Thread *cur = Sched_Current();
|
|
|
|
// Encode this like POSIX
|
|
cur->exitValue = status;
|
|
|
|
Sched_SetZombie(cur);
|
|
Semaphore_Release(&cur->proc->zombieSemaphore);
|
|
Thread_Release(cur);
|
|
Sched_Scheduler();
|
|
|
|
// Should not return
|
|
Panic("Returned to exited thread!\n");
|
|
}
|
|
|
|
static void
|
|
ThreadWakeupHelper(void *arg)
|
|
{
|
|
Thread *thr = (Thread *)arg;
|
|
|
|
Sched_SetRunnable(thr);
|
|
KTimer_Release(thr->timerEvt);
|
|
thr->timerEvt = NULL;
|
|
Thread_Release(thr);
|
|
}
|
|
|
|
uint64_t
|
|
Syscall_ThreadSleep(uint64_t time)
|
|
{
|
|
Thread *cur = Sched_Current();
|
|
|
|
// If the sleep time is zero just yield
|
|
if (time != 0) {
|
|
cur->timerEvt = KTimer_Create(time, ThreadWakeupHelper, cur);
|
|
if (cur->timerEvt == NULL)
|
|
return -ENOMEM;
|
|
|
|
Sched_SetWaiting(cur);
|
|
}
|
|
Sched_Scheduler();
|
|
|
|
Thread_Release(cur);
|
|
|
|
return 0;
|
|
}
|
|
|
|
uint64_t
|
|
Syscall_ThreadWait(uint64_t tid)
|
|
{
|
|
int status;
|
|
Thread *cur = Sched_Current();
|
|
|
|
/*
|
|
* Acquire the zombie semaphore see if the specified thread has exited or
|
|
* any thread if tid == 0. If the specified thread hasn't exited wait
|
|
* again on the semaphore. POSIX does not give any guarentees if multiple
|
|
* threads wait on the same thread and neither do we.
|
|
*
|
|
* As a precaution we call Sched_Scheduler to prevent looping on the
|
|
* semaphore acquire-release.
|
|
*/
|
|
while (1) {
|
|
Semaphore_Acquire(&cur->proc->zombieSemaphore);
|
|
status = Thread_Wait(cur, tid);
|
|
if (status != 0) {
|
|
Thread_Release(cur);
|
|
return status;
|
|
}
|
|
Semaphore_Release(&cur->proc->zombieSemaphore);
|
|
Sched_Scheduler();
|
|
}
|
|
}
|
|
|
|
uint64_t
|
|
Syscall_NICStat(uint64_t nicNo, uint64_t user_stat)
|
|
{
|
|
int status;
|
|
NIC *nic;
|
|
|
|
nic = NIC_GetByID(nicNo);
|
|
if (nic == NULL) {
|
|
return ENOENT;
|
|
}
|
|
|
|
status = CopyOut(nic, user_stat, sizeof(NIC));
|
|
if (status != 0) {
|
|
return status;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
uint64_t
|
|
Syscall_NICSend(uint64_t nicNo, uint64_t user_mbuf)
|
|
{
|
|
return SYSCALL_PACK(ENOSYS, 0);
|
|
}
|
|
|
|
uint64_t
|
|
Syscall_NICRecv(uint64_t nicNo, uint64_t user_mbuf)
|
|
{
|
|
int status;
|
|
NIC *nic;
|
|
MBuf mbuf;
|
|
|
|
status = CopyIn(user_mbuf, &mbuf, sizeof(mbuf));
|
|
if (status != 0) {
|
|
return SYSCALL_PACK(status, 0);
|
|
}
|
|
|
|
nic = NIC_GetByID(nicNo);
|
|
if (nic == NULL) {
|
|
return SYSCALL_PACK(ENOENT, 0);
|
|
}
|
|
|
|
// Pin Memory
|
|
(nic->rx)(nic, &mbuf, NULL, NULL);
|
|
// Unpin Memory
|
|
|
|
return 0;
|
|
}
|
|
|
|
uint64_t
|
|
Syscall_Entry(uint64_t syscall, uint64_t a1, uint64_t a2,
|
|
uint64_t a3, uint64_t a4, uint64_t a5)
|
|
{
|
|
switch (syscall)
|
|
{
|
|
case SYSCALL_NULL:
|
|
return 0;
|
|
case SYSCALL_TIME:
|
|
return Syscall_Time();
|
|
case SYSCALL_GETPID:
|
|
return Syscall_GetPID();
|
|
case SYSCALL_EXIT:
|
|
Syscall_Exit(a1);
|
|
return 0; // To eliminate warning
|
|
case SYSCALL_SPAWN:
|
|
return Syscall_Spawn(a1);
|
|
case SYSCALL_WAIT:
|
|
return Syscall_Wait(a1);
|
|
case SYSCALL_MMAP:
|
|
return Syscall_MMap(a1, a2, a3);
|
|
case SYSCALL_MUNMAP:
|
|
return Syscall_MUnmap(a1, a2);
|
|
case SYSCALL_MPROTECT:
|
|
return Syscall_MProtect(a1, a2, a3);
|
|
case SYSCALL_READ:
|
|
return Syscall_Read(a1, a2, a3, a4);
|
|
case SYSCALL_WRITE:
|
|
return Syscall_Write(a1, a2, a3, a4);
|
|
case SYSCALL_FLUSH:
|
|
return Syscall_Flush(a1);
|
|
case SYSCALL_OPEN:
|
|
return Syscall_Open(a1, a2);
|
|
case SYSCALL_CLOSE:
|
|
return Syscall_Close(a1);
|
|
case SYSCALL_STAT:
|
|
return Syscall_Stat(a1, a2);
|
|
case SYSCALL_READDIR:
|
|
return Syscall_ReadDir(a1, (char *)a2, a3, a4);
|
|
case SYSCALL_THREADCREATE:
|
|
return Syscall_ThreadCreate(a1, a2);
|
|
case SYSCALL_THREADEXIT:
|
|
Syscall_ThreadExit(a1);
|
|
return 0;
|
|
case SYSCALL_THREADSLEEP:
|
|
return Syscall_ThreadSleep(a1);
|
|
case SYSCALL_THREADWAIT:
|
|
return Syscall_ThreadWait(a1);
|
|
case SYSCALL_NICSTAT:
|
|
return Syscall_NICStat(a1, a2);
|
|
case SYSCALL_NICSEND:
|
|
return Syscall_NICSend(a1, a2);
|
|
case SYSCALL_NICRECV:
|
|
return Syscall_NICRecv(a1, a2);
|
|
default:
|
|
return (uint64_t)-1;
|
|
}
|
|
}
|
|
|