summaryrefslogtreecommitdiff
path: root/src/kernel/mem
diff options
context:
space:
mode:
authordzwdz2021-09-05 18:32:08 +0200
committerdzwdz2021-09-05 18:32:08 +0200
commit838b142b7374150df63ec02b02377c114a14b314 (patch)
treeb5b22e5517da7a4ac96a47f0cfe6a4755d00da44 /src/kernel/mem
parentd6c1dceba2511fa2edf839942b237d3a27bb9535 (diff)
move most of the memory stuff to kernel/mem/
Diffstat (limited to 'src/kernel/mem')
-rw-r--r--src/kernel/mem/alloc.c38
-rw-r--r--src/kernel/mem/alloc.h15
-rw-r--r--src/kernel/mem/virt.c78
-rw-r--r--src/kernel/mem/virt.h28
4 files changed, 159 insertions, 0 deletions
diff --git a/src/kernel/mem/alloc.c b/src/kernel/mem/alloc.c
new file mode 100644
index 0000000..42a2f3b
--- /dev/null
+++ b/src/kernel/mem/alloc.c
@@ -0,0 +1,38 @@
+#include <kernel/arch/generic.h>
+#include <kernel/mem/alloc.h>
+#include <kernel/util.h>
+#include <stdint.h>
+
+static void *highest_page;
+
+void mem_init(struct kmain_info *info) {
+ // finds the highest used page, and starts allocating pages above it
+ void *highest = &_bss_end;
+
+ if (highest < info->init.at + info->init.size)
+ highest = info->init.at + info->init.size;
+
+ // align up to PAGE_SIZE
+ highest_page = (void*)(((uintptr_t)highest + PAGE_MASK) & ~PAGE_MASK);
+}
+
+void *page_alloc(size_t pages) {
+ void *bottom = highest_page;
+ highest_page += pages * PAGE_SIZE;
+ return bottom;
+}
+
+// frees `pages` consecutive pages starting from *first
+void page_free(void *first, size_t pages) {
+ // not implemented
+}
+
+
+void *kmalloc(size_t len) {
+ // extremely inefficient, but this is only temporary anyways
+ return page_alloc(len / PAGE_SIZE + 1);
+}
+
+void kfree(void *ptr) {
+ // unimplemented
+}
diff --git a/src/kernel/mem/alloc.h b/src/kernel/mem/alloc.h
new file mode 100644
index 0000000..571b289
--- /dev/null
+++ b/src/kernel/mem/alloc.h
@@ -0,0 +1,15 @@
+#pragma once
+#include <kernel/arch/generic.h>
+#include <kernel/main.h>
+#include <stddef.h>
+
+void mem_init(struct kmain_info *);
+
+// allocates `pages` consecutive pages
+void *page_alloc(size_t pages);
+
+// frees `pages` consecutive pages starting from *first
+void page_free(void *first, size_t pages);
+
+void *kmalloc(size_t len);
+void kfree(void *ptr);
diff --git a/src/kernel/mem/virt.c b/src/kernel/mem/virt.c
new file mode 100644
index 0000000..2990668
--- /dev/null
+++ b/src/kernel/mem/virt.c
@@ -0,0 +1,78 @@
+#include <kernel/arch/generic.h>
+#include <kernel/mem/virt.h>
+#include <kernel/util.h>
+
+void virt_iter_new(
+ struct virt_iter *iter, user_ptr virt, size_t length,
+ struct pagedir *pages, bool user, bool writeable)
+{
+ iter->frag = 0;
+ 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;
+}
+
+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 */
+
+ user_ptr virt = iter->_virt;
+ size_t partial = iter->_remaining;
+ iter->prior += iter->frag_len;
+ if (partial <= 0) return false;
+
+ // don't read past the page
+ if ((virt & PAGE_MASK) + partial > PAGE_SIZE)
+ partial = PAGE_SIZE - (virt & PAGE_MASK);
+
+ iter->frag = pagedir_virt2phys(iter->_pages,
+ iter->_virt, iter->_user, iter->_writeable);
+
+ if (iter->frag == 0) {
+ iter->error = true;
+ return false;
+ }
+
+ iter->frag_len = partial;
+ iter->_remaining -= partial;
+ iter->_virt += partial;
+ return true;
+}
+
+bool virt_user_cpy(
+ struct pagedir *dest_pages, user_ptr dest,
+ struct pagedir *src_pages, const user_ptr src, size_t length)
+{
+ struct virt_iter dest_iter, src_iter;
+ size_t min;
+
+ virt_iter_new(&dest_iter, dest, length, dest_pages, true, true);
+ virt_iter_new( &src_iter, src, length, src_pages, true, false);
+ dest_iter.frag_len = 0;
+ src_iter.frag_len = 0;
+
+ for (;;) {
+ if (dest_iter.frag_len <= 0)
+ if (!virt_iter_next(&dest_iter)) break;
+ if ( src_iter.frag_len <= 0)
+ if (!virt_iter_next( &src_iter)) break;
+
+ min = src_iter.frag_len < dest_iter.frag_len
+ ? src_iter.frag_len : dest_iter.frag_len;
+ memcpy(dest_iter.frag, src_iter.frag, min);
+
+ dest_iter.frag_len -= min;
+ dest_iter.frag += min;
+ src_iter.frag_len -= min;
+ src_iter.frag += min;
+ }
+
+ return !(dest_iter.error || src_iter.error);
+}
diff --git a/src/kernel/mem/virt.h b/src/kernel/mem/virt.h
new file mode 100644
index 0000000..98a7a16
--- /dev/null
+++ b/src/kernel/mem/virt.h
@@ -0,0 +1,28 @@
+/* contains utilities for interacting with virtual memory */
+#pragma once
+#include <kernel/types.h>
+#include <stdbool.h>
+#include <stddef.h>
+
+struct virt_iter {
+ void *frag;
+ size_t frag_len;
+ size_t prior; // sum of all prior frag_lens
+ bool error;
+
+ user_ptr _virt;
+ size_t _remaining;
+ struct pagedir *_pages;
+ bool _user;
+ bool _writeable;
+};
+
+void virt_iter_new(
+ struct virt_iter *iter, user_ptr virt, size_t length,
+ struct pagedir *pages, bool user, bool writeable);
+
+bool virt_iter_next(struct virt_iter *);
+
+bool virt_user_cpy(
+ struct pagedir *dest_pages, user_ptr dest,
+ struct pagedir *src_pages, const user_ptr src, size_t length);