summaryrefslogtreecommitdiff
path: root/src/kernel/arch/amd64/pagedir.c
diff options
context:
space:
mode:
authordzwdz2022-07-16 13:33:00 +0200
committerdzwdz2022-07-16 13:33:00 +0200
commit912d2e3c7eb1baa71dda2c0a28aa5809eaa96f27 (patch)
tree4e27f3538466d5fd63a311d50916039a7a15a485 /src/kernel/arch/amd64/pagedir.c
parent1eeb66af44ab335888410d716d604e569f20866e (diff)
amd64: barely boot into kernel code
Diffstat (limited to 'src/kernel/arch/amd64/pagedir.c')
-rw-r--r--src/kernel/arch/amd64/pagedir.c213
1 files changed, 213 insertions, 0 deletions
diff --git a/src/kernel/arch/amd64/pagedir.c b/src/kernel/arch/amd64/pagedir.c
new file mode 100644
index 0000000..334cfc2
--- /dev/null
+++ b/src/kernel/arch/amd64/pagedir.c
@@ -0,0 +1,213 @@
+#include <kernel/arch/generic.h>
+#include <kernel/mem/alloc.h>
+#include <shared/mem.h>
+#include <stdint.h>
+
+/* <heat> nitpick: I highly recommend you dont use bitfields for paging
+ * structures
+ * <heat> you can't change them atomically and the way they're layed out
+ * in memory is implementation defined iirc
+ */
+struct pagetable_entry {
+ uint32_t present : 1;
+ uint32_t writeable : 1;
+ uint32_t user : 1;
+ uint32_t writethru : 1;
+ uint32_t uncached : 1;
+ uint32_t dirty : 1;
+ uint32_t always0 : 1; // memory type thing?
+ uint32_t global : 1;
+ uint32_t _unused : 3;
+ uint32_t address : 21;
+};
+
+struct pagedir_entry {
+ uint32_t present : 1;
+ uint32_t _writeable: 1; // don't use! not checked by multiple functions here
+ uint32_t _user : 1; // ^
+ uint32_t writethru : 1;
+ uint32_t uncached : 1;
+ uint32_t accessed : 1;
+ uint32_t always0 : 1;
+ uint32_t large : 1; // 4 MiB instead of 4 KiB
+ uint32_t _unused : 3;
+ uint32_t address : 21;
+} __attribute__((packed));
+
+struct pagedir {
+ struct pagedir_entry e[1024];
+} __attribute__((packed));
+
+
+struct pagedir *pagedir_new(void) {
+ struct pagedir *dir = page_alloc(1);
+ for (int i = 0; i < 1024; i++)
+ dir->e[i].present = 0;
+ return dir;
+}
+
+void pagedir_free(struct pagedir *dir) {
+ // assumes all user pages are unique and can be freed
+ struct pagetable_entry *pt;
+ void *page;
+
+ for (int i = 0; i < 1024; i++) {
+ if (!dir->e[i].present) continue;
+
+ pt = (void*)(dir->e[i].address << 11);
+
+ for (int j = 0; j < 1024; j++) {
+ if (!pt[j].present) continue;
+ if (!pt[j].user) continue;
+
+ page = (void*)(pt[j].address << 11);
+ page_free(page, 1);
+ }
+ page_free(pt, 1);
+ }
+ page_free(dir, 1);
+}
+
+static struct pagetable_entry*
+get_entry(struct pagedir *dir, const void __user *virt) {
+ uint32_t pd_idx = ((uintptr_t)virt) >> 22;
+ uint32_t pt_idx = ((uintptr_t)virt) >> 12 & 0x03FF;
+ struct pagetable_entry *pagetable;
+
+ if (!dir->e[pd_idx].present) return NULL;
+
+ pagetable = (void*)(dir->e[pd_idx].address << 11);
+ return &pagetable[pt_idx];
+}
+
+void *pagedir_unmap(struct pagedir *dir, void __user *virt) {
+ void *phys = pagedir_virt2phys(dir, virt, false, false);
+ struct pagetable_entry *page = get_entry(dir, virt);
+ page->present = false;
+ return phys;
+}
+
+void pagedir_map(struct pagedir *dir, void __user *virt, void *phys,
+ bool user, bool writeable)
+{
+ kprintf("in pagedir_map, dir 0x%x\n", dir);
+ uintptr_t virt_cast = (uintptr_t) virt;
+ uint32_t pd_idx = virt_cast >> 22;
+ uint32_t pt_idx = virt_cast >> 12 & 0x03FF;
+ struct pagetable_entry *pagetable;
+
+ kprintf("pre-if, accessing 0x%x because 0x%x\n", &dir->e[pd_idx], pd_idx);
+ if (dir->e[pd_idx].present) {
+ kprintf("present already\n");
+ pagetable = (void*) (dir->e[pd_idx].address << 11);
+ } else {
+ kprintf("allocing\n");
+ pagetable = page_alloc(1);
+ kprintf("alloc successful\n");
+ for (int i = 0; i < 1024; i++)
+ pagetable[i].present = 0;
+
+ dir->e[pd_idx] = (struct pagedir_entry) {
+ .present = 1,
+ ._writeable= 1,
+ ._user = 1,
+ .writethru = 1,
+ .uncached = 0,
+ .accessed = 0,
+ .always0 = 0,
+ .large = 0,
+ ._unused = 0,
+ .address = (uintptr_t) pagetable >> 11
+ };
+ }
+
+ pagetable[pt_idx] = (struct pagetable_entry) {
+ .present = 1,
+ .writeable = writeable,
+ .user = user,
+ .writethru = 1,
+ .uncached = 0,
+ .dirty = 0,
+ .always0 = 0,
+ .global = 0,
+ ._unused = 0,
+ .address = (uintptr_t) phys >> 11
+ };
+ kprintf("out pagedir_map\n");
+}
+
+extern void *pagedir_current;
+void pagedir_switch(struct pagedir *dir) {
+ pagedir_current = dir;
+}
+
+// creates a new pagedir with exact copies of the user pages
+struct pagedir *pagedir_copy(const struct pagedir *orig) {
+ struct pagedir *clone = page_alloc(1);
+ struct pagetable_entry *orig_pt, *clone_pt;
+ void *orig_page, *clone_page;
+
+ for (int i = 0; i < 1024; i++) {
+ clone->e[i] = orig->e[i];
+ if (!orig->e[i].present) continue;
+
+ orig_pt = (void*)(orig->e[i].address << 11);
+ clone_pt = page_alloc(1);
+ clone->e[i].address = (uintptr_t) clone_pt >> 11;
+
+ for (int j = 0; j < 1024; j++) {
+ clone_pt[j] = orig_pt[j];
+ if (!orig_pt[j].present) continue;
+ if (!orig_pt[j].user) continue;
+ // i could use .global?
+
+ orig_page = (void*)(orig_pt[j].address << 11);
+ clone_page = page_alloc(1);
+ clone_pt[j].address = (uintptr_t) clone_page >> 11;
+
+ memcpy(clone_page, orig_page, PAGE_SIZE);
+ }
+ }
+
+ return clone;
+}
+
+bool pagedir_iskern(struct pagedir *dir, const void __user *virt) {
+ struct pagetable_entry *page = get_entry(dir, virt);
+ return page && page->present && !page->user;
+}
+
+void *pagedir_virt2phys(struct pagedir *dir, const void __user *virt,
+ bool user, bool writeable)
+{
+ struct pagetable_entry *page;
+ uintptr_t phys;
+ page = get_entry(dir, virt);
+ if (!page || !page->present) return NULL;
+ if (user && !page->user) return NULL;
+ if (writeable && !page->writeable) return NULL;
+
+ phys = page->address << 11;
+ phys |= ((uintptr_t)virt) & 0xFFF;
+ return (void*)phys;
+}
+
+void __user *pagedir_findfree(struct pagedir *dir, char __user *start, size_t len) {
+ struct pagetable_entry *page;
+ char __user *iter;
+ start = (userptr_t)(((uintptr_t __force)start + PAGE_MASK) & ~PAGE_MASK); // round up to next page
+ iter = start;
+
+ while (iter < (char __user *)0xFFF00000) { // TODO better boundary
+ page = get_entry(dir, iter);
+ if (page && page->present) {
+ start = iter + PAGE_SIZE;
+ } else {
+ if ((size_t)(iter + PAGE_SIZE - start) >= len)
+ return start;
+ }
+ iter += PAGE_SIZE;
+ }
+
+ return NULL;
+}