/* * 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 #include #include #include #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; }