From b58c6e5f49929aa6d6e7a72768b08ab180382896 Mon Sep 17 00:00:00 2001
From: sbosse <sbosse@uni-bremen.de>
Date: Mon, 14 Oct 2024 23:07:13 +0200
Subject: [PATCH] Mon 14 Oct 23:06:38 CEST 2024

---
 kernel/pagetable.c | 296 +++++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 296 insertions(+)
 create mode 100644 kernel/pagetable.c

diff --git a/kernel/pagetable.c b/kernel/pagetable.c
new file mode 100644
index 0000000..6d85339
--- /dev/null
+++ b/kernel/pagetable.c
@@ -0,0 +1,296 @@
+/*
+Copyright (C) 2015-2019 The University of Notre Dame
+This software is distributed under the GNU General Public License.
+See the file LICENSE for details.
+*/
+
+#include "pagetable.h"
+#include "page.h"
+#include "string.h"
+#include "kernelcore.h"
+
+#define ENTRIES_PER_TABLE (PAGE_SIZE/4)
+
+struct pageentry {
+	unsigned present:1;	// 1 = present
+	unsigned readwrite:1;	// 1 = writable
+	unsigned user:1;	// 1 = user mode
+	unsigned writethrough:1;	// 1 = write through
+
+	unsigned nocache:1;	// 1 = no caching
+	unsigned accessed:1;	// 1 = accessed
+	unsigned dirty:1;	// 1 = dirty
+	unsigned pagesize:1;	// leave to zero
+
+	unsigned globalpage:1;	// 1 if not to be flushed
+	unsigned avail:3;
+
+	unsigned addr:20;
+};
+
+struct pagetable {
+	struct pageentry entry[ENTRIES_PER_TABLE];
+};
+
+struct pagetable *pagetable_create()
+{
+	return page_alloc(1);
+}
+
+void pagetable_init(struct pagetable *p)
+{
+	unsigned i, stop;
+	stop = total_memory * 1024 * 1024;
+	for(i = 0; i < stop; i += PAGE_SIZE) {
+		pagetable_map(p, i, i, PAGE_FLAG_KERNEL | PAGE_FLAG_READWRITE);
+	}
+	stop = (unsigned) video_buffer + video_xres * video_yres * 3;
+	for(i = (unsigned) video_buffer; i <= stop; i += PAGE_SIZE) {
+		pagetable_map(p, i, i, PAGE_FLAG_KERNEL | PAGE_FLAG_READWRITE);
+	}
+}
+
+int pagetable_getmap(struct pagetable *p, unsigned vaddr, unsigned *paddr, int *flags)
+{
+	struct pagetable *q;
+	struct pageentry *e;
+
+	unsigned a = vaddr >> 22;
+	unsigned b = (vaddr >> 12) & 0x3ff;
+
+	e = &p->entry[a];
+	if(!e->present)
+		return 0;
+
+	q = (struct pagetable *) (e->addr << 12);
+
+	e = &q->entry[b];
+	if(!e->present)
+		return 0;
+
+	*paddr = e->addr << 12;
+
+	if(flags) {
+		*flags = 0;
+		if(e->readwrite)
+			*flags |= PAGE_FLAG_READWRITE;
+		if(e->avail & 0x01)
+			*flags |= PAGE_FLAG_ALLOC;
+		if(!e->user)
+			*flags |= PAGE_FLAG_KERNEL;
+	}
+
+	return 1;
+}
+
+int pagetable_map(struct pagetable *p, unsigned vaddr, unsigned paddr, int flags)
+{
+	struct pagetable *q;
+	struct pageentry *e;
+
+	unsigned a = vaddr >> 22;
+	unsigned b = (vaddr >> 12) & 0x3ff;
+
+	if(flags & PAGE_FLAG_ALLOC) {
+		paddr = (unsigned) page_alloc(flags & PAGE_FLAG_CLEAR);
+		if(!paddr)
+			return 0;
+	}
+
+	e = &p->entry[a];
+
+	if(!e->present) {
+		q = pagetable_create();
+		if(!q)
+			return 0;
+		e->present = 1;
+		e->readwrite = 1;
+		e->user = (flags & PAGE_FLAG_KERNEL) ? 0 : 1;
+		e->writethrough = 0;
+		e->nocache = 0;
+		e->accessed = 0;
+		e->dirty = 0;
+		e->pagesize = 0;
+		e->globalpage = (flags & PAGE_FLAG_KERNEL) ? 1 : 0;
+		e->avail = 0;
+		e->addr = (((unsigned) q) >> 12);
+	} else {
+		q = (struct pagetable *) (((unsigned) e->addr) << 12);
+	}
+
+
+	e = &q->entry[b];
+
+	e->present = 1;
+	e->readwrite = (flags & PAGE_FLAG_READWRITE) ? 1 : 0;
+	e->user = (flags & PAGE_FLAG_KERNEL) ? 0 : 1;
+	e->writethrough = 0;
+	e->nocache = 0;
+	e->accessed = 0;
+	e->dirty = 0;
+	e->pagesize = 0;
+	e->globalpage = !e->user;
+	e->avail = (flags & PAGE_FLAG_ALLOC) ? 1 : 0;
+	e->addr = (paddr >> 12);
+
+	return 1;
+}
+
+void pagetable_unmap(struct pagetable *p, unsigned vaddr)
+{
+	struct pagetable *q;
+	struct pageentry *e;
+
+	unsigned a = vaddr >> 22;
+	unsigned b = vaddr >> 12 & 0x3ff;
+
+	e = &p->entry[a];
+	if(e->present) {
+		q = (struct pagetable *) (e->addr << 12);
+		e = &q->entry[b];
+		e->present = 0;
+	}
+}
+
+void pagetable_delete(struct pagetable *p)
+{
+	unsigned i, j;
+
+	struct pageentry *e;
+	struct pagetable *q;
+
+	for(i = 0; i < ENTRIES_PER_TABLE; i++) {
+		e = &p->entry[i];
+		if(e->present) {
+			q = (struct pagetable *) (e->addr << 12);
+			for(j = 0; j < ENTRIES_PER_TABLE; j++) {
+				e = &q->entry[j];
+				if(e->present && e->avail) {
+					void *paddr;
+					paddr = (void *) (e->addr << 12);
+					page_free(paddr);
+				}
+			}
+			page_free(q);
+		}
+	}
+
+	page_free(p);
+}
+
+void pagetable_alloc(struct pagetable *p, unsigned vaddr, unsigned length, int flags)
+{
+	unsigned npages = length / PAGE_SIZE;
+
+	if(length % PAGE_SIZE)
+		npages++;
+
+	vaddr &= 0xfffff000;
+
+	while(npages > 0) {
+		unsigned paddr;
+		if(!pagetable_getmap(p, vaddr, &paddr, 0)) {
+			pagetable_map(p, vaddr, 0, flags | PAGE_FLAG_ALLOC);
+		}
+		vaddr += PAGE_SIZE;
+		npages--;
+	}
+}
+
+void pagetable_free(struct pagetable *p, unsigned vaddr, unsigned length)
+{
+	unsigned npages = length / PAGE_SIZE;
+
+	if(length % PAGE_SIZE)
+		npages++;
+
+	vaddr &= 0xfffff000;
+
+	while(npages > 0) {
+		unsigned paddr;
+		int flags;
+		if(pagetable_getmap(p, vaddr, &paddr, &flags)) {
+			pagetable_unmap(p, vaddr);
+			if(flags & PAGE_FLAG_ALLOC)
+				page_free((void *) paddr);
+		}
+		vaddr += PAGE_SIZE;
+		npages--;
+	}
+}
+
+struct pagetable *pagetable_load(struct pagetable *p)
+{
+	struct pagetable *oldp;
+      asm("mov %%cr3, %0":"=r"(oldp));
+	asm("mov %0, %%cr3"::"r"(p));
+	return oldp;
+}
+
+void pagetable_refresh()
+{
+	asm("mov %cr3, %eax");
+	asm("mov %eax, %cr3");
+}
+
+void pagetable_enable()
+{
+	asm("movl %cr0, %eax");
+	asm("orl $0x80000000, %eax");
+	asm("movl %eax, %cr0");
+}
+
+struct pagetable *pagetable_duplicate(struct pagetable *sp)
+{
+	unsigned i, j;
+
+	struct pageentry *e;
+	struct pagetable *q;
+
+	struct pageentry *newe;
+	struct pagetable *newq;
+	struct pagetable *newp = pagetable_create();
+	if(!newp)
+		goto cleanup;
+
+	for(i = 0; i < ENTRIES_PER_TABLE; i++) {
+		e = &sp->entry[i];
+		newe = &newp->entry[i];
+		if(e->present) {
+			q = (struct pagetable *) (e->addr << 12);
+			newq = pagetable_create();
+			if(!newq)
+				goto cleanup;
+			memcpy(newe, e, sizeof(struct pageentry));
+			newe->addr = (((unsigned) newq) >> 12);
+			for(j = 0; j < ENTRIES_PER_TABLE; j++) {
+				e = &q->entry[j];
+				newe = &newq->entry[j];
+				memcpy(newe, e, sizeof(struct pageentry));
+				if(e->present) {
+					void *paddr;
+					paddr = (void *) (e->addr << 12);
+					void *new_paddr = 0;
+					if(e->avail) {
+						new_paddr = page_alloc(0);
+						if(!new_paddr)
+							goto cleanup;
+						memcpy(new_paddr, paddr, PAGE_SIZE);
+					} else {
+						new_paddr = paddr;
+					}
+					newe->addr = (((unsigned) new_paddr) >> 12);
+				}
+			}
+		}
+	}
+	return newp;
+      cleanup:
+	printf("Pagetable duplicate errors\n");
+	if(newp) {
+		pagetable_delete(newp);
+	}
+	return 0;
+}
+
+void pagetable_copy(struct pagetable *sp, unsigned saddr, struct pagetable *tp, unsigned taddr, unsigned length);