Nucleus
Barry Virtual Memory Regions and namespace 381dc7b (3 years, 3 months ago)
diff --git a/memory/namespace.h b/memory/namespace.h
new file mode 100644
index 0000000..20a8e20
--- /dev/null
+++ b/memory/namespace.h
@@ -0,0 +1,30 @@
+#ifndef MEMORY_NAMESPACE_H
+#define MEMORY_NAMESPACE_H
+
+#include <nucleus/object.h>
+#include <nucleus/vfs.h>
+
+/* Structure for a Page in a Cache */
+struct Page {
+ Object obj;
+ off_t offset;
+ page_t frame;
+};
+
+/* Virtual Memory namespace */
+struct VirtualMemory {
+ Object obj;
+ ObjectList *regions;
+};
+
+/* Structure for a Virtual Memory Region object */
+struct VMRegion {
+ Object obj;
+ uintptr_t start, end;
+ int prot;
+ int flags;
+ off_t offset;
+ File *front, *back;
+};
+
+#endif
diff --git a/memory/page.c b/memory/page.c
index 6e1e67a..3aa4923 100644
--- a/memory/page.c
+++ b/memory/page.c
@@ -6,19 +6,7 @@
#include <nucleus/object.h>
#include <nucleus/memory.h>
-
-/* Structure for a Page in a Cache */
-struct Page {
- Object obj;
- off_t offset;
- page_t frame;
-};
-
-/* Information for find callback */
-struct FindData {
- off_t offset;
- Page *result;
-};
+#include "namespace.h"
static void page_delete(Object *);
@@ -37,19 +25,6 @@ page_delete(Object *obj)
free_frame(PAGE_ADDR(page->frame));
}
-/* Callback for finding a Page by offset */
-static int
-compare_page_offset(void *object, void *data)
-{
- Page *page = object;
- struct FindData *find = data;
- if (page->offset == find->offset) {
- find->result = get(page);
- return 1;
- }
- return 0;
-}
-
/* Create a new Page entry */
Page *
create_page(ObjectList *cache, page_t frame, off_t offset)
@@ -58,6 +33,7 @@ create_page(ObjectList *cache, page_t frame, off_t offset)
page->frame = frame;
page->offset = offset;
add(cache, page);
+ put(page);
return page;
}
@@ -65,12 +41,23 @@ create_page(ObjectList *cache, page_t frame, off_t offset)
Page *
find_page(ObjectList *cache, off_t offset)
{
- struct FindData data = {
- .offset = offset,
- .result = NULL,
- };
- iterate(cache, compare_page_offset, &data);
- return data.result;
+ Page *page;
+ foreach (cache, page) {
+ if (page->offset == offset)
+ break;
+ }
+ return page;
+}
+
+/* Install a page into a virtual address space */
+void
+install_page(uintptr_t addr, Page *page, int prot)
+{
+ int access = PTE_PRESENT | PTE_USER;
+ if (prot & PROT_WRITE)
+ access |= PTE_WRITE;
+ set_page(PAGE_ADDR(addr), page->frame | access);
+ flush_tlb(PAGE_ADDR(addr));
}
/* Get the virtual address for the mapping of a page */
diff --git a/memory/region.c b/memory/region.c
new file mode 100644
index 0000000..3cf9fa4
--- /dev/null
+++ b/memory/region.c
@@ -0,0 +1,198 @@
+/*
+ * This file implements the Virtual Memory namespace, the VMRegion object and
+ * the associated function. A Virtual Memory namespace stores all of the
+ * VMRegions for a task. A VMRegion represents a region of virtual memory that
+ * is mapped in the task's virtual address space.
+ */
+
+#include <nucleus/object.h>
+#include <nucleus/memory.h>
+#include <nucleus/task.h>
+#include <nucleus/vfs.h>
+#include "namespace.h"
+
+static void vm_new(Object *);
+static void vm_delete(Object *);
+static void region_new(Object *);
+static void region_delete(Object *);
+static void region_copy(Object *a, Object *b);
+
+/* Virtual Memory Namespace object type */
+ObjectType virtualMemoryType = {
+ .name = "VIRTUAL MEMORY",
+ .size = sizeof(VirtualMemory),
+ .new = vm_new,
+ .delete = vm_delete,
+};
+
+/* VMRegion object type */
+ObjectType vmRegionType = {
+ .name = "VIRTUAL MEMORY REGION",
+ .size = sizeof(VMRegion),
+ .delete = region_delete,
+ .copy = region_copy,
+};
+
+/* Create a new Virtual Memory object */
+static void
+vm_new(Object *obj)
+{
+ VirtualMemory *vm = (void *) obj;
+ vm->regions = create_list(&vmRegionType);
+}
+
+/* Destroy a Virtual Memory object */
+static void
+vm_delete(Object *obj)
+{
+ VirtualMemory *vm = (void *) obj;
+ destroy_list(vm->regions);
+}
+
+/* Copy a Virtual Memory object */
+static void
+vm_copy(Object *a, Object *b)
+{
+ VirtualMemory *parent = (void *) a, *child = (void *) b;
+ VMRegion *region, *insert;
+ foreach (parent->regions, region) {
+ insert = copy(region);
+ add(child->regions, insert);
+ put(insert);
+ }
+}
+
+/* Delete a Virtual Memory Region */
+static void
+region_delete(Object *obj)
+{
+ VMRegion *region = (void *) obj;
+
+ /* Unlink files */
+ if (region->front)
+ put(region->front);
+ if (region->back)
+ put(region->back);
+
+ /* Clean page directory */
+ uintptr_t addr;
+ for (addr = PAGE_ADDR(region->start); addr < region->end;
+ addr += PAGE_SIZE) {
+ set_page(addr, 0x00000000);
+ flush_tlb(addr);
+ }
+}
+
+/* Copy a Virtual Memory Region */
+static void
+region_copy(Object *a, Object *b)
+{
+ VMRegion *parent = (void *) a, *child = (void *) b;
+ child->start = parent->start;
+ child->end = parent->end;
+ child->prot = parent->prot;
+ child->flags = parent->flags;
+ child->offset = parent->offset;
+ /* Front (anonymous regions) */
+ if (parent->front && (parent->flags & MAP_PRIVATE))
+ child->front = copy(parent->front);
+ else if (parent->front)
+ child->front = get(parent->front);
+ /* Back (always a disk file) */
+ if (parent->back)
+ child->back = get(parent->back);
+}
+
+/* Remove a range of pages from a region's page cache */
+static void
+remove_cache_range(VMRegion *region, uintptr_t start, uintptr_t end)
+{
+ Page *page;
+ Inode *inode;
+ uintptr_t p;
+ for (p = 0; p < end - start; p += PAGE_SIZE) {
+ page = NULL;
+ if (!page && region->front) {
+ inode = region->front->inode;
+ page = find_page(inode->pages, region->offset + p);
+ }
+ if (!page && region->back) {
+ inode = region->back->inode;
+ page = find_page(inode->pages, region->offset + p);
+ }
+ if (page) {
+ set_page(PAGE_ADDR(start) + p, 0x00000000);
+ flush_tlb(PAGE_ADDR(start) + p);
+ remove(inode->pages, page);
+ }
+ }
+}
+
+/* Find a Virtual Memory Region by address */
+VMRegion *
+find_region(uintptr_t addr)
+{
+ VMRegion *region;
+ foreach (current->vm->regions, region) {
+ if (region->start <= addr && region->end > addr)
+ break;
+ }
+ return region;
+}
+
+/* Create a new Virtual Memory Region */
+VMRegion *
+vm_create_region(void *addr, size_t len, int prot, int flags, off_t offset,
+ File *back)
+{
+ /* Create new region */
+ VMRegion *region = new(&vmRegionType);
+ region->end = (uintptr_t) addr + len;
+ if (region-> end % PAGE_SIZE)
+ region->end += PAGE_SIZE - (region->end % PAGE_SIZE);
+ region->start = PAGE_ADDR((uintptr_t) addr);
+ region->prot = prot;
+ region->flags = flags;
+ region->offset = offset;
+ region->front = NULL;
+ region->back = NULL;
+ if (back)
+ region->back = get(back);
+
+ /* Fix overlaps */
+ VMRegion *head, *insert;
+ foreach (current->vm->regions, head) {
+ if (head->start >= region->end || head->end <= region->start)
+ continue;
+
+ /* Middle eclipsed */
+ if (head->start < region->start && head->end > region->end) {
+ /* Split into two regions */
+ insert = copy(head);
+ remove_cache_range(head, region->start, head->end);
+ head->end = region->start;
+ remove_cache_range(insert, insert->start, region->end);
+ insert->offset += (region->end - insert->start);
+ insert->start = region->end;
+ add(current->vm->regions, insert);
+ put(insert);
+ }
+ /* Start eclipsed */
+ if (head->start >= region->start && head->end > region->end) {
+ remove_cache_range(head, head->start, region->end);
+ head->offset += (region->end - head->start);
+ head->start = region->end;
+ }
+ /* End eclipsed */
+ if (head->start < region->start && head->end <= region->end) {
+ remove_cache_range(head, region->start, head->end);
+ head->end = region->start;
+ }
+ /* Total eclipse */
+ if (head->start >= region->start && head->end <= region->end)
+ remove(current->vm->regions, region);
+ }
+
+ add(current->vm->regions, region);
+ return region;
+}