BarryServer : Git

All the code for all my projects
// BarryServer : Git / Nucleus / blob / master / memory / region.c

// Related

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;
}