#include "api/memory.h" #include "api/syscall.h" #include "hal/memory.h" #include "jove.h" #include "lib/printf.h" #include "syscall.h" static int s_syscall_pagedir_get_untyped( lockstack_t *lockstack, jove_obj_t *objdir, jove_owned_obj_t *pml4, size_t payload_bytes, void *payload_data, jove_perm_obj_t *untyped_ret) { size_t bytes_needed = sizeof(struct syscall_pagedir_map); CHECK_TOOSMALL(bytes_needed, payload_bytes, "Message too small for payload."); struct syscall_pagedir_map *payload = payload_data; if(payload->virtual_address > 0xFFFF800000000000ULL) { kprintf("%s: Virtual address is above user limit.\n", __func__); return -EBADARG; } bytes_needed += payload->untyped_pathlen << 1; CHECK_TOOSMALL(bytes_needed, payload_bytes, "Message too small for untyped path."); errno = 0; jove_perm_obj_t untyped = link_traverse_objdir(objdir, payload->untyped_pathlen, payload->untyped_path); lockstack_push(lockstack, untyped.obj); if(untyped.obj == 0) { klogf("Unable to get untyped object %P : %s\n", payload->untyped_path, payload->untyped_pathlen, errstr(errno)); return -errno; } CHECK_OBJTYPE(untyped.obj, JOVE_OBJ_UNTYPED, "Untyped path does not point to an untyped object."); CHECK_OBJPERM(untyped, JOVE_PERM_MUTATE, "Untyped object does not have MUTATE permission."); jove_untyped_t *untyped_data = obj_deref(untyped.obj); if((uintptr_t)untyped_data & 0xFFF) { kprintf("%s: Untyped object is not page aligned.\n", __func__); return -EALIGN; } if(untyped_data->limit != 0x1000) { klogf("Untyped object is not page sized. (%x vs 1000)\n", untyped_data->limit); return -EBADSIZE; } *untyped_ret = untyped; return 0; } static int s_syscall_pagedir_get_mapping( jove_owned_obj_t *pagedir, size_t bytes_left, void *payload_data) { size_t bytes_needed = sizeof(struct pagedir_mapping); CHECK_TOOSMALL(bytes_needed, bytes_left, "Message too small for payload"); virtaddr_t vptr = *((virtaddr_t*)payload_data); if(vptr > JOVE_USERMEMORY_LIMIT) { klogf("Virtual address %p is out of bounds!\n", vptr); return -EOOB; } virtaddr_t *mapping = pagedir_get_mapping(pagedir, vptr); if(mapping == NULL) return -errno; struct pagedir_mapping *payload = payload_data; payload->pfn = pagedir_mapping_get_pfn(mapping); payload->flags.present = pagedir_mapping_is_present(mapping); payload->flags.writeable = pagedir_mapping_is_writeable(mapping); payload->flags.executable = pagedir_mapping_is_executable(mapping); return 0; } static int s_syscall_pagedir_transfer( lockstack_t *lockstack, jove_obj_t *rootdir, jove_owned_obj_t *pagedir, size_t bytes_left, void *payload_data) { //Verify the message is big enough for the payload. size_t bytes_needed = sizeof(struct syscall_pagedir_transfer); CHECK_TOOSMALL(bytes_needed, bytes_left, "Message too small for payload"); //Verify the payload is big enough for the target path. struct syscall_pagedir_transfer *payload = payload_data; bytes_needed += sizeof(objpath_part_t) * payload->target_path_length; CHECK_TOOSMALL(bytes_needed, bytes_left, "Message too small for target path"); //Verify the target object is a pagedir with the correct permissions. jove_perm_obj_t target = link_traverse_objdir(rootdir, payload->target_path_length, payload->target_path); CHECK_OBJEXISTS(target.obj, "Target path does not point to a valid object"); lockstack_push(lockstack, target.obj); CHECK_OBJTYPE(target.obj, JOVE_OBJ_PAGEDIR, "Target object is not a JOVE_OBJ_PAGEDIR"); CHECK_OBJPERM(target, SYSCALL_PERM_PAGEDIR_TRANSFER_TGT, "Not allowed to transfer to target"); //Verify subject has a mapping at the given address. virtaddr_t *subject_mapping = pagedir_get_mapping(pagedir, payload->subject_address); if(subject_mapping == NULL) { klogf("Subject page directory does not have tables for %p\n", payload->subject_address); return -errno; } //Verify target has a mapping at the given address. virtaddr_t *target_mapping = pagedir_get_mapping(target.obj, payload->target_address); if(target_mapping == NULL) { klogf("Target page directory does not have tables for %p\n", payload->target_address); return -errno; } //Verify target mapping is not marked present. if(pagedir_mapping_is_present(target_mapping)) { klogf("Target page at %p is marked as present\n", payload->target_address); return -EBUSY; } //Move the mapping over and return. *target_mapping = *subject_mapping; *subject_mapping = 0; pagedir_invlpg(payload->subject_address); return 0; } static int s_syscall_pagedir_unmap( lockstack_t *lockstack, jove_obj_t *objdir, jove_owned_obj_t *pagedir, size_t payload_bytes, void *payload_data, jove_perm_obj_t *none_ret) { size_t bytes_needed = sizeof(struct syscall_pagedir_unmap); CHECK_TOOSMALL(bytes_needed, payload_bytes, "Message too small for payload"); struct syscall_pagedir_unmap *payload = payload_data; if(payload->virtual_address > 0xFFFF800000000000ULL) { kprintf("%s: Virtual address is above user limit.\n", __func__); return EBADARG; } bytes_needed += payload->none_pathlen * 2; CHECK_TOOSMALL(bytes_needed, payload_bytes, "Message too small for none path"); jove_perm_obj_t none = link_traverse_objdir( objdir, payload->none_pathlen, payload->none_path); lockstack_push(lockstack, none.obj); if(errno) { kprintf("%s: None obj retrieval failed for the reason: %s\n", __func__, errstr(errno)); return errno; } CHECK_OBJTYPE(none.obj, JOVE_OBJ_NONE, "None path does not point to none object."); *none_ret = none; return 0; } static int s_syscall_pagedir_map_frame( lockstack_t *lockstack, jove_obj_t *objdir, jove_owned_obj_t *pagedir, size_t payload_bytes, void *payload_data) { jove_perm_obj_t untyped = { .obj = NULL, .permissions = 0 }; int sysret = s_syscall_pagedir_get_untyped(lockstack, objdir, pagedir, payload_bytes, payload_data, &untyped); if(sysret != 0) { return sysret; } struct syscall_pagedir_map *payload = payload_data; sysret = pagedir_map_untyped_frame(pagedir, &untyped, payload->virtual_address, payload->flags); if(sysret == 0) obj_settype(untyped.obj, JOVE_OBJ_NONE); return sysret; } static int s_syscall_pagedir_map_table( lockstack_t *lockstack, jove_obj_t *objdir, jove_owned_obj_t *pagedir, size_t payload_bytes, void *payload_data) { jove_perm_obj_t untyped = { .obj = NULL, .permissions = 0 }; int sysret = s_syscall_pagedir_get_untyped(lockstack, objdir, pagedir, payload_bytes, payload_data, &untyped); if(sysret != 0) { return sysret; } struct syscall_pagedir_map *payload = payload_data; sysret = pagedir_map_untyped_table(pagedir, &untyped, payload->virtual_address); if(sysret == 0) obj_settype(untyped.obj, JOVE_OBJ_NONE); return sysret; } static int s_syscall_pagedir_map_shmem( lockstack_t *lockstack, jove_obj_t *objdir, jove_owned_obj_t *pagedir, size_t payload_bytes, void *payload_data) { size_t bytes_needed = sizeof(struct syscall_pagedir_map_shmem); CHECK_TOOSMALL(bytes_needed, payload_bytes, "Message too small for payload."); struct syscall_pagedir_map_shmem *payload = payload_data; if(payload->virtual_address > 0xFFFF800000000000ULL) { kprintf("%s: Virtual address is above user limit.\n", __func__); return EBADARG; } bytes_needed += payload->shmem_pathlen << 1; CHECK_TOOSMALL(bytes_needed, payload_bytes, "Message too small for shared memory path."); jove_perm_obj_t shmem = link_traverse_objdir(objdir, payload->shmem_pathlen, payload->shmem_path); lockstack_push(lockstack, shmem.obj); if(shmem.obj == NULL) { klogf("Unable to retrieve shared memory object %P : %s\n", payload->shmem_path, payload->shmem_pathlen, errstr(errno)); return errno; } CHECK_OBJTYPE(shmem.obj, JOVE_OBJ_SHMEMORY, "Shared memory path does not point to a shared memory object."); uintmax_t shmem_value = obj_getvalue(shmem.obj); uint8_t shmem_pages = shmem_value & 0xFFF; if(payload->page_offset >= shmem_pages) { klogf("Page offset goes beyond size of shared memory object: (%i >= %i)\n", payload->page_offset, shmem_pages); return -EOOB; } return pagedir_map_shmem_frame(pagedir, &shmem, payload->page_offset, payload->virtual_address, payload->flags); } static int s_syscall_pagedir_unmap_frame( lockstack_t *lockstack, jove_obj_t *objdir, jove_owned_obj_t *pagedir, size_t payload_bytes, void *payload_data) { jove_perm_obj_t none; int sysret = s_syscall_pagedir_unmap(lockstack, objdir, pagedir, payload_bytes, payload_data, &none); if(!sysret) return sysret; struct syscall_pagedir_unmap *payload = payload_data; return pagedir_unmap_untyped_frame(pagedir, none.obj, payload->virtual_address); } static int s_syscall_pagedir_unmap_table( lockstack_t *lockstack, jove_obj_t *objdir, jove_owned_obj_t *pagedir, size_t payload_bytes, void *payload_data) { jove_perm_obj_t none; int sysret = s_syscall_pagedir_unmap(lockstack, objdir, pagedir, payload_bytes, payload_data, &none); if(!sysret) return sysret; struct syscall_pagedir_unmap *payload = payload_data; return pagedir_unmap_untyped_table(pagedir, none.obj, payload->virtual_address); } int _syscall_handle_pagedir(syscall_context_t *ctx) { lockstack_t *lockstack = &ctx->lockstack; struct syscall_header *header = ctx->header; jove_obj_t *objdir = ctx->objdir; jove_perm_obj_t *target = &ctx->target; size_t bytes_left = ctx->bytes_left; void *payload = ctx->payload; switch(header->callid) { case SYSCALL_PAGEDIR_GET_MAPPING: if(target->permissions & SYSCALL_PERM_PAGEDIR_GET_MAPPING) return s_syscall_pagedir_get_mapping(target->obj, bytes_left, payload); return -EPERM; case SYSCALL_PAGEDIR_TRANSFER: if(target->permissions & SYSCALL_PERM_PAGEDIR_TRANSFER) return s_syscall_pagedir_transfer( lockstack, objdir, target->obj, bytes_left, payload); case SYSCALL_PAGEDIR_MAP_FRAME: if(target->permissions & SYSCALL_PERM_PAGEDIR_MAP_FRAME) return s_syscall_pagedir_map_frame(lockstack, objdir, target->obj, bytes_left, payload); return -EPERM; case SYSCALL_PAGEDIR_MAP_TABLE: if(target->permissions & SYSCALL_PERM_PAGEDIR_MAP_TABLE) return s_syscall_pagedir_map_table(lockstack, objdir, target->obj, bytes_left, payload); return -EPERM; case SYSCALL_PAGEDIR_MAP_SHMEM: if(target->permissions & SYSCALL_PERM_PAGEDIR_MAP_SHMEM) return s_syscall_pagedir_map_shmem(lockstack, objdir, target->obj, bytes_left, payload); return -EPERM; case SYSCALL_PAGEDIR_UNMAP_FRAME: if(target->permissions & SYSCALL_PERM_PAGEDIR_UNMAP_FRAME) return s_syscall_pagedir_unmap_frame(lockstack, objdir, target->obj, bytes_left, payload); return -EPERM; case SYSCALL_PAGEDIR_UNMAP_TABLE: if(target->permissions & SYSCALL_PERM_PAGEDIR_UNMAP_TABLE) return s_syscall_pagedir_unmap_table(lockstack, objdir, target->obj, bytes_left, payload); return -EPERM; default: kprintf("syscall_handle_pagedir: Pagedir does not support syscall %i\n", header->callid); return EBADARG; } }