BarryServer : Git

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

// Related

Nucleus

Barry Improved page fault handling and mmap system call 665af0a (3 years, 2 months ago)
diff --git a/include/nucleus/memory.h b/include/nucleus/memory.h
index 5395278..059ba9f 100644
--- a/include/nucleus/memory.h
+++ b/include/nucleus/memory.h
@@ -71,4 +71,6 @@ void *map_page(Page *page);
 
 int verify_access(const void *addr, size_t len, int prot);
 
+VMRegion *vm_create_stack(void);
+
 #endif
diff --git a/memory/fault.c b/memory/fault.c
index 3c5ecfe..02c95ca 100644
--- a/memory/fault.c
+++ b/memory/fault.c
@@ -44,25 +44,26 @@ copy_on_write(VMRegion *region, uintptr_t addr)
 		inode = back->inode;
 		page = find_page(inode->pages, offset);
 	}
+	if (!page) {
+		panic("no page! %#.8x = %#.8x", addr, get_page(addr));
+	}
 	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 {
+	/* Determine what to do */
+	if (usage(page) > 1 || page->frame == zeroFrame) {
+		/* Copy page */
 		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));
+		remove(inode->pages, page);
+		page = newPage;
+	} else if (back && inode == back->inode && private) {
+		/* Page in wrong inode for write */
+		add(front->inode->pages, page);
+		remove(inode->pages, page);
 	}
+	return install_page(addr, page, region->prot);
 }
 
 /* Handle a non-present read page fault */
@@ -84,7 +85,7 @@ not_present_read(VMRegion *region, uintptr_t addr)
 		inode = front->inode;
 		page = find_page(inode->pages, offset);
 		if (page)
-			return install_page(addr, page, PROT_READ);
+			return install_page(addr, page, region->prot);
 		/* Zero-fill if anonymous */
 		if (region->flags & MAP_ANONYMOUS) {
 			page = create_page(inode->pages, zeroFrame, offset);
@@ -127,10 +128,15 @@ not_present_write(VMRegion *region, uintptr_t addr)
 		inode = back->inode;
 		page = find_page(inode->pages, offset);
 		if (page)
-			return install_page(addr, page, PROT_WRITE);
+			return install_page(addr, page, region->prot);
 		page = create_page(inode->pages, alloc_frame(), offset);
-		install_page(addr, page, PROT_WRITE);
-		memset((void *) PAGE_ADDR(addr), 0, PAGE_SIZE);
+		install_page(addr, page, region->prot);
+		/* Zero-fill if anonymous, otherwise read */
+		if (region->flags & MAP_ANONYMOUS)
+			memset((void *) PAGE_ADDR(addr), 0, PAGE_SIZE);
+		else
+			file_mmap(back, (void *) PAGE_ADDR(addr),
+		                  PAGE_SIZE, offset);
 		return;
 	}
 
@@ -139,7 +145,7 @@ not_present_write(VMRegion *region, uintptr_t addr)
 	inode = front->inode;
 	page = find_page(inode->pages, offset);
 	newPage = create_page(inode->pages, alloc_frame(), offset);
-	install_page(addr, newPage, PROT_WRITE);
+	install_page(addr, newPage, region->prot);
 	if (page) {
 		copy_page_frame(PAGE_ADDR(page->frame),
 		                PAGE_ADDR(newPage->frame));
@@ -194,9 +200,9 @@ page_fault_handler(struct InterruptFrame *frame, uint32_t err)
 
 	if (present && write)
 		return copy_on_write(region, addr);
-	if (write)
+	if (!present && write)
 		return not_present_write(region, addr);
-	else
+	if (!present && !write)
 		return not_present_read(region, addr);
 }
 
diff --git a/memory/frame.c b/memory/frame.c
index c477121..2e063d5 100644
--- a/memory/frame.c
+++ b/memory/frame.c
@@ -108,8 +108,10 @@ alloc_frame(void)
 		if (idx != -1)
 			break;
 	}
-	if (idx == -1)
+	if (idx == -1) {
+		panic("Could not allocate frame!");
 		return 0x00000000;
+	}
 
 	set_frame(region, idx);
 	return region->base + (idx << 12);
diff --git a/memory/mmap.c b/memory/mmap.c
new file mode 100644
index 0000000..b7a99eb
--- /dev/null
+++ b/memory/mmap.c
@@ -0,0 +1,53 @@
+/*
+ * This file implements the mmap() routine.  It takes the parameters and sets up
+ * the relevant memory regions.  If no address is passed it will find the first
+ * available one and use it.
+ */
+
+#include <sys/mman.h>
+#include <errno.h>
+#include <nucleus/object.h>
+#include <nucleus/memory.h>
+#include <nucleus/task.h>
+#include <nucleus/vfs.h>
+#include "namespace.h"
+
+/* Map an object into memory */
+void *
+mmap(void *addr, size_t len, int prot, int flags, int fildes, off_t off)
+{
+	VMRegion *region = NULL;
+
+	/* Find gap big enough */
+	if (!addr) {
+		VMRegion *head;
+		foreach (current->vm->regions, head) {
+			if (region && (head->start - region->end >= len))
+				break;
+			region = head;
+		}
+		if (!head && region->end + len >= 0xDFC00000)
+			return (void *) -ENOMEM;
+		addr = (void *) region->end;
+	}
+
+	/* Map anonymous memory */
+	if (flags & MAP_ANONYMOUS) {
+		region = vm_create_region(addr, len, prot, flags, 0, NULL);
+		goto end;
+	}
+
+	/* Map a file */
+	if (fildes < 0 || fildes >= NFILES)
+		return (void *) -EBADF;
+	File *file = get_file_by_fd(fildes);
+	if (!file)
+		return (void *) -EBADF;
+	region = vm_create_region(addr, len, prot, flags, off, file);
+end:
+	if (!region)
+		return (void *) -ENOMEM;
+	void *start = (void *) region->start;
+	put(region);
+	return start;
+}
diff --git a/memory/namespace.h b/memory/namespace.h
index 20a8e20..bd5b0a1 100644
--- a/memory/namespace.h
+++ b/memory/namespace.h
@@ -27,4 +27,7 @@ struct VMRegion {
 	File *front, *back;
 };
 
+VMRegion *vm_create_region(void *addr, size_t len, int prot, int flags,
+                           off_t offset, File *back);
+
 #endif
diff --git a/memory/page.c b/memory/page.c
index 3aa4923..95b1548 100644
--- a/memory/page.c
+++ b/memory/page.c
@@ -6,6 +6,7 @@
 
 #include <nucleus/object.h>
 #include <nucleus/memory.h>
+#include "paging.h"
 #include "namespace.h"
 
 static void page_delete(Object *);
@@ -22,7 +23,8 @@ static void
 page_delete(Object *obj)
 {
 	Page *page = (void *) obj;
-	free_frame(PAGE_ADDR(page->frame));
+	if (page->frame != zeroFrame)
+		free_frame(PAGE_ADDR(page->frame));
 }
 
 /* Create a new Page entry */
diff --git a/memory/paging.c b/memory/paging.c
index 75d762a..fe08a96 100644
--- a/memory/paging.c
+++ b/memory/paging.c
@@ -46,6 +46,8 @@ set_page(uintptr_t vaddr, page_t page)
 	/* Create table if not present */
 	if (!(tables[tbl] & PDE_PRESENT)) {
 		tables[tbl] = alloc_frame() | PDE_PRESENT | PDE_WRITE;
+		if (page & PTE_USER)
+			tables[tbl] |= PDE_USER;
 		memset(mappings + (tbl * 1024), 0, PAGE_SIZE);
 	}
 	mappings[address] = page;
diff --git a/memory/region.c b/memory/region.c
index bea28ca..ef43efc 100644
--- a/memory/region.c
+++ b/memory/region.c
@@ -13,9 +13,10 @@
 
 static void vm_new(Object *);
 static void vm_delete(Object *);
+static void vm_copy(Object *, Object *);
 static void region_new(Object *);
 static void region_delete(Object *);
-static void region_copy(Object *a, Object *b);
+static void region_copy(Object *, Object *);
 
 /* Virtual Memory Namespace object type */
 ObjectType virtualMemoryType = {
@@ -23,6 +24,7 @@ ObjectType virtualMemoryType = {
 	.size = sizeof(VirtualMemory),
 	.new = vm_new,
 	.delete = vm_delete,
+	.copy = vm_copy,
 };
 
 /* VMRegion object type */
@@ -135,9 +137,14 @@ find_region(uintptr_t addr)
 	VMRegion *region;
 	foreach (current->vm->regions, region) {
 		if (region->start <= addr && region->end > addr)
-			break;
+			return region;
 	}
-	return region;
+
+	region = current->stack;
+	if (region->start <= addr && region->end > addr)
+		return region;
+
+	return NULL;
 }
 
 /* Create a new Virtual Memory Region */
@@ -148,7 +155,7 @@ vm_create_region(void *addr, size_t len, int prot, int flags, off_t offset,
 	/* Create new region */
 	VMRegion *region = new(&vmRegionType);
 	region->end = (uintptr_t) addr + len;
-	if (region-> end % PAGE_SIZE)
+	if (region->end % PAGE_SIZE)
 		region->end += PAGE_SIZE - (region->end % PAGE_SIZE);
 	region->start = PAGE_ADDR((uintptr_t) addr);
 	region->prot = prot;
@@ -190,9 +197,20 @@ vm_create_region(void *addr, size_t len, int prot, int flags, off_t offset,
 		}
 		/* Total eclipse */
 		if (head->start >= region->start && head->end <= region->end)
-			remove(current->vm->regions, region);
+			remove(current->vm->regions, head);
 	}
 
 	add(current->vm->regions, region);
 	return region;
 }
+
+/* Create a Virtual Memory Region for the stack */
+VMRegion *
+vm_create_stack(void)
+{
+	VMRegion *stack = vm_create_region((void *) 0xDFC00000, 0x400000,
+	                                   PROT_READ | PROT_WRITE,
+	                                   MAP_PRIVATE | MAP_ANONYMOUS, 0, NULL);
+	remove(current->vm->regions, stack);
+	return stack;
+}
diff --git a/memory/user.c b/memory/user.c
index cdf4bcd..ec19d1f 100644
--- a/memory/user.c
+++ b/memory/user.c
@@ -6,19 +6,27 @@
  */
 
 #include <nucleus/memory.h>
+#include <nucleus/task.h>
+#include "namespace.h"
+
+VMRegion *find_region(uintptr_t addr);
 
 /* Check if user has access to a region of memory */
 int
 verify_access(const void *addr, size_t len, int prot)
 {
-	/*
-	 * This should iterate all memory regions to check if the address range
-	 * fits inside one, if not then access should be denied.  If a matching
-	 * region is found, the prot parameter should be checked against the
-	 * protection value for that region.  If this function is called from
-	 * the kernel directly, or the user did not pass an address or a length
-	 * (NULL and 0, respectively), then it should just grant access
-	 * immediately.  This can be implemented when userspace is implemented.
-	 */
-	return 1;
+	if (!len || !(current && current->inSyscall))
+		return 1;
+
+	VMRegion *region;
+	int minprot = ~0;
+	uintptr_t end = (uintptr_t) addr;
+	do {
+		region = find_region(end);
+		if (!region)
+			return 0;
+		minprot &= region->prot;
+		end = region->end;
+	} while (end < (uintptr_t) addr + len);
+	return (minprot & prot);
 }