diff --git a/kernel-src/include/kernel/abi.h b/kernel-src/include/kernel/abi.h index 5a718e9a..2d9fad18 100644 --- a/kernel-src/include/kernel/abi.h +++ b/kernel-src/include/kernel/abi.h @@ -2,6 +2,7 @@ #define _ABI_H #include +#include typedef int pid_t; typedef int tid_t; @@ -115,20 +116,6 @@ typedef struct { char sun_path[108]; } __attribute__((packed)) unaddr_t; -typedef struct { - void *addr; - size_t len; -} iovec_t; - -static inline size_t iovec_size(iovec_t *iovec, size_t count) { - size_t size = 0; - - for (int i = 0; i < count; ++i) - size += iovec[i].len; - - return size; -} - typedef struct { void *addr; size_t addrlen; diff --git a/kernel-src/include/kernel/iovec.h b/kernel-src/include/kernel/iovec.h new file mode 100644 index 00000000..4963d022 --- /dev/null +++ b/kernel-src/include/kernel/iovec.h @@ -0,0 +1,67 @@ +#ifndef _IOVEC_H +#define _IOVEC_H + +#include +#include +#include + +typedef struct { + void *addr; + size_t len; +} iovec_t; + +typedef struct { + iovec_t *iovec; + size_t count; + + iovec_t *current; + uintmax_t current_offset; + + uintmax_t total_offset; + size_t total_size; +} iovec_iterator_t; + +// checks if all iovec[x].addr is an userspace address +bool iovec_user_check(iovec_t *iovec, size_t count); + +// returns the sum of the size of all iovecs +size_t iovec_size(iovec_t *iovec, size_t count); + +// initializes an iovec iterator +void iovec_iterator_init(iovec_iterator_t *iovec_iterator, iovec_t *iovec, size_t count); + +// skips skip_count bytes in the iovec iterator, returns the remaining number of bytes +size_t iovec_iterator_skip(iovec_iterator_t *iovec_iterator, size_t skip_count); + +// sets the current offset of the iovec iterator to offset, returns the remaining number of bytes +size_t iovec_iterator_set(iovec_iterator_t *iovec_iterator, size_t offset); + +// copies byte_count bytes from the iovec_iterator into buffer +// NOTE: the iovec can have userspace addresses. in this case, the copy can possibly fail with EFAULT +int iovec_iterator_copy_to_buffer(iovec_iterator_t *iovec_iterator, void *buffer, size_t byte_count); + +// copies byte_count bytes from buffer into the iovec_iterator +// NOTE: the iovec can have userspace addresses. in this case, the copy can possibly fail with EFAULT +int iovec_iterator_copy_from_buffer(iovec_iterator_t *iovec_iterator, void *buffer, size_t byte_count); + +// copies byte_count bytes from the iovec_iterator into the ringbuffer +// returns the number of bytes copied +// NOTE: the iovec can have userspace addresses. in this case, the copy can possibly fail by returning RINGBUFFER_USER_COPY_FAILED +size_t iovec_iterator_copy_to_ringbuffer(iovec_iterator_t *iovec_iterator, ringbuffer_t *ringbuffer, size_t byte_count); + +// copies byte_count bytes from the ringbuffer into the iovec_iterator +// returns the number of bytes copied +// NOTE: the iovec can have userspace addresses. in this case, the copy can possibly fail with RINGBUFFER_USER_COPY_FAILED +size_t iovec_iterator_copy_from_ringbuffer(iovec_iterator_t *iovec_iterator, ringbuffer_t *ringbuffer, size_t byte_count); + +// copies byte_count bytes from the ringbuffer into the iovec_iterator, without moving the ringbuffer read pointer +// returns the number of bytes copied +// NOTE: the iovec can have userspace addresses. in this case, the copy can possibly fail with RINGBUFFER_USER_COPY_FAILED +size_t iovec_iterator_peek_from_ringbuffer(iovec_iterator_t *iovec_iterator, ringbuffer_t *ringbuffer, size_t byte_count, uintmax_t ringbuffer_offset); + +// returns the physical address of the next page in the iovec_iterator, adding a reference count while at it +// fails with EINVAL if the iterator does not currently point to the start of a page or there is not enough space for a page +// fails with EFAULT if the page is not mapped +int iovec_iterator_next_page(iovec_iterator_t *iovec_iterator, void **page); + +#endif diff --git a/kernel-src/include/kernel/sock.h b/kernel-src/include/kernel/sock.h index 91603497..a10e8e2f 100644 --- a/kernel-src/include/kernel/sock.h +++ b/kernel-src/include/kernel/sock.h @@ -6,6 +6,7 @@ #include #include #include +#include #define SOCKET_STATE_UNBOUND 0 #define SOCKET_STATE_BOUND 1 diff --git a/kernel-src/io/block/block.c b/kernel-src/io/block/block.c index 5fc6958c..a22416ed 100644 --- a/kernel-src/io/block/block.c +++ b/kernel-src/io/block/block.c @@ -74,12 +74,14 @@ static void bytestolba(blockdesc_t *desc, uintmax_t offset, size_t size, uintmax #define MAP_FLAGS (ARCH_MMU_FLAGS_READ | ARCH_MMU_FLAGS_WRITE | ARCH_MMU_FLAGS_NOEXEC) +// this is only really called by the page cache VOP_GETPAGE and VOP_PUTPAGE functions, so we can make a few assumptions static int rwblock(int minor, void *buffer, size_t size, uintmax_t offset, int flags, bool write, size_t *done) { blockdesc_t *desc = getdesc(minor); if (desc == NULL) return ENODEV; __assert((size % desc->blocksize) == 0); + __assert((size % PAGE_SIZE) == 0); uintmax_t bytetop = desc->blockcapacity * desc->blocksize; // offset past end diff --git a/kernel-src/io/iovec.c b/kernel-src/io/iovec.c new file mode 100644 index 00000000..87b644f6 --- /dev/null +++ b/kernel-src/io/iovec.c @@ -0,0 +1,210 @@ +#include +#include +#include + +static inline bool iovec_iterator_finished(iovec_iterator_t *iovec_iterator) { + return iovec_iterator->total_size == iovec_iterator->total_offset; +} + +bool iovec_user_check(iovec_t *iovec, size_t count) { + for (int i = 0; i < count; ++i) { + if (IS_USER_ADDRESS(iovec[i].addr) == false) + return false; + } + + return true; +} + +size_t iovec_size(iovec_t *iovec, size_t count) { + size_t size = 0; + + for (int i = 0; i < count; ++i) + size += iovec[i].len; + + return size; +} + +void iovec_iterator_init(iovec_iterator_t *iovec_iterator, iovec_t *iovec, size_t count) { + iovec_iterator->iovec = iovec; + iovec_iterator->count = count; + + iovec_iterator->current = iovec; + iovec_iterator->current_offset = 0; + + iovec_iterator->total_offset = 0; + iovec_iterator->total_size = iovec_size(iovec, count); +} + +size_t iovec_iterator_skip(iovec_iterator_t *iovec_iterator, size_t skip_count) { + for (;;) { + size_t remaining_current = iovec_iterator->current->len - iovec_iterator->current_offset; + size_t skip_current = min(remaining_current, skip_count); + + skip_count -= skip_current; + + iovec_iterator->total_offset += skip_current; + iovec_iterator->current_offset += skip_current; + + // end of current iovec, go to next + if (iovec_iterator->current_offset == iovec_iterator->current->len && iovec_iterator_finished(iovec_iterator) == false) { + iovec_iterator->current++; + iovec_iterator->current_offset = 0; + } + + // end of skip or end of iovecs + if (skip_count == 0 || iovec_iterator_finished(iovec_iterator)) + break; + } + + return iovec_iterator->total_size - iovec_iterator->total_offset; +} + +size_t iovec_iterator_set(iovec_iterator_t *iovec_iterator, size_t offset) { + iovec_iterator->current = iovec_iterator->current; + iovec_iterator->current_offset = 0; + iovec_iterator->total_offset = 0; + + return iovec_iterator_skip(iovec_iterator, offset); +} + +int iovec_iterator_copy_to_buffer(iovec_iterator_t *iovec_iterator, void *buffer, size_t byte_count) { + size_t iterator_offset_save = iovec_iterator->total_offset; + size_t remaining_total = min(byte_count, iovec_iterator->total_size - iovec_iterator->total_offset); + int error = 0; + + for (;;) { + size_t remaining_current = iovec_iterator->current->len - iovec_iterator->current_offset; + size_t copy_current = min(remaining_current, remaining_total); + + error = USERCOPY_POSSIBLY_FROM_USER(buffer, (void *)((uintptr_t)iovec_iterator->current->addr + iovec_iterator->current_offset), copy_current); + if (error) + break; + + iovec_iterator_skip(iovec_iterator, copy_current); + remaining_total -= copy_current; + + if (iovec_iterator_finished(iovec_iterator) || remaining_total == 0) + break; + } + + if (error) + iovec_iterator_set(iovec_iterator, iterator_offset_save); + + return error; +} + +int iovec_iterator_copy_from_buffer(iovec_iterator_t *iovec_iterator, void *buffer, size_t byte_count) { + size_t iterator_offset_save = iovec_iterator->total_offset; + size_t remaining_total = min(byte_count, iovec_iterator->total_size - iovec_iterator->total_offset); + int error = 0; + + for (;;) { + size_t remaining_current = iovec_iterator->current->len - iovec_iterator->current_offset; + size_t copy_current = min(remaining_current, remaining_total); + + error = USERCOPY_POSSIBLY_FROM_USER((void *)((uintptr_t)iovec_iterator->current->addr + iovec_iterator->current_offset), buffer, copy_current); + if (error) + break; + + iovec_iterator_skip(iovec_iterator, copy_current); + remaining_total -= copy_current; + + if (iovec_iterator_finished(iovec_iterator) || remaining_total == 0) + break; + } + + if (error) + iovec_iterator_set(iovec_iterator, iterator_offset_save); + + return error; +} + +size_t iovec_iterator_copy_to_ringbuffer(iovec_iterator_t *iovec_iterator, ringbuffer_t *ringbuffer, size_t byte_count) { + size_t iterator_offset_save = iovec_iterator->total_offset; + size_t remaining_total = min(byte_count, iovec_iterator->total_size - iovec_iterator->total_offset); + size_t total_done = 0; + size_t current_done = 0; + + for (;;) { + size_t remaining_current = iovec_iterator->current->len - iovec_iterator->current_offset; + size_t copy_current = min(remaining_current, remaining_total); + + current_done = ringbuffer_write(ringbuffer, (void *)((uintptr_t)iovec_iterator->current->addr + iovec_iterator->current_offset), copy_current); + if (current_done == RINGBUFFER_USER_COPY_FAILED) { + // remove the data that was inserted + ringbuffer_remove(ringbuffer, total_done); + break; + } + + if (current_done == 0) + break; + + iovec_iterator_skip(iovec_iterator, copy_current); + total_done += current_done; + + if (total_done == byte_count || iovec_iterator_finished(iovec_iterator)) + break; + } + + if (current_done == RINGBUFFER_USER_COPY_FAILED) { + iovec_iterator_set(iovec_iterator, iterator_offset_save); + return RINGBUFFER_USER_COPY_FAILED; + } + + return total_done; +} + +size_t iovec_iterator_peek_from_ringbuffer(iovec_iterator_t *iovec_iterator, ringbuffer_t *ringbuffer, size_t byte_count, uintmax_t ringbuffer_offset) { + size_t iterator_offset_save = iovec_iterator->total_offset; + size_t remaining_total = min(byte_count, iovec_iterator->total_size - iovec_iterator->total_offset); + size_t total_done = 0; + size_t current_done = 0; + + for (;;) { + size_t remaining_current = iovec_iterator->current->len - iovec_iterator->current_offset; + size_t copy_current = min(remaining_current, remaining_total); + + current_done = ringbuffer_peek(ringbuffer, (void *)((uintptr_t)iovec_iterator->current->addr + iovec_iterator->current_offset), total_done, copy_current); + if (current_done == 0 || current_done == RINGBUFFER_USER_COPY_FAILED) + break; + + iovec_iterator_skip(iovec_iterator, copy_current); + total_done += current_done; + + if (total_done == byte_count || iovec_iterator_finished(iovec_iterator)) + break; + } + + if (current_done == RINGBUFFER_USER_COPY_FAILED) { + iovec_iterator_set(iovec_iterator, iterator_offset_save); + return RINGBUFFER_USER_COPY_FAILED; + } + + return total_done; +} + +size_t iovec_iterator_copy_from_ringbuffer(iovec_iterator_t *iovec_iterator, ringbuffer_t *ringbuffer, size_t byte_count) { + size_t done = iovec_iterator_peek_from_ringbuffer(iovec_iterator, ringbuffer, byte_count, 0); + if (done == RINGBUFFER_USER_COPY_FAILED) + return done; + + ringbuffer_truncate(ringbuffer, done); + return done; +} + +int iovec_iterator_next_page(iovec_iterator_t *iovec_iterator, void **page) { + void *addr = (void *)((uintptr_t)iovec_iterator->current->addr + iovec_iterator->current_offset); + if ((uintptr_t)addr % PAGE_SIZE) + return EINVAL; + + size_t remaining = iovec_iterator->current->len - iovec_iterator->current_offset; + if (remaining < PAGE_SIZE) + return EINVAL; + + void *phys = vmm_getphysical(addr, true); + if (phys == NULL) + return EFAULT; + + *page = phys; + return 0; +}