diff options
author | dzwdz | 2023-01-25 20:56:36 +0100 |
---|---|---|
committer | dzwdz | 2023-01-25 20:56:36 +0100 |
commit | c24a9376b4a048a3f9408df2d47eaad367459bd7 (patch) | |
tree | 74388b8b575719ab94e6f8eed9ad834abe7ba36b /src/kernel/pcpy.c | |
parent | 93194f8645ecd9f280372ec99497ede5f0ce7cee (diff) |
kernel: consolidate some header files
Diffstat (limited to 'src/kernel/pcpy.c')
-rw-r--r-- | src/kernel/pcpy.c | 159 |
1 files changed, 159 insertions, 0 deletions
diff --git a/src/kernel/pcpy.c b/src/kernel/pcpy.c new file mode 100644 index 0000000..9cf7c37 --- /dev/null +++ b/src/kernel/pcpy.c @@ -0,0 +1,159 @@ +/* Copies stuff between different pagedirs. Mostly a wrapper behind an old, + * bad interface. */ + +// TODO ensure the behaviour of kernel vs user fs on faults is the same + +#include <kernel/arch/generic.h> +#include <kernel/panic.h> +#include <kernel/proc.h> +#include <kernel/util.h> +#include <shared/mem.h> + +struct virt_iter { + void *frag; + size_t frag_len; + size_t prior; // sum of all prior frag_lens + bool error; + + void __user *_virt; + size_t _remaining; + Pagedir *_pages; + bool _user; + bool _writeable; +}; + +struct virt_cpy_error { // unused + bool read_fail, write_fail; +}; + +/* if pages == NULL, creates an iterator over physical memory. */ +static void virt_iter_new( + struct virt_iter *iter, void __user *virt, size_t length, + Pagedir *pages, bool user, bool writeable +); +static bool virt_iter_next(struct virt_iter *); +static size_t virt_cpy( + Pagedir *dest_pages, void __user *dest, + Pagedir *src_pages, const void __user *src, + size_t length, struct virt_cpy_error *err +); + + +static void +virt_iter_new( + struct virt_iter *iter, void __user *virt, size_t length, + Pagedir *pages, bool user, bool writeable +) { + iter->frag = NULL; + iter->frag_len = 0; + iter->prior = 0; + iter->error = false; + iter->_virt = virt; + iter->_remaining = length; + iter->_pages = pages; + iter->_user = user; + iter->_writeable = writeable; +} + +static bool +virt_iter_next(struct virt_iter *iter) +{ + /* note: While i'm pretty sure that this should always work, this + * was only tested in cases where the pages were consecutive both in + * virtual and physical memory, which might not always be the case. + * TODO test this */ + + size_t partial = iter->_remaining; + iter->prior += iter->frag_len; + if (partial <= 0) return false; + + if (iter->_pages) { // if iterating over virtual memory + // don't cross page boundaries + size_t to_page = PAGE_SIZE - ((uintptr_t)iter->_virt & PAGE_MASK); + partial = min(partial, to_page); + + iter->frag = pagedir_virt2phys(iter->_pages, + iter->_virt, iter->_user, iter->_writeable); + + if (!iter->frag) { + iter->error = true; + return false; + } + } else { + // "iterate" over physical memory + // the double cast supresses the warning about changing address spaces + iter->frag = (void* __force)iter->_virt; + } + + iter->frag_len = partial; + iter->_remaining -= partial; + iter->_virt += partial; + return true; +} + +static size_t +virt_cpy( + Pagedir *dest_pages, void __user *dest, + Pagedir *src_pages, const void __user *src, + size_t length, struct virt_cpy_error *err +) { + struct virt_iter dest_iter, src_iter; + size_t total = 0, partial; + + virt_iter_new(&dest_iter, dest, length, dest_pages, true, true); + virt_iter_new( &src_iter, (userptr_t)src, length, src_pages, true, false); + dest_iter.frag_len = 0; + src_iter.frag_len = 0; + + for (;;) { + if (dest_iter.frag_len <= 0 && !virt_iter_next(&dest_iter)) break; + if ( src_iter.frag_len <= 0 && !virt_iter_next( &src_iter)) break; + + partial = min(src_iter.frag_len, dest_iter.frag_len); + total += partial; + memcpy(dest_iter.frag, src_iter.frag, partial); + + dest_iter.frag_len -= partial; + dest_iter.frag += partial; + src_iter.frag_len -= partial; + src_iter.frag += partial; + } + + if (err) { + err->read_fail = src_iter.error; + err->write_fail = dest_iter.error; + } + if (src_iter.error || dest_iter.error) + assert(total != length); + else + assert(total == length); + return total; +} + +size_t +pcpy_to(Proc *p, __user void *dst, const void *src, size_t len) +{ + assert(p); + if (!p->pages) return 0; + return virt_cpy(p->pages, dst, NULL, (__user void*)src, len, NULL); +} + +size_t +pcpy_from(Proc *p, void *dst, const __user void *src, size_t len) +{ + assert(p); + if (!p->pages) return 0; + return virt_cpy(NULL, (__user void*)dst, p->pages, src, len, NULL); +} + +size_t +pcpy_bi( + Proc *dstp, __user void *dst, + Proc *srcp, const __user void *src, + size_t len +) { + assert(dstp && srcp); + if (!dstp->pages) return 0; + if (!srcp->pages) return 0; + return virt_cpy(dstp->pages, dst, srcp->pages, src, len, NULL); +} |