/* SPDX-License-Identifier: copyleft-next-0.3.1 */ /* Copyright 2021 - 2022, Kim Kuparinen < kimi.h.kuparinen@gmail.com > */ /** * @file mem.c * Memory handling syscall implementations. * \todo Should we return more error information? */ #include #include #include #include /** * Memory request syscall handler. * * @param t Current tcb. * @param size Minimum size of allocation. * @param flags Flags of allocation. * @return \ref OK and start of allocation when succesful, * \ref ERR_OOMEM and \c NULL otherwise. */ SYSCALL_DEFINE2(req_mem)(struct tcb *t, sys_arg_t size, sys_arg_t flags) { struct tcb *r = get_cproc(t); flags = sanitize_uvflags(flags); vm_t start = alloc_uvmem(r, size, flags); if (ERR_CODE(start)) return_args1(t, start); flush_tlb_all(); return_args2(t, OK, start); } /** * Fixed memory request syscall handler. * * @param t Current tcb. * @param fixed Address which should be included in allocation. * @param size Minimum size of allocation after \c start. * @param flags Flags of allocation. * @return \ref OK and start of allocation when succesful, * \ref ERR_OOMEM and \c NULL otherwise. */ SYSCALL_DEFINE3(req_fixmem)(struct tcb *t, sys_arg_t fixed, sys_arg_t size, sys_arg_t flags) { struct tcb *r = get_cproc(t); flags = sanitize_uvflags(flags); vm_t start = alloc_fixed_uvmem(r, fixed, size, flags); if (ERR_CODE(start)) return_args1(t, start); flush_tlb_all(); return_args2(t, OK, start); } /** * Free memory syscall handler. * * @param t Current tcb. * @param start Start of allocation to free. * @return \ref OK and \c 0 when succesful, \ref ERR_NF and \c 0 otherwise. */ SYSCALL_DEFINE1(free_mem)(struct tcb *t, sys_arg_t start) { struct tcb *r = get_cproc(t); stat_t status = OK; /* try freeing normal user memory first, if that fails, try device * memory, otherwise just assume the address is borked. */ if (!(status = free_uvmem(r, start))) return_args1(t, OK); if (!(status = free_devmem(r, start))) return_args1(t, OK); flush_tlb_all(); return_args1(t, status); } /** * Request physical memory syscall handler. * * @param t Current tcb. * @param paddr Physical address to map. * @param size Minimum size of allocation. * @param flags Flags of allocation. * @return \ref OK and start of allocation when succesful, * otherwise an error code. */ SYSCALL_DEFINE3(req_pmem)(struct tcb *t, sys_arg_t paddr, sys_arg_t size, sys_arg_t flags) { /* this will require some pondering, but essentially this syscall should * only be used for device access, so any addresses requested should be * outside the RAM area, and I'll probably have to implement some method * that keeps track of used regions outside of RAM. We'll see. */ struct tcb *r = get_cproc(t); if (!has_cap(r->caps, CAP_PHYSICAL)) return_args1(t, ERR_PERM); flags = sanitize_uvflags(flags); vm_t start = alloc_devmem(r, paddr, size, flags); if (ERR_CODE(start)) return_args1(t, start); flush_tlb_all(); return_args2(t, OK, start); } /** * Allocate some contiguous region in memory. * Due to page allocation constraints, this is generally just one page that's * larger than the \p size parameter. Should work fine for 4K - 2M allocations, * which I assume is generally what is used, but we'll see if I have to * implement an actually good page allocator at some point. * * @param t Current tcb. * @param size Minimum size of mapping. * @param flags Flags of mapping. Unsure why you'd want anything besides VM_W | * VM_R, but eh. * * @return \see sys_req_page(). */ SYSCALL_DEFINE2(req_page)(struct tcb *t, sys_arg_t size, sys_arg_t flags) { struct tcb *r = get_cproc(t); pm_t addr = 0; vm_t start = 0; size_t asize = 0; flags = sanitize_uvflags(flags); start = alloc_uvpage(r, size, flags, &addr, &asize); if (ERR_CODE(start)) return_args1(t, start); flush_tlb_all(); return_args4(t, OK, start, addr, asize); } /** * Request shared memory syscall handler. * * @param t Current tcb. * @param size Minimum size of allocation. * @param flags Flags of allocation. * @return \ref OK, start * in that order, otherwise an error code. */ SYSCALL_DEFINE2(req_sharedmem)(struct tcb *t, sys_arg_t size, sys_arg_t flags) { struct tcb *c = get_cproc(t); if (!has_cap(c->caps, CAP_SHARED)) return_args1(t, ERR_PERM); flags = sanitize_uvflags(flags); vm_t start = alloc_shared_uvmem(c, size, flags); if (ERR_CODE(start)) return_args1(t, start); flush_tlb_all(); return_args2(t, OK, start); } /** * Reference shared memory. * * @param t Current tcb. * @param tid In which thread's address space to create mapping. * @param addr Address of shared region in \p t. * @param flags Flags of allocation. * @return \ref ERR_OOMEM if unsucessful, otherwise \ref OK, virtual address, * actual size, in that order. Generally the actual size should match with the * original shared region, but I wouldn't count on it. */ SYSCALL_DEFINE3(ref_sharedmem)(struct tcb *t, sys_arg_t tid, sys_arg_t addr, sys_arg_t flags) { struct tcb *c = get_cproc(t); if (!has_cap(c->caps, CAP_SHARED)) return_args1(t, ERR_PERM); struct tcb *r = get_tcb(tid); if (!r || zombie(r)) return_args1(t, ERR_INVAL); flags = sanitize_uvflags(flags); vm_t start = ref_shared_uvmem(r, c, addr, flags); if (ERR_CODE(start)) return_args1(t, start); flush_tlb_all(); return_args2(t, OK, start); }