toaruos/kernel/sys/syscall.c
Kevin Lange 16da56ea9b General support for open() flags
Includes truncation support in tmpfs, and changes the signure of the VFS
open() method. Also tweaked some comment style in the VFS.
2013-04-24 00:19:08 -07:00

778 lines
19 KiB
C

/* vim: tabstop=4 shiftwidth=4 noexpandtab
*
* Syscall Tables
*
*/
#include <system.h>
#include <process.h>
#include <logging.h>
#include <fs.h>
#include <pipe.h>
#include <version.h>
#include <shm.h>
#include <utsname.h>
#define SPECIAL_CASE_STDIO
#define RESERVED 1
static char hostname[256];
static size_t hostname_len = 0;
/*
* System calls themselves
*/
void validate(void * ptr) {
if (validate_safe(ptr)) {
debug_print(ERROR, "SEGFAULT: Invalid pointer passed to syscall. (0x%x < 0x%x)", (uintptr_t)ptr, current_process->image.entry);
HALT_AND_CATCH_FIRE("Segmentation fault", NULL);
}
}
int validate_safe(void * ptr) {
if (ptr && (uintptr_t)ptr < current_process->image.entry) {
return 1;
}
return 0;
}
/*
* print something to the debug terminal (serial)
*/
static int print(char * s) {
validate((void *)s);
kprintf("%s", s);
return 0;
}
/*
* Exit the current task.
* DOES NOT RETURN!
*/
static int exit(int retval) {
/* Deschedule the current task */
task_exit(retval);
while (1) { };
return retval;
}
static int read(int fd, char * ptr, int len) {
if (fd >= (int)current_process->fds->length || fd < 0) {
return -1;
}
if (current_process->fds->entries[fd] == NULL) {
return -1;
}
validate(ptr);
fs_node_t * node = current_process->fds->entries[fd];
uint32_t out = read_fs(node, node->offset, len, (uint8_t *)ptr);
node->offset += out;
return out;
}
static int ioctl(int fd, int request, void * argp) {
if (fd >= (int)current_process->fds->length || fd < 0) {
return -1;
}
if (current_process->fds->entries[fd] == NULL) {
return -1;
}
validate(argp);
fs_node_t * node = current_process->fds->entries[fd];
return ioctl_fs(node, request, argp);
}
static int readdir(int fd, int index, struct dirent * entry) {
if (fd >= (int)current_process->fds->length || fd < 0) {
return -1;
}
if (current_process->fds->entries[fd] == NULL) {
return -1;
}
validate(entry);
fs_node_t * node = current_process->fds->entries[fd];
struct dirent * kentry = readdir_fs(node, (uint32_t)index);
if (!kentry) {
free(kentry);
return 1;
}
memcpy(entry, kentry, sizeof(struct dirent));
free(kentry);
return 0;
}
static int write(int fd, char * ptr, int len) {
if ((fd == 1 && !current_process->fds->entries[fd]) ||
(fd == 2 && !current_process->fds->entries[fd])) {
for (uint32_t i = 0; i < (uint32_t)len; ++i) {
kprintf("%c", ptr[i]);
}
return len;
}
if (fd >= (int)current_process->fds->length || fd < 0) {
return -1;
}
if (current_process->fds->entries[fd] == NULL) {
return -1;
}
validate(ptr);
fs_node_t * node = current_process->fds->entries[fd];
uint32_t out = write_fs(node, node->offset, len, (uint8_t *)ptr);
node->offset += out;
return out;
}
static int wait(int child) {
process_t * volatile child_task;
if (child == 0) {
debug_print_process_tree();
child_task = process_get_first_child((process_t *)current_process);
} else if (child < 1) {
debug_print(WARNING, "Process %d requested group wait, which we can not do!", getpid());
return 0;
} else {
child_task = process_from_pid(child);
}
/* If the child task doesn't exist, bail */
if (!child_task) {
debug_print(WARNING, "Tried to wait for non-existent process");
return 0;
}
debug_print(NOTICE, "pid=%d waiting on pid=%d", current_process->id, child_task->id);
while (child_task->finished == 0) {
/* Add us to the wait queue for this child */
sleep_on(child_task->wait_queue);
}
/* Grab the child's return value */
int ret = child_task->status;
return ret;
}
static int open(const char * file, int flags, int mode) {
validate((void *)file);
debug_print(NOTICE, "open(%s) flags=0x%x; mode=0x%x", file, flags, mode);
fs_node_t * node = kopen((char *)file, flags);
if (!node && (flags & O_CREAT)) {
debug_print(NOTICE, "- file does not exist and create was requested.");
/* Um, make one */
if (!create_file_fs((char *)file, mode)) {
node = kopen((char *)file, flags);
}
}
if (!node) {
debug_print(NOTICE, "File does not exist; someone should be setting errno?");
return -1;
}
node->offset = 0;
int fd = process_append_fd((process_t *)current_process, node);
debug_print(INFO, "[open] pid=%d %s -> %d", getpid(), file, fd);
return fd;
}
static int access(const char * file, int flags) {
validate((void *)file);
debug_print(INFO, "access(%s, 0x%x) from pid=%d", file, flags, getpid());
fs_node_t * node = kopen((char *)file, 0);
if (!node) return -1;
close_fs(node);
return 0;
}
static int close(int fd) {
if (fd >= (int)current_process->fds->length || fd < 0) {
return -1;
}
close_fs(current_process->fds->entries[fd]);
return 0;
}
static int sys_sbrk(int size) {
uintptr_t ret = current_process->image.heap;
uintptr_t i_ret = ret;
while (ret % 0x1000) {
ret++;
}
current_process->image.heap += (ret - i_ret) + size;
while (current_process->image.heap > current_process->image.heap_actual) {
current_process->image.heap_actual += 0x1000;
assert(current_process->image.heap_actual % 0x1000 == 0);
alloc_frame(get_page(current_process->image.heap_actual, 1, current_directory), 0, 1);
}
return ret;
}
static int sys_getpid() {
/* The user actually wants the pid of the originating thread (which can be us). */
if (current_process->group) {
return current_process->group;
} else {
/* We are the leader */
return current_process->id;
}
}
/* Actual getpid() */
static int gettid() {
return getpid();
}
static int execve(const char * filename, char *const argv[], char *const envp[]) {
validate((void *)argv);
validate((void *)filename);
validate((void *)envp);
debug_print(NOTICE, "%d = exec(%s, ...)", current_process->id, filename);
int argc = 0, envc = 0;
while (argv[argc]) { ++argc; }
if (envp) {
while (envp[envc]) { ++envc; }
}
debug_print(INFO, "Allocating space for arguments...");
char ** argv_ = malloc(sizeof(char *) * (argc + 1));
for (int j = 0; j < argc; ++j) {
argv_[j] = malloc((strlen(argv[j]) + 1) * sizeof(char));
memcpy(argv_[j], argv[j], strlen(argv[j]) + 1);
}
argv_[argc] = 0;
char ** envp_;
if (envp && envc) {
envp_ = malloc(sizeof(char *) * (envc + 1));
for (int j = 0; j < envc; ++j) {
envp_[j] = malloc((strlen(envp[j]) + 1) * sizeof(char));
memcpy(envp_[j], envp[j], strlen(envp[j]) + 1);
}
envp_[envc] = 0;
} else {
envp_ = malloc(sizeof(char *));
envp_[0] = NULL;
}
debug_print(INFO,"Releasing all shmem regions...");
shm_release_all((process_t *)current_process);
debug_print(INFO,"Executing...");
/* Discard envp */
exec((char *)filename, argc, (char **)argv_, (char **)envp_);
return -1;
}
static int getgraphicsaddress() {
return (int)lfb_get_address();
}
static int seek(int fd, int offset, int whence) {
if (fd >= (int)current_process->fds->length || fd < 0) {
return -1;
}
if (fd < 3) {
return 0;
}
if (whence == 0) {
current_process->fds->entries[fd]->offset = offset;
} else if (whence == 1) {
current_process->fds->entries[fd]->offset += offset;
} else if (whence == 2) {
current_process->fds->entries[fd]->offset = current_process->fds->entries[fd]->length + offset;
}
return current_process->fds->entries[fd]->offset;
}
static int stat_node(fs_node_t * fn, uintptr_t st) {
struct stat * f = (struct stat *)st;
if (!fn) {
memset(f, 0x00, sizeof(struct stat));
debug_print(INFO, "stat: This file doesn't exist");
return -1;
}
f->st_dev = 0;
f->st_ino = fn->inode;
uint32_t flags = 0;
if (fn->flags & FS_FILE) { flags |= _IFREG; }
if (fn->flags & FS_DIRECTORY) { flags |= _IFDIR; }
if (fn->flags & FS_CHARDEVICE) { flags |= _IFCHR; }
if (fn->flags & FS_BLOCKDEVICE) { flags |= _IFBLK; }
if (fn->flags & FS_PIPE) { flags |= _IFIFO; }
if (fn->flags & FS_SYMLINK) { flags |= _IFLNK; }
f->st_mode = fn->mask | flags;
f->st_nlink = 0;
f->st_uid = fn->uid;
f->st_gid = fn->gid;
f->st_rdev = 0;
f->st_size = fn->length;
f->st_atime = fn->atime;
f->st_mtime = fn->mtime;
f->st_ctime = fn->ctime;
if (fn->get_size) {
f->st_size = fn->get_size(fn);
}
return 0;
}
static int stat_file(char * file, uintptr_t st) {
int result;
validate((void *)file);
validate((void *)st);
fs_node_t * fn = kopen(file, 0);
result = stat_node(fn, st);
if (fn) {
close_fs(fn);
}
return result;
}
static int sys_chmod(char * file, int mode) {
int result;
validate((void *)file);
fs_node_t * fn = kopen(file, 0);
if (fn) {
result = chmod_fs(fn, mode);
close_fs(fn);
return result;
} else {
return -1;
}
}
static int stat(int fd, uintptr_t st) {
validate((void *)st);
if (fd >= (int)current_process->fds->length || fd < 0) {
return -1;
}
fs_node_t * fn = current_process->fds->entries[fd];
return stat_node(fn, st);
}
static int setgraphicsoffset(int rows) {
bochs_set_y_offset(rows);
return 0;
}
static int getgraphicswidth() {
return lfb_resolution_x;
}
static int getgraphicsheight() {
return lfb_resolution_y;
}
static int getgraphicsdepth() {
return lfb_resolution_b;
}
static int mkpipe() {
fs_node_t * node = make_pipe(4096 * 2);
return process_append_fd((process_t *)current_process, node);
}
static int dup2(int old, int new) {
process_move_fd((process_t *)current_process, old, new);
return new;
}
static int getuid() {
return current_process->user;
}
static int setuid(user_t new_uid) {
if (current_process->user == USER_ROOT_UID) {
current_process->user = new_uid;
return 0;
}
return -1;
}
static int kernel_name_XXX(char * buffer) {
char version_number[1024];
sprintf(version_number, __kernel_version_format,
__kernel_version_major,
__kernel_version_minor,
__kernel_version_lower,
__kernel_version_suffix);
return sprintf(buffer, "%s %s %s %s %s %s",
__kernel_name,
version_number,
__kernel_version_codename,
__kernel_build_date,
__kernel_build_time,
__kernel_arch);
}
static int uname(struct utsname * name) {
validate((void *)name);
char version_number[256];
sprintf(version_number, __kernel_version_format,
__kernel_version_major,
__kernel_version_minor,
__kernel_version_lower,
__kernel_version_suffix);
char version_string[256];
sprintf(version_string, "%s %s %s",
__kernel_version_codename,
__kernel_build_date,
__kernel_build_time);
strcpy(name->sysname, __kernel_name);
strcpy(name->nodename, hostname);
strcpy(name->release, version_number);
strcpy(name->version, version_string);
strcpy(name->machine, __kernel_arch);
strcpy(name->domainname, "");
return 0;
}
static int send_signal(pid_t process, uint32_t signal) {
process_t * receiver = process_from_pid(process);
if (!receiver) {
/* Invalid pid */
return 1;
}
if (receiver->user != current_process->user && current_process->user != USER_ROOT_UID) {
/* No way in hell. */
return 1;
}
if (signal > NUMSIGNALS) {
/* Invalid signal */
return 1;
}
if (receiver->finished) {
/* Can't send signals to finished processes */
return 1;
}
/* Append signal to list */
signal_t * sig = malloc(sizeof(signal_t));
sig->handler = (uintptr_t)receiver->signals.functions[signal];
sig->signum = signal;
memset(&sig->registers_before, 0x00, sizeof(regs_t));
if (!process_is_ready(receiver)) {
make_process_ready(receiver);
}
list_insert(receiver->signal_queue, sig);
return 0;
}
static uintptr_t sys_signal(uint32_t signum, uintptr_t handler) {
if (signum > NUMSIGNALS) {
return -1;
}
uintptr_t old = current_process->signals.functions[signum];
current_process->signals.functions[signum] = handler;
return old;
}
/*
static void inspect_memory (uintptr_t vaddr) {
// Please use this scary hack of a function as infrequently as possible.
shmem_debug_frame(vaddr);
}
*/
extern void ext2_disk_sync();
static int reboot() {
debug_print(NOTICE, "[kernel] Reboot requested from process %d by user #%d", current_process->id, current_process->user);
if (current_process->user != USER_ROOT_UID) {
return -1;
} else {
debug_print(NOTICE, "[kernel] Good bye!");
ext2_disk_sync();
/* Goodbye, cruel world */
IRQ_OFF;
uint8_t out = 0x02;
while ((out & 0x02) != 0) {
out = inportb(0x64);
}
outportb(0x64, 0xFE); /* Reset */
STOP;
}
return 0;
}
static int chdir(char * newdir) {
char * path = canonicalize_path(current_process->wd_name, newdir);
fs_node_t * chd = kopen(path, 0);
if (chd) {
if ((chd->flags & FS_DIRECTORY) == 0) {
return -1;
}
free(current_process->wd_name);
current_process->wd_name = malloc(strlen(path) + 1);
memcpy(current_process->wd_name, path, strlen(path) + 1);
return 0;
} else {
return -1;
}
}
static char * getcwd(char * buf, size_t size) {
if (!buf) return NULL;
validate((void *)buf);
memcpy(buf, current_process->wd_name, min(size, strlen(current_process->wd_name) + 1));
return buf;
}
static int sethostname(char * new_hostname) {
if (current_process->user == USER_ROOT_UID) {
size_t len = strlen(new_hostname) + 1;
if (len > 256) {
return 1;
}
hostname_len = len;
memcpy(hostname, new_hostname, hostname_len);
return 0;
} else {
return 1;
}
}
static int gethostname(char * buffer) {
memcpy(buffer, hostname, hostname_len);
return hostname_len;
}
static int mousedevice() {
extern fs_node_t * mouse_pipe;
return process_append_fd((process_t *)current_process, mouse_pipe);
}
static int open_serial(int device) {
return process_append_fd((process_t *)current_process, serial_device_create(device));
}
extern int mkdir_fs(char *name, uint16_t permission);
static int sys_mkdir(char * path, uint32_t mode) {
return mkdir_fs(path, 0777);
}
/**
* share_fd: Make a file descriptor available to another process.
*/
static uintptr_t share_fd(int fd, int pid) {
if (fd >= (int)current_process->fds->length || fd < 0) {
return 0;
}
fs_node_t * fn = current_process->fds->entries[fd];
fn->shared_with = pid;
return (uintptr_t)fn;
}
/**
* get_fd: Retreive a file descriptor (by key == pointer to fs_node_t) from
* another proces.
*/
static int get_fd(uintptr_t fn) {
fs_node_t * node = (fs_node_t *)fn;
if (node->shared_with == current_process->id || node->shared_with == current_process->group) {
return process_append_fd((process_t *)current_process, node);
} else {
return -1;
}
}
/*
* Yield the rest of the quantum;
* useful for busy waiting and other such things
*/
static int yield() {
switch_task(1);
return 1;
}
/*
* System Function
*/
static int system_function(int fn, char ** args) {
/* System Functions are special debugging system calls */
if (current_process->user == USER_ROOT_UID) {
switch (fn) {
case 1:
/* Print memory information */
/* Future: /proc/meminfo */
kprintf("Memory used: %d\n", memory_use());
kprintf("Memory available: %d\n", memory_total());
return 0;
case 2:
/* Print process tree */
/* Future: /proc in general */
debug_print_process_tree();
return 0;
case 3:
ext2_disk_sync();
return 0;
case 4:
/* Request kernel output to file descriptor in arg0*/
kprintf("Setting output to file object in process %d's fd=%d!\n", getpid(), (int)args);
kprint_to_file = current_process->fds->entries[(int)args];
break;
case 5:
debug_print(INFO, "replacing process %d's file descriptors with null devices", getpid());
fs_node_t * nulldev = null_device_create();
while (current_process->fds->length < 3) {
process_append_fd((process_t *)current_process, nulldev);
}
current_process->fds->entries[0] = nulldev;
current_process->fds->entries[1] = nulldev;
current_process->fds->entries[2] = nulldev;
break;
default:
kprintf("Bad system function %d\n", fn);
break;
}
}
return -1; /* Bad system function or access failure */
}
static int sleep(unsigned long seconds, unsigned long subseconds) {
/* Mark us as asleep until <some time period> */
sleep_until((process_t *)current_process, seconds, subseconds);
/* Switch without adding us to the queue */
switch_task(0);
if (seconds > timer_ticks || (seconds == timer_ticks && subseconds >= timer_subticks)) {
return 0;
} else {
return 1;
}
}
static int sleep_rel(unsigned long seconds, unsigned long subseconds) {
unsigned long s, ss;
relative_time(seconds, subseconds, &s, &ss);
return sleep(s, ss);
}
static int sys_umask(int mode) {
current_process->mask = mode & 0777;
return 0;
}
static int sys_unlink(char * file) {
return unlink_fs(file);
}
/*
* System Call Internals
*/
static void syscall_handler(struct regs * r);
static uintptr_t syscalls[] = {
/* System Call Table */
(uintptr_t)&exit, /* 0 */
(uintptr_t)&print,
(uintptr_t)&open,
(uintptr_t)&read,
(uintptr_t)&write, /* 4 */
(uintptr_t)&close,
(uintptr_t)&gettimeofday,
(uintptr_t)&execve,
(uintptr_t)&fork, /* 8 */
(uintptr_t)&sys_getpid,
(uintptr_t)&sys_sbrk,
(uintptr_t)&getgraphicsaddress,
(uintptr_t)&uname, /* 12 */
(uintptr_t)&openpty,
(uintptr_t)&seek,
(uintptr_t)&stat,
(uintptr_t)&setgraphicsoffset, /* 16 */
(uintptr_t)&wait,
(uintptr_t)&getgraphicswidth,
(uintptr_t)&getgraphicsheight,
(uintptr_t)&getgraphicsdepth, /* 20 */
(uintptr_t)&mkpipe,
(uintptr_t)&dup2,
(uintptr_t)&getuid,
(uintptr_t)&setuid, /* 24 */
(uintptr_t)&kernel_name_XXX,
(uintptr_t)&reboot,
(uintptr_t)&readdir,
(uintptr_t)&chdir, /* 28 */
(uintptr_t)&getcwd,
(uintptr_t)&clone,
(uintptr_t)&sethostname,
(uintptr_t)&gethostname, /* 32 */
(uintptr_t)&mousedevice,
(uintptr_t)&sys_mkdir,
(uintptr_t)&shm_obtain,
(uintptr_t)&shm_release, /* 36 */
(uintptr_t)&send_signal,
(uintptr_t)&sys_signal,
(uintptr_t)&share_fd,
(uintptr_t)&get_fd, /* 40 */
(uintptr_t)&gettid,
(uintptr_t)&yield,
(uintptr_t)&system_function,
(uintptr_t)&open_serial, /* 44 */
(uintptr_t)&sleep,
(uintptr_t)&sleep_rel,
(uintptr_t)&ioctl,
(uintptr_t)&access, /* 48 */
(uintptr_t)&stat_file,
(uintptr_t)&sys_chmod,
(uintptr_t)&sys_umask,
(uintptr_t)&sys_unlink, /* 52 */
0
};
uint32_t num_syscalls;
void
syscalls_install() {
for (num_syscalls = 0; syscalls[num_syscalls] != 0; ++num_syscalls);
debug_print(NOTICE, "Initializing syscall table with %d functions", num_syscalls);
isrs_install_handler(0x7F, &syscall_handler);
}
void
syscall_handler(
struct regs * r
) {
if (r->eax >= num_syscalls) {
return;
}
uintptr_t location = syscalls[r->eax];
if (location == 1) {
return;
}
/* Update the syscall registers for this process */
current_process->syscall_registers = r;
uint32_t ret;
asm volatile (
"push %1\n"
"push %2\n"
"push %3\n"
"push %4\n"
"push %5\n"
"call *%6\n"
"pop %%ebx\n"
"pop %%ebx\n"
"pop %%ebx\n"
"pop %%ebx\n"
"pop %%ebx\n"
: "=a" (ret) : "r" (r->edi), "r" (r->esi), "r" (r->edx), "r" (r->ecx), "r" (r->ebx), "r" (location));
/* The syscall handler may have moved the register pointer
* (ie, by creating a new stack)
* Update the pointer */
r = current_process->syscall_registers;
r->eax = ret;
}