BarryServer : Git

All the code for all my projects
// BarryServer : Git / Orion / blob / 7ae31b03c38925f5d527e6303765925586731209 / mem / vm.c

// Related

Orion

Barry Importing existing Orion kernel d41a53c (2 years, 4 months ago)
/*
 * This file handles the Virtual Memory system for processes.  It splits each
 * process into several memory regions, and points each of those reasons to a
 * memory object.  Each object can be modified on demand, and can be made up of
 * several pages, and backed by various stores.  This allows objects such as
 * files to be easily mapped into an address space, or for large regions to be
 * shared between processes.
 */

#include <stdint.h>
#include <sys/types.h>
#include <sys/mman.h>
#include <errno.h>
#include "heap.h"
#include "paging.h"
#include "vm.h"
#include "../vfs/vfs.h"
#include "../vfs/inode.h"
#include "../vfs/cache.h"
#include "../vfs/tmpfs/fs.h"
#include "../task/task.h"
#include "../proc/proc.h"
#include "../screen.h"

/* Unmap a range of pages from page directory */
static void
unmap_page_range(uintptr_t start, uintptr_t end)
{
	uintptr_t addr;
	for (addr = start; addr < end; addr += 0x1000) {
        	*get_page((void *) addr) = 0x00000000;
	        flush_tlb(addr);
	}
}

/* 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 += 0x1000) {
		page = NULL;
		if (!page && region->front) {
			inode = region->front->inode;
			page = page_find(inode, region->offset + p);
		}
		if (!page && region->back) {
			inode = region->back->inode;
			page = page_find(inode, region->offset + p);
		}
		if (page)
			page_remove(inode, page);
	}
}

/* Create a new VM Region */
VMRegion *
vm_create_region(void *addr, size_t len, int prot, int flags, off_t offset,
                 File *back)
{
	/* Create new region */
	VMRegion *head, *next, *insert, *region = kmalloc(sizeof(VMRegion));
	region->end = (uintptr_t) addr + len;
	if (region->end % 0x1000)
		region->end += 0x1000 - (region->end % 0x1000);
	region->start = (uintptr_t) addr & ~0xFFF;
	region->prot = prot;
	region->flags = flags;
	region->offset = offset;
	region->front = NULL;
	region->back = NULL;
	if (back)
		region->back = file_get(back);

	/* Create new list */
	if (!current->vm->regions) {
		current->vm->regions = region;
		return region;
	}

	/* Fix overlaps */
	uintptr_t p;
	for (head = current->vm->regions; head; head = next) {
		next = head->next; /* head may be destroyed during iteration */
		if (head->start >= region->end || head->end <= region->start)
			continue;

		/* Middle eclipsed */
		if (head->start < region->start && head->end > region->end) {
			/* Create region after current */
			insert = kmalloc(sizeof(VMRegion));
			insert->end = head->end;
			insert->start = head->end = region->start;
			insert->prot = head->prot;
			insert->flags = head->flags;
			insert->offset = head->offset;
			insert->offset += (insert->start - head->start);
			if (head->front)
				insert->front = file_get(head->front);
			if (head->back)
				insert->back = file_get(head->back);
			/* Insert into list */
			insert->next = head->next;
			head->next = insert;
			insert->prev = head;
			insert->next->prev = insert;
			/* Inserted region will be dealt with on next pass */
		}
		/* Start eclipsed */
		if (head->start >= region->start && head->end > region->end) {
			unmap_page_range(head->start, region->end);
			remove_cache_range(head, head->start, region->end);
			head->start = region->end;
			head->offset += (region->end - head->start);
		}
		/* End eclipsed */
		if (head->start < region->start && head->end <= region->end) {
			unmap_page_range(region->start, head->end);
			remove_cache_range(head, region->start, head->end);
			head->end = region->start;
		}
		/* Total eclipse */
		if (head->start >= region->start && head->end <= region->end)
			vm_destroy_region(head);
	}
	/* Add to ordered list */
	for (head = current->vm->regions; head->next; head = head->next)
		if (head->end <= region->start
		 && head->next->start >= region->end)
			break;
	region->next = head->next;
	region->prev = head;
	region->prev->next = region;
	if (region->next)
		region->next->prev = region;

	return region;
}

/* Remove a VM Region */
void
vm_remove_region(VMRegion *region)
{
	/* Remove from list */
	if (current->vm->regions == region)
		current->vm->regions = region->next;
	if (region->prev)
		region->prev->next = region->next;
	if (region->next)
		region->next->prev = region->prev;
//	region->prev = region->next = NULL;
}

/* Destroy a VM Region */
void
vm_destroy_region(VMRegion *region)
{
	/* Unlink files */
	if (region->front)
		file_put(region->front);
	if (region->back)
		file_put(region->back);

	/* Clean page directory */
	unmap_page_range(region->start, region->end);

	vm_remove_region(region);
	kfree(region);
}

/* Clone a set of VM Regions */
VMRegion *
vm_clone_regions(VMRegion *head)
{
	if (!head)
		return NULL;

	VMRegion *newhead = NULL, *newcurr, *newprev = NULL;
	VMRegion *curr = head;
	off_t i;
	Page *page;
	File *file;

	while (curr) {
		newcurr = kmalloc(sizeof(VMRegion));
		if (!newhead)
			newhead = newcurr;

		newcurr->prev = newprev;
		newcurr->next = NULL;
		if (newprev)
			newprev->next = newcurr;

		newcurr->start = curr->start;
		newcurr->end = curr->end;
		newcurr->prot = curr->prot;
		newcurr->flags = curr->flags;
		newcurr->offset = curr->offset;
		/* Front (anonymous regions) */
		if (curr->front && (curr->flags & MAP_PRIVATE)) {
			/* Copy the file */
			file = kmalloc(sizeof(File));
			file->inode = inode_get(kmalloc(sizeof(Inode)));
			file->ops = &tmpfsFileOps;
			newcurr->front = file_get(file);
			for (i = 0; i < curr->end - curr->start; i += 0x1000) {
				page = page_find(curr->front->inode,
				                 i + curr->offset);
				if (page)
					page_add(file->inode, page);
			}
		} else if (curr->front) {
			newcurr->front = file_get(curr->front);
		}
		/* Back (always a file) */
		if (curr->back)
			newcurr->back = file_get(curr->back);

		curr = curr->next;
		newprev = newcurr;
	};

	return newhead;
}

/* Map an object into memory */
void *
mmap(void *addr, size_t len, int prot, int flags, int fildes, off_t off)
{
	VMRegion *region;

	/* Find gap big enough */
	if (!addr) {
		for (region = current->vm->regions;
		     region->next; region = region->next) {
			if (region->next->start - region->end >= len)
				break;
		}
		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 = current->files->fd[fildes];
	if (!file)
		return (void *) -EBADF;
	region = vm_create_region(addr, len, prot, flags, off, file);
end:
	if (!region)
		return (void *) -ENOMEM;
	return (void *) region->start;
}