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