A toy operating system written in C.
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
freywaros/src/kernel/syscall.c

224 lines
5.8 KiB

#include "src/kernel/syscall.h"
#include "src/kernel/fs.h"
#include "src/kernel/gdt.h"
#include "src/kernel/path.h"
#include "src/kernel/process.h"
#include "src/kernel/stream.h"
#include "src/kernel/tss.h"
#include "src/lib/layout.h"
#include "src/lib/memory.h"
#include "src/lib/string.h"
#include "src/lib/util.h"
#define MSR_EFER 0xC0000080
#define MSR_STAR 0xC0000081
#define MSR_LSTAR 0xC0000082
#define MSR_FMASK 0xC0000084
static inline uint64_t rdmsr(uint32_t msr) {
uint32_t lo, hi;
__asm__ volatile("rdmsr" : "=a"(lo), "=d"(hi) : "c"(msr));
return ((uint64_t)hi << 32) | lo;
}
static inline void wrmsr(uint32_t msr, uint64_t value) {
__asm__ volatile("wrmsr" : : "c"(msr), "a"((uint32_t)value), "d"((uint32_t)(value >> 32)));
}
extern void syscall_entry();
void syscall_init() {
wrmsr(MSR_EFER, rdmsr(MSR_EFER) | 0x01);
wrmsr(MSR_STAR, ((uint64_t)KERNEL_DS << 48) | ((uint64_t)KERNEL_CS << 32));
wrmsr(MSR_LSTAR, (uint64_t)syscall_entry);
wrmsr(MSR_FMASK, 0x200);
}
static uint64_t read(uint64_t fd, uint64_t max, char *to) {
if (fd >= MAX_FDS || !current_process->fds[fd]) {
return (uint64_t)-1;
}
return current_process->fds[fd]->read(current_process->fds[fd], max, to);
}
static uint64_t write(uint64_t fd, const char *from, uint64_t bytes) {
if (fd >= MAX_FDS || !current_process->fds[fd]) {
return (uint64_t)-1;
}
return current_process->fds[fd]->write(current_process->fds[fd], from, bytes);
}
static uint64_t getcwd(uint64_t max, char *to) {
if (max == 0) {
return 0;
}
if (max == 1) {
to[0] = '\0';
return 0;
}
if (max == 2 || !current_process->cwd->depth) {
to[0] = '/';
to[1] = '\0';
return 1;
}
uint64_t i = 0;
for (uint8_t j = 0; j < current_process->cwd->depth; j++) {
uint64_t l = string_length(current_process->cwd->stack[j]->name);
if (i + l + 1 > max) {
break;
}
to[i++] = '/';
memory_copy(current_process->cwd->stack[j]->name, l, to + i);
i += l;
}
to[i] = '\0';
return i;
}
static uint64_t chdir(const char *path) {
path_t *cwd = path_open(current_process->root, current_process->cwd, path);
if (!cwd) {
return (uint64_t)-1;
}
if (!cwd->depth || !cwd->stack[cwd->depth - 1]->is_dir) {
path_close(cwd);
return (uint64_t)-1;
}
path_close((path_t *)current_process->cwd);
current_process->cwd = cwd;
return current_process->cwd->depth;
}
static uint64_t spawn(const char *path, uint64_t argc, const char **argv) {
fs_node_t *node = path_open_file(current_process->root, current_process->cwd, path);
if (!node) {
return EXIT_CODE_NOT_FOUND;
}
uint8_t *bin = memory_allocate(node->size);
fs_read(node, 0, node->size, bin);
uint64_t size = 0;
uint64_t sizes[16];
uint64_t offsets[16];
for (uint64_t i = 0; i < argc; i++) {
offsets[i] = size;
size += (sizes[i] = string_length(argv[i]) + 1);
}
char *blob = memory_allocate(size);
for (uint64_t i = 0; i < argc; i++) {
memory_copy(argv[i], sizes[i], blob + offsets[i]);
}
process_t *child = process_create(current_process, bin, node->size);
memory_free(bin);
fs_close(node);
__asm__ volatile("mov %0, %%cr3" : : "r"((uint64_t)VIRT_TO_PHYS((void *)KERNEL_VIRTUAL_PML4)) : "memory");
uint8_t *stack = (uint8_t *)child->user_stack;
stack -= size;
memory_copy(blob, size, stack);
memory_free(blob);
stack = (uint8_t *)((uint64_t)stack & ~7ULL);
stack -= argc * sizeof(char *);
for (uint64_t i = 0; i < argc; i++) {
((char **)stack)[i] = (char *)(USER_VIRTUAL_STACK_TOP - size + offsets[i]);
}
stack -= sizeof(uint64_t);
*(uint64_t *)stack = argc;
child->user_rsp = (void *)(USER_VIRTUAL_STACK_TOP - ((uint64_t)child->user_stack - (uint64_t)stack));
__asm__ volatile("mov %0, %%cr3" : : "r"((uint64_t)VIRT_TO_PHYS((void *)current_process->pml4)) : "memory");
return child->pid;
}
static exit_code_t wait(uint64_t pid) {
process_t *child = process_get(pid);
if (!child) {
return EXIT_CODE_GENERAL_FAILURE;
}
current_process->state = PROCESS_WAITING;
current_process->waiting_for = pid;
while (child->state != PROCESS_ZOMBIE) {
process_next();
}
current_process->state = PROCESS_RUNNING;
exit_code_t code = child->code;
process_destroy(child);
return code;
}
static uint64_t open(const char *path) {
if (current_process->free_fd >= MAX_FDS) {
return (uint64_t)-1;
}
stream_t *stream = path_open_stream(current_process->root, current_process->cwd, path);
if (!stream) {
return (uint64_t)-1;
}
current_process->fds[current_process->free_fd++] = stream;
return current_process->free_fd - 1;
}
static uint64_t close(uint64_t fd) {
if (fd >= MAX_FDS || !current_process->fds[fd]) {
return (uint64_t)-1;
}
current_process->fds[fd]->close(current_process->fds[fd]);
current_process->fds[fd] = NUL;
return 0;
}
static void exit(exit_code_t code) {
current_process->state = PROCESS_ZOMBIE;
current_process->code = code;
if (current_process->parent) {
process_t *parent = (process_t *)current_process->parent;
if (parent->state == PROCESS_WAITING && parent->waiting_for == current_process->pid) {
parent->state = PROCESS_RUNNING;
}
}
process_next();
}
uint64_t syscall_dispatch(uint64_t func, uint64_t arg1, uint64_t arg2, uint64_t arg3, __attribute__((unused)) uint64_t arg4) {
switch (func) {
case SYSCALL_READ:
return read(arg1, arg2, (char *)arg3);
case SYSCALL_WRITE:
return write(arg1, (const char *)arg2, arg3);
case SYSCALL_GETCWD:
return getcwd(arg1, (char *)arg2);
case SYSCALL_CHDIR:
return chdir((const char *)arg1);
case SYSCALL_SPAWN:
return spawn((const char *)arg1, arg2, (const char **)arg3);
case SYSCALL_WAIT:
return wait(arg1);
case SYSCALL_OPEN:
return open((const char *)arg1);
case SYSCALL_CLOSE:
return close(arg1);
case SYSCALL_EXIT:
exit(arg1);
return 0;
default:
return (uint64_t)-1;
}
}