BarryServer : Git

All the code for all my projects
// BarryServer : Git / Nucleus / commit / b0cc93c299def7155031fe255095ce906485cd4d

// Related

Nucleus

Barry Virtual Memory page fault handling b0cc93c (3 years, 2 months ago)
diff --git a/include/nucleus/vfs.h b/include/nucleus/vfs.h
index 40239c7..fa193fb 100644
--- a/include/nucleus/vfs.h
+++ b/include/nucleus/vfs.h
@@ -85,6 +85,7 @@ struct FileOps {
 	size_t (*read)(File *, char *, size_t, off_t);
 	size_t (*write)(File *, char *, size_t, off_t);
 	int (*open)(File *);
+	void (*mmap)(File *, void *, size_t, off_t);
 };
 
 extern ObjectType fstypeType;
@@ -96,6 +97,7 @@ extern ObjectType dirEntryType;
 extern ObjectType fileType;
 
 void init_vfs(void);
+File *create_anonymous_file(void);
 void register_fstype(const char *name,	mount_callback_t mount);
 int mount(const char *src, const char *target, const char *type,
           unsigned long flags, void *data);
@@ -115,6 +117,7 @@ DirEntry *find_direntry(ObjectList *list, const char *name);
 int file_open(File *file);
 size_t file_read(File *file, char *buf, size_t count);
 size_t file_write(File *file, char *buf, size_t count);
+void file_mmap(File *file, void *addr, size_t len, off_t offset);
 /* Files namespace functions */
 File *get_file_by_fd(int fd);
 int allocate_fd(void);
diff --git a/kernel/acpi/apic.c b/kernel/acpi/apic.c
index 8bdfbf8..73da356 100644
--- a/kernel/acpi/apic.c
+++ b/kernel/acpi/apic.c
@@ -228,4 +228,5 @@ ap_startup(void)
 
 	/* Start running tasks */
 	schedule();
+	__builtin_unreachable();
 }
diff --git a/memory/fault.c b/memory/fault.c
index 08a4252..3c5ecfe 100644
--- a/memory/fault.c
+++ b/memory/fault.c
@@ -1,14 +1,204 @@
 /*
- * This file contains the page fault handler.
+ * This file contains the page fault handler.  The main handler determines what
+ * went wrong and calls the relevant routine to handle it.  That includes:
+ * non-present reads, non-present writes, copy-on-write.
  * There is an early handler which just gives out memory when requested.  This
  * is used by the kernel before it has initialised multi-tasking and the virtual
  * file system.
  */
 
 #include <stdint.h>
+#include <string.h>
 #include <nucleus/cpu.h>
 #include <nucleus/memory.h>
+#include <nucleus/task.h>
 #include <nucleus/panic.h>
+#include "paging.h"
+#include "namespace.h"
+
+VMRegion *find_region(uintptr_t addr);
+void copy_page_frame(page_t src, page_t dest);
+
+/* Copy on write */
+static void
+copy_on_write(VMRegion *region, uintptr_t addr)
+{
+	Inode *inode;
+	Page *page = NULL, *newPage = NULL;
+	File *front = region->front,
+	     *back  = region->back;
+	off_t offset = (PAGE_ADDR(addr) - region->start) + region->offset;
+
+	/* Handle uninitialised anonymous region */
+	int private = region->flags & MAP_PRIVATE;
+	int anonymous = (region->flags & MAP_ANONYMOUS);
+	if (!front && (private || anonymous))
+		region->front = front = create_anonymous_file();
+
+	/* Find original page frame */
+	if (!page && front) {
+		inode = front->inode;
+		page = find_page(inode->pages, offset);
+	}
+	if (!page && back) {
+		inode = back->inode;
+		page = find_page(inode->pages, offset);
+	}
+	ASSERT(page);
+
+	/* Copy already happened, or region is shared file */
+	if (usage(page) == 1 && page->frame != zeroFrame
+	 && ((front && inode == front->inode) || !private))
+		return install_page(addr, page, PROT_WRITE);
+
+	/* Page is still in use, or in wrong inode for writing */
+	if (usage(inode) == 1) {
+		add(front->inode->pages, page);
+		install_page(addr, page, PROT_WRITE);
+		remove(inode->pages, page);
+	} else {
+		newPage = create_page(front->inode->pages,
+		                      alloc_frame(), offset);
+		install_page(addr, newPage, PROT_WRITE);
+		copy_page_frame(PAGE_ADDR(page->frame),
+		                PAGE_ADDR(newPage->frame));
+	}
+}
+
+/* Handle a non-present read page fault */
+static void
+not_present_read(VMRegion *region, uintptr_t addr)
+{
+	Inode *inode;
+	Page *page = NULL;
+	File *front = region->front,
+	     *back  = region->back;
+	off_t offset = (PAGE_ADDR(addr) - region->start) + region->offset;
+
+	/* Handle uninitialised anonymous regions */
+	if (!front && (region->flags & MAP_ANONYMOUS))
+		region->front = front = create_anonymous_file();
+
+	/* Attempt to use front */
+	if (front) {
+		inode = front->inode;
+		page = find_page(inode->pages, offset);
+		if (page)
+			return install_page(addr, page, PROT_READ);
+		/* Zero-fill if anonymous */
+		if (region->flags & MAP_ANONYMOUS) {
+			page = create_page(inode->pages, zeroFrame, offset);
+			return install_page(addr, page, PROT_READ);
+		}
+	}
+
+	/* Use back */
+	ASSERT(back);
+	inode = back->inode;
+	page = find_page(inode->pages, offset);
+	if (page)
+		return install_page(addr, page, PROT_READ);
+	/* Create new block cache entry */
+	page = create_page(inode->pages, alloc_frame(), offset);
+	install_page(addr, page, PROT_READ);
+	file_mmap(back, (void *) PAGE_ADDR(addr), PAGE_SIZE, offset);
+}
+
+/* Handle a non-present write page fault */
+static void
+not_present_write(VMRegion *region, uintptr_t addr)
+{
+	Inode *inode;
+	Page *page = NULL, *newPage = NULL;
+	File *front = region->front,
+	     *back  = region->back;
+	off_t offset = (PAGE_ADDR(addr) - region->start) + region->offset;
+
+	/* Handle uninitialised anonymous regions */
+	if (!front && ((region->flags & MAP_PRIVATE)
+	 || (region->flags & MAP_ANONYMOUS)))
+		region->front = front = create_anonymous_file();
+
+	/* Shared region, write through to back */
+	if (region->flags & MAP_SHARED) {
+		if (region->flags & MAP_ANONYMOUS)
+			back = front;
+		ASSERT(back);
+		inode = back->inode;
+		page = find_page(inode->pages, offset);
+		if (page)
+			return install_page(addr, page, PROT_WRITE);
+		page = create_page(inode->pages, alloc_frame(), offset);
+		install_page(addr, page, PROT_WRITE);
+		memset((void *) PAGE_ADDR(addr), 0, PAGE_SIZE);
+		return;
+	}
+
+	/* Private region, copy to front */
+	ASSERT(front);
+	inode = front->inode;
+	page = find_page(inode->pages, offset);
+	newPage = create_page(inode->pages, alloc_frame(), offset);
+	install_page(addr, newPage, PROT_WRITE);
+	if (page) {
+		copy_page_frame(PAGE_ADDR(page->frame),
+		                PAGE_ADDR(newPage->frame));
+		remove(inode->pages, page);
+		return;
+	}
+
+	/* Anonymous region, zero-fill */
+	if (region->flags & MAP_ANONYMOUS) {
+		memset((void *) PAGE_ADDR(addr), 0, PAGE_SIZE);
+		return;
+	}
+
+	/* Use back */
+	ASSERT(back);
+	inode = back->inode;
+	page = find_page(inode->pages, offset);
+	if (page) {
+		copy_page_frame(PAGE_ADDR(page->frame),
+		                PAGE_ADDR(newPage->frame));
+		remove(inode->pages, page);
+	} else {
+		file_mmap(back, (void *) PAGE_ADDR(addr), PAGE_SIZE, offset);
+	}
+}
+
+/* Page fault handler */
+void
+page_fault_handler(struct InterruptFrame *frame, uint32_t err)
+{
+	uintptr_t addr;
+	asm volatile("mov %%cr2, %0" : "=r" (addr));
+	uint8_t present = err & (1 << 0);
+	uint8_t write   = err & (1 << 1);
+	uint8_t user    = err & (1 << 2);
+
+	/* Iterate VM Regions */
+	VMRegion *region = find_region(addr);
+	/* Not in a region */
+	if (__builtin_expect(!region, 0)) {
+		page_t pg = get_page(addr);
+		panic("Page Fault [%d:%d] (%#.8x -> %#.8x [tbl:%d, pg:%d][%#.8x], %s, %s, %s)",
+		      current->tgid, current->tid, frame->eip,
+		      addr, (addr >> 12) / 1024, (addr >> 12) % 1024, pg,
+		      present ? "present" : "not present",
+		      write ? "write" : "read",
+		      user ? "user" : "kernel");
+	}
+
+	if (user && write && !(region->prot & PROT_WRITE))
+		panic("Segmentation violation");
+
+	if (present && write)
+		return copy_on_write(region, addr);
+	if (write)
+		return not_present_write(region, addr);
+	else
+		return not_present_read(region, addr);
+}
 
 /* Early (pre-VFS/tasking) page fault handler */
 void
diff --git a/vfs/vfs.c b/vfs/vfs.c
index bbd61db..303f9c7 100644
--- a/vfs/vfs.c
+++ b/vfs/vfs.c
@@ -9,6 +9,8 @@
 #include <nucleus/object.h>
 #include <nucleus/vfs.h>
 
+extern FileOps tmpfsFileOps;
+
 Inode *tmpfs_mount(FSType *type, int flags, const char *dev, void *data);
 Inode *devfs_mount(FSType *type, int flags, const char *dev, void *data);
 
@@ -23,3 +25,13 @@ init_vfs(void)
 	mkdir("dev", 0);
 	mount("devfs", "/dev", "devfs", 0, NULL);
 }
+
+/* Create an anonymous file */
+File *
+create_anonymous_file(void)
+{
+	File *file = new(&fileType);
+	file->inode = new(&inodeType);
+	file->ops = &tmpfsFileOps;
+	return file;
+}