Nucleus
Barry Kernel threads + threads share address space 6217f0d (3 years, 1 month ago)
/*
* 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/memory.h>
#include <nucleus/object.h>
#include <nucleus/task.h>
#include <nucleus/vfs.h>
#include "namespace.h"
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 *, Object *);
static int region_compare(void *, void *);
/* Virtual Memory Namespace object type */
ObjectType virtualMemoryType = {
.name = "VIRTUAL MEMORY",
.size = sizeof(VirtualMemory),
.new = vm_new,
.delete = vm_delete,
.copy = vm_copy,
};
/* 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, LIST_ORDERED, region_compare);
/* Create stack region */
VMRegion *stack = new(&vmRegionType);
stack->start = 0xDFC00000;
stack->end = 0xE0000000;
stack->prot = PROT_READ | PROT_WRITE;
stack->flags = MAP_PRIVATE | MAP_ANONYMOUS;
vm->stack = stack;
asm volatile("mov %%cr3, %0" : "=r" (vm->pageDir));
}
/* Destroy a Virtual Memory object */
static void
vm_delete(Object *obj)
{
VirtualMemory *vm = (void *) obj;
destroy_list(vm->regions);
if (vm->stack)
put(vm->stack);
}
/* 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);
}
if (parent->stack)
child->stack = copy(parent->stack);
child->pageDir = clone_dir();
}
/* 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);
}
/* Compare two regions of memory */
static int
region_compare(void *a, void *b)
{
VMRegion *ra = a, *rb = b;
return rb->start - ra->start;
}
/* 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);
}
}
}
/* Switch to another virtual memory namespace */
void
switch_to_mm(VirtualMemory *vm)
{
if (!current || !current->vm)
return;
if (!vm->pageDir)
return;
if (current->vm->pageDir != vm->pageDir)
switch_dir(vm->pageDir);
}
/* 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)
return region;
}
region = current->vm->stack;
if (region->start <= addr && region->end > addr)
return region;
return NULL;
}
/* 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, head);
}
add(current->vm->regions, region);
return region;
}