summaryrefslogtreecommitdiff
path: root/src/kernel/arch
diff options
context:
space:
mode:
authordzwdz2021-07-19 18:17:50 +0200
committerdzwdz2021-07-19 18:17:50 +0200
commitf2a0c7595bf805cd53001984495e4db0268f5f98 (patch)
tree95900e23e386beeea8dfd17a102f7360b66adf73 /src/kernel/arch
parentc9ad4bfbf875b0d23e1f7bf7d5aff2d149ba4fd7 (diff)
very basic paging
Diffstat (limited to 'src/kernel/arch')
-rw-r--r--src/kernel/arch/generic.h7
-rw-r--r--src/kernel/arch/i386/pagedir.c98
2 files changed, 105 insertions, 0 deletions
diff --git a/src/kernel/arch/generic.h b/src/kernel/arch/generic.h
index 46782f8..22efe58 100644
--- a/src/kernel/arch/generic.h
+++ b/src/kernel/arch/generic.h
@@ -1,6 +1,7 @@
#pragma once
#include <kernel/arch/log.h>
+#include <stdbool.h>
// i have no idea where else to put it
// some code assumes that it's a power of 2
@@ -15,3 +16,9 @@ void halt_cpu();
// src/arch/i386/sysenter.s
void sysexit(void (*fun)(), void *stack_top);
void sysenter_setup();
+
+// all of those can allocate memory
+struct pagedir *pagedir_new();
+void pagedir_map(struct pagedir *dir, void *virt, void *phys,
+ bool user, bool writeable);
+void pagedir_use(struct pagedir *);
diff --git a/src/kernel/arch/i386/pagedir.c b/src/kernel/arch/i386/pagedir.c
new file mode 100644
index 0000000..264508b
--- /dev/null
+++ b/src/kernel/arch/i386/pagedir.c
@@ -0,0 +1,98 @@
+#include <kernel/arch/generic.h>
+#include <kernel/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;
+ 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() {
+ struct pagedir *dir = page_alloc(1);
+ for (int i = 0; i < 1024; i++)
+ dir->e[i].present = 0;
+ return dir;
+}
+
+void pagedir_map(struct pagedir *dir, void *virt, void *phys,
+ bool user, bool writeable)
+{
+ uintptr_t virt_casted = (uintptr_t) virt;
+ uint32_t pd_idx = virt_casted >> 22;
+ uint32_t pt_idx = virt_casted >> 12 & 0x03FF;
+ struct pagetable_entry *pagetable;
+
+ if (dir->e[pd_idx].present) {
+ pagetable = (void*) (dir->e[pd_idx].address << 11);
+ } else {
+ pagetable = page_alloc(1);
+ 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
+ };
+}
+
+void pagedir_use(struct pagedir *dir) {
+ asm volatile("mov %0, %%cr3;"
+
+ "mov %%cr0, %%eax;"
+ "or $0x80000001, %%eax;"
+ "mov %%eax, %%cr0;"
+ : : "r" (dir) : "eax");
+}