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.
250 lines
6.8 KiB
250 lines
6.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/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) {
|
|
switch (fd) {
|
|
case 0:
|
|
return current_process->stdin->read(current_process->stdin, max, to);
|
|
case 1:
|
|
return current_process->stdout->read(current_process->stdout, max, to);
|
|
default:
|
|
if (!current_process->fds[fd].node) {
|
|
return (uint64_t)-1;
|
|
}
|
|
if (current_process->fds[fd].offset >= current_process->fds[fd].node->size) {
|
|
return 0;
|
|
}
|
|
uint64_t remaining = current_process->fds[fd].node->size - current_process->fds[fd].offset;
|
|
uint64_t to_read = remaining > max ? max : remaining;
|
|
fs_read(current_process->fds[fd].node, current_process->fds[fd].offset, to_read, to);
|
|
current_process->fds[fd].offset += to_read;
|
|
return to_read;
|
|
}
|
|
return (uint64_t)-1;
|
|
}
|
|
|
|
static uint64_t write(uint64_t fd, const char *from, uint64_t bytes) {
|
|
switch (fd) {
|
|
case 0:
|
|
return current_process->stdin->write(current_process->stdin, from, bytes);
|
|
case 1:
|
|
return current_process->stdout->write(current_process->stdout, from, bytes);
|
|
default:
|
|
return (uint64_t)-1;
|
|
}
|
|
return (uint64_t)-1;
|
|
}
|
|
|
|
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 (uint64_t)-1;
|
|
}
|
|
uint8_t *code = memory_allocate(node->size);
|
|
fs_read(node, 0, node->size, code);
|
|
|
|
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, code, node->size);
|
|
|
|
memory_free(code);
|
|
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));
|
|
|
|
process_t *parent = current_process;
|
|
current_process = child;
|
|
tss.rsp0 = (uint64_t)child->kernel_stack;
|
|
|
|
process_switch_to(&parent->kernel_rsp, child->kernel_rsp, VIRT_TO_PHYS(child->pml4));
|
|
|
|
tss.rsp0 = (uint64_t)parent->kernel_stack;
|
|
current_process = parent;
|
|
process_destroy(child);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static uint64_t readdir(const char *path, uint64_t index, uint64_t max, char *to) {
|
|
fs_node_t *dir = path_open_directory(current_process->root, current_process->cwd, path);
|
|
if (!dir) {
|
|
return (uint64_t)-1;
|
|
}
|
|
fs_node_t *item = fs_open_at(dir, index);
|
|
if (!item) {
|
|
if (dir != current_process->root) {
|
|
fs_close(dir);
|
|
}
|
|
return 0;
|
|
}
|
|
uint64_t length = string_length(item->name);
|
|
uint64_t size = length > max - 1 ? max - 1 : length;
|
|
memory_copy(item->name, size, to);
|
|
to[size] = '\0';
|
|
|
|
fs_close(item);
|
|
if (dir != current_process->root) {
|
|
fs_close(dir);
|
|
}
|
|
|
|
return size;
|
|
}
|
|
|
|
static uint64_t open(const char *path) {
|
|
if (current_process->free_fd >= MAX_FDS) {
|
|
return (uint64_t)-1;
|
|
}
|
|
fs_node_t *node = path_open_node(current_process->root, current_process->cwd, path);
|
|
if (!node) {
|
|
return (uint64_t)-1;
|
|
}
|
|
current_process->fds[current_process->free_fd].node = node;
|
|
current_process->fds[current_process->free_fd].offset++;
|
|
current_process->free_fd++;
|
|
return current_process->free_fd - 1;
|
|
}
|
|
|
|
static uint64_t close(uint64_t fd) {
|
|
if (fd >= MAX_FDS || !current_process->fds[fd].node) {
|
|
return (uint64_t)-1;
|
|
}
|
|
fs_close(current_process->fds[fd].node);
|
|
current_process->fds[fd].node = NUL;
|
|
current_process->fds[fd].offset = 0;
|
|
return 0;
|
|
}
|
|
|
|
static void exit() {
|
|
process_switch_to(¤t_process->kernel_rsp, current_process->parent->kernel_rsp, VIRT_TO_PHYS(current_process->parent->pml4));
|
|
}
|
|
|
|
uint64_t syscall_dispatch(uint64_t func, uint64_t arg1, uint64_t arg2, uint64_t arg3, 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_READDIR:
|
|
return readdir((const char *)arg1, arg2, arg3, (char *)arg4);
|
|
case SYSCALL_OPEN:
|
|
return open((const char *)arg1);
|
|
case SYSCALL_CLOSE:
|
|
return close(arg1);
|
|
case SYSCALL_EXIT:
|
|
exit();
|
|
return 0;
|
|
default:
|
|
return (uint64_t)-1;
|
|
}
|
|
}
|
|
|