mirror of
https://github.com/Mathewnd/Astral.git
synced 2026-11-01 15:08:21 +00:00
vmm: use red-black trees
This commit is contained in:
@@ -4,6 +4,7 @@
|
|||||||
#include <arch/mmu.h>
|
#include <arch/mmu.h>
|
||||||
#include <mutex.h>
|
#include <mutex.h>
|
||||||
#include <kernel/vfs.h>
|
#include <kernel/vfs.h>
|
||||||
|
#include <rbtree.h>
|
||||||
|
|
||||||
#define VMM_FLAGS_PAGESIZE 1
|
#define VMM_FLAGS_PAGESIZE 1
|
||||||
#define VMM_FLAGS_ALLOCATE 2
|
#define VMM_FLAGS_ALLOCATE 2
|
||||||
@@ -27,8 +28,7 @@ typedef struct {
|
|||||||
|
|
||||||
struct vmmcache_t;
|
struct vmmcache_t;
|
||||||
typedef struct vmmrange_t{
|
typedef struct vmmrange_t{
|
||||||
struct vmmrange_t *next;
|
rbtree_t rbtree_node;
|
||||||
struct vmmrange_t *prev;
|
|
||||||
void *start;
|
void *start;
|
||||||
size_t size;
|
size_t size;
|
||||||
int flags;
|
int flags;
|
||||||
@@ -57,7 +57,7 @@ typedef struct vmmcache_t {
|
|||||||
|
|
||||||
typedef struct {
|
typedef struct {
|
||||||
mutex_t lock;
|
mutex_t lock;
|
||||||
vmmrange_t *ranges;
|
rbtree_t *ranges;
|
||||||
void *start;
|
void *start;
|
||||||
void *end;
|
void *end;
|
||||||
} vmmspace_t;
|
} vmmspace_t;
|
||||||
|
|||||||
@@ -12,6 +12,9 @@
|
|||||||
#define likely(x) __builtin_expect(!!(x), 1)
|
#define likely(x) __builtin_expect(!!(x), 1)
|
||||||
#define unlikely(x) __builtin_expect(!!(x), 0)
|
#define unlikely(x) __builtin_expect(!!(x), 0)
|
||||||
|
|
||||||
|
#define container_of(ptr, type, member) \
|
||||||
|
((type *)((void *)ptr - offsetof(type, member)))
|
||||||
|
|
||||||
static inline long abs(long x) {
|
static inline long abs(long x) {
|
||||||
return x < 0 ? -x : x;
|
return x < 0 ? -x : x;
|
||||||
}
|
}
|
||||||
|
|||||||
+252
-225
@@ -21,10 +21,8 @@ static vmmcache_t *newcache() {
|
|||||||
ptr->header.next = NULL;
|
ptr->header.next = NULL;
|
||||||
MUTEX_INIT(&ptr->header.lock);
|
MUTEX_INIT(&ptr->header.lock);
|
||||||
|
|
||||||
for (uintmax_t i = 0; i < VMM_RANGES_PER_CACHE; ++i) {
|
for (uintmax_t i = 0; i < VMM_RANGES_PER_CACHE; ++i)
|
||||||
ptr->ranges[i].size = 0;
|
ptr->ranges[i].size = 0;
|
||||||
ptr->ranges[i].next = NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
return ptr;
|
return ptr;
|
||||||
}
|
}
|
||||||
@@ -95,43 +93,61 @@ static vmmspace_t *getspace(void *vaddr) {
|
|||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int rbtree_value_compare(void *addr, rbtree_t *node) {
|
||||||
|
vmmrange_t *range = container_of(node, vmmrange_t, rbtree_node);
|
||||||
|
void *range_top = RANGE_TOP(range);
|
||||||
|
|
||||||
|
if (addr >= range_top)
|
||||||
|
return 1;
|
||||||
|
|
||||||
|
if (addr < range->start)
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
// get a range from an address
|
// get a range from an address
|
||||||
static vmmrange_t *getrange(vmmspace_t *space, void *addr) {
|
static vmmrange_t *getrange(vmmspace_t *space, void *addr) {
|
||||||
vmmrange_t *range = space->ranges;
|
rbtree_t *rbtree = rbtree_lookup(space->ranges, addr, rbtree_value_compare);
|
||||||
while (range) {
|
return container_of(rbtree, vmmrange_t, rbtree_node);
|
||||||
if (addr >= range->start && addr < RANGE_TOP(range))
|
|
||||||
break;
|
|
||||||
range = range->next;
|
|
||||||
}
|
|
||||||
return range;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// get start of range that fits specific size from specific offset
|
// get start of range that fits specific size from specific offset
|
||||||
static void *getfreerange(vmmspace_t *space, void *addr, size_t size) {
|
static void *getfreerange(vmmspace_t *space, void *addr, size_t size) {
|
||||||
vmmrange_t *range = space->ranges;
|
rbtree_t *rbtree = space->ranges;
|
||||||
if (addr == NULL)
|
if (addr == NULL)
|
||||||
addr = space->start;
|
addr = space->start;
|
||||||
|
|
||||||
// if theres no ranges
|
// no ranges
|
||||||
if (range == NULL)
|
if (rbtree == NULL)
|
||||||
return addr;
|
return addr;
|
||||||
|
|
||||||
|
rbtree = rbtree_first(rbtree);
|
||||||
|
vmmrange_t *range = container_of(rbtree, vmmrange_t, rbtree_node);
|
||||||
|
|
||||||
// if theres free space before the first range
|
// if theres free space before the first range
|
||||||
if (range->start != space->start && addr < range->start && (uintptr_t)range->start - (uintptr_t)addr >= size)
|
if (range->start != space->start && addr < range->start && (uintptr_t)range->start - (uintptr_t)addr >= size)
|
||||||
return addr;
|
return addr;
|
||||||
|
|
||||||
while (range->next) {
|
// check for space between two ranges
|
||||||
|
rbtree_t *next = rbtree_successor(rbtree);
|
||||||
|
while (next) {
|
||||||
|
vmmrange_t *next_range = container_of(next, vmmrange_t, rbtree_node);
|
||||||
|
|
||||||
void *rangetop = RANGE_TOP(range);
|
void *rangetop = RANGE_TOP(range);
|
||||||
if (addr < rangetop)
|
if (addr < rangetop)
|
||||||
addr = rangetop;
|
addr = rangetop;
|
||||||
|
|
||||||
if (addr < range->next->start) {
|
if (addr < next_range->start) {
|
||||||
size_t freesize = (uintptr_t)range->next->start - (uintptr_t)addr;
|
size_t freesize = (uintptr_t)next_range->start - (uintptr_t)addr;
|
||||||
if (freesize >= size)
|
if (freesize >= size)
|
||||||
return addr;
|
return addr;
|
||||||
}
|
}
|
||||||
range = range->next;
|
|
||||||
|
rbtree = next;
|
||||||
|
range = container_of(rbtree, vmmrange_t, rbtree_node);
|
||||||
|
|
||||||
|
next = rbtree_successor(next);
|
||||||
}
|
}
|
||||||
|
|
||||||
// if theres free space after the last range
|
// if theres free space after the last range
|
||||||
@@ -145,80 +161,60 @@ static void *getfreerange(vmmspace_t *space, void *addr, size_t size) {
|
|||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void insertrange(vmmspace_t *space, vmmrange_t *newrange) {
|
static int rbtree_compare(rbtree_t *a, rbtree_t *b) {
|
||||||
vmmrange_t *range = space->ranges;
|
vmmrange_t *range_a = container_of(a, vmmrange_t, rbtree_node);
|
||||||
|
vmmrange_t *range_b = container_of(b, vmmrange_t, rbtree_node);
|
||||||
|
|
||||||
// space has no other ranges
|
if (range_a->start == range_b->start)
|
||||||
if (range == NULL) {
|
return 0;
|
||||||
space->ranges = newrange;
|
|
||||||
newrange->next = NULL;
|
|
||||||
newrange->prev = NULL;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
void *newrangetop = RANGE_TOP(newrange);
|
return range_a->start > range_b->start ? 1 : -1;
|
||||||
|
}
|
||||||
|
|
||||||
// range is before the first space range
|
static bool compatible(vmmrange_t *prev, vmmrange_t *next) {
|
||||||
if (newrangetop <= range->start) {
|
void *prev_range_top = RANGE_TOP(prev);
|
||||||
space->ranges = newrange;
|
|
||||||
newrange->next = range;
|
|
||||||
range->prev = newrange;
|
|
||||||
newrange->prev = NULL;
|
|
||||||
goto fragcheck;
|
|
||||||
}
|
|
||||||
|
|
||||||
while (range->next) {
|
return prev_range_top == next->start && prev->flags == next->flags && prev->mmuflags == next->mmuflags && // general compatibility
|
||||||
if (newrange->start >= RANGE_TOP(range) && newrange->start < range->next->start) { // space inbetween two other ranges
|
((prev->flags & VMM_FLAGS_FILE) == 0 || (prev->vnode == next->vnode && prev->offset + prev->size == next->offset)); // same file mapping, if any
|
||||||
newrange->next = range->next;
|
}
|
||||||
if (newrange->next)
|
|
||||||
newrange->next->prev = newrange;
|
|
||||||
|
|
||||||
newrange->prev = range;
|
static void printspace(vmmspace_t *space);
|
||||||
range->next = newrange;
|
static void insertrange(vmmspace_t *space, vmmrange_t *new_range) {
|
||||||
goto fragcheck;
|
rbtree_insert(&space->ranges, &new_range->rbtree_node, rbtree_compare);
|
||||||
}
|
|
||||||
range = range->next;
|
|
||||||
}
|
|
||||||
|
|
||||||
// space after the second range
|
rbtree_t *successor = rbtree_successor(&new_range->rbtree_node);
|
||||||
range->next = newrange;
|
rbtree_t *predecessor = rbtree_predecessor(&new_range->rbtree_node);
|
||||||
newrange->prev = range;
|
|
||||||
newrange->next = NULL;
|
|
||||||
|
|
||||||
fragcheck:
|
vmmrange_t *next_range = successor ? container_of(successor, vmmrange_t, rbtree_node) : NULL;
|
||||||
// join new range and the next
|
vmmrange_t *prev_range = predecessor ? container_of(predecessor, vmmrange_t, rbtree_node) : NULL;
|
||||||
if (newrange->next && newrange->next->start == newrangetop && newrange->flags == newrange->next->flags && newrange->mmuflags == newrange->next->mmuflags
|
|
||||||
&& ((newrange->flags & VMM_FLAGS_FILE) == 0 || (newrange->vnode == newrange->next->vnode && newrange->offset + newrange->size == newrange->next->offset))) {
|
|
||||||
vmmrange_t *oldrange = newrange->next;
|
|
||||||
newrange->size += oldrange->size;
|
|
||||||
newrange->next = oldrange->next;
|
|
||||||
if (oldrange->next)
|
|
||||||
oldrange->next->prev = newrange;
|
|
||||||
|
|
||||||
freerange(oldrange);
|
// fragmentation checking
|
||||||
if (newrange->flags & VMM_FLAGS_FILE) {
|
|
||||||
VOP_RELEASE(newrange->vnode);
|
// check next range
|
||||||
|
if (next_range && compatible(new_range, next_range)) {
|
||||||
|
new_range->size += next_range->size;
|
||||||
|
|
||||||
|
rbtree_remove(&space->ranges, &next_range->rbtree_node);
|
||||||
|
freerange(next_range);
|
||||||
|
if (new_range->flags & VMM_FLAGS_FILE) {
|
||||||
|
VOP_RELEASE(new_range->vnode);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// join new range and the previous
|
// check prev range
|
||||||
if (newrange->prev && RANGE_TOP(newrange->prev) == newrange->start && newrange->flags == newrange->prev->flags && newrange->mmuflags == newrange->prev->mmuflags
|
if (prev_range && compatible(prev_range, new_range)) {
|
||||||
&& ((newrange->flags & VMM_FLAGS_FILE) == 0 || (newrange->vnode == newrange->prev->vnode && newrange->prev->offset + newrange->prev->size == newrange->offset))) {
|
prev_range->size += new_range->size;
|
||||||
vmmrange_t *oldrange = newrange->prev;
|
|
||||||
oldrange->size += newrange->size;
|
|
||||||
oldrange->next = newrange->next;
|
|
||||||
|
|
||||||
if (newrange->next)
|
rbtree_remove(&space->ranges, &new_range->rbtree_node);
|
||||||
newrange->next->prev = oldrange;
|
freerange(new_range);
|
||||||
|
if (prev_range->flags & VMM_FLAGS_FILE) {
|
||||||
freerange(newrange);
|
VOP_RELEASE(prev_range->vnode);
|
||||||
if (oldrange->flags & VMM_FLAGS_FILE) {
|
|
||||||
VOP_RELEASE(oldrange->vnode);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static void destroyrange(vmmrange_t *range, uintmax_t _offset, size_t size, int flags) {
|
static void destroyrange(vmmrange_t *range, uintmax_t _offset, size_t size, int flags) {
|
||||||
|
// TODO find first
|
||||||
uintmax_t top = _offset + size;
|
uintmax_t top = _offset + size;
|
||||||
|
|
||||||
for (uintmax_t offset = _offset; offset < top; offset += PAGE_SIZE) {
|
for (uintmax_t offset = _offset; offset < top; offset += PAGE_SIZE) {
|
||||||
@@ -307,185 +303,215 @@ static inline bool canwritevnode(vmmrange_t *range) {
|
|||||||
return error == 0;
|
return error == 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int changemap(vmmspace_t *space, void *address, size_t size, bool free, int flags, mmuflags_t newmmuflags) {
|
static int changemap(vmmspace_t *space, void *address, size_t size, bool free, int flags, mmuflags_t new_mmuflags) {
|
||||||
void *top = (void *)((uintptr_t)address + size);
|
void *top = (void *)((uintptr_t)address + size);
|
||||||
vmmrange_t *range = space->ranges;
|
vmmrange_t *new_range = NULL;
|
||||||
vmmrange_t *newrange = NULL;
|
|
||||||
|
if (space->ranges == NULL)
|
||||||
|
return 0;
|
||||||
|
|
||||||
// allocated here and as soon as its used to make sure that
|
// allocated here and as soon as its used to make sure that
|
||||||
// even in an allocation failure there will always be a valid mapping
|
// even in an allocation failure there will always be a valid mapping
|
||||||
if (free == false) {
|
if (free == false) {
|
||||||
newrange = allocrange();
|
new_range = allocrange();
|
||||||
if (newrange == NULL)
|
if (new_range == NULL)
|
||||||
return ENOMEM;
|
return ENOMEM;
|
||||||
}
|
}
|
||||||
|
|
||||||
int error = 0;
|
int error = 0;
|
||||||
|
|
||||||
while (range && range->start < top) {
|
// get first range after address
|
||||||
__assert(range != space->ranges || range->prev == NULL);
|
rbtree_t *rbtree = rbtree_find_first_larger_equal(space->ranges, address, rbtree_value_compare);
|
||||||
void *rangetop = RANGE_TOP(range);
|
vmmrange_t *range = container_of(rbtree, vmmrange_t, rbtree_node);
|
||||||
|
|
||||||
vmmrange_t *nextsave = range->next;
|
if (rbtree == NULL)
|
||||||
if (range->start >= address && rangetop <= top) {
|
goto leave;
|
||||||
// completely changed
|
|
||||||
if (free) {
|
|
||||||
if (range->prev)
|
|
||||||
range->prev->next = range->next;
|
|
||||||
else
|
|
||||||
space->ranges = range->next;
|
|
||||||
|
|
||||||
if (range->next)
|
// check if we actually start at the predecessor
|
||||||
range->next->prev = range->prev;
|
rbtree_t *predecessor = rbtree_predecessor(rbtree);
|
||||||
|
if (predecessor) {
|
||||||
|
vmmrange_t *predecessor_range = container_of(predecessor, vmmrange_t, rbtree_node);
|
||||||
|
if (RANGE_TOP(predecessor_range) > address) {
|
||||||
|
rbtree = predecessor;
|
||||||
|
range = predecessor_range;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
destroyrange(range, 0, range->size, 0);
|
// rbtree now has the first vmm range to be unmapped/remapped
|
||||||
freerange(range);
|
|
||||||
} else {
|
|
||||||
if ((flags & VMM_FLAGS_CREDCHECK) && (range->flags & VMM_FLAGS_SHARED) &&
|
|
||||||
(range->flags & VMM_FLAGS_FILE) && canwritevnode(range) == false) {
|
|
||||||
error = EACCES;
|
|
||||||
goto leave;
|
|
||||||
}
|
|
||||||
|
|
||||||
changemmurange(range, range->start, range->size, newmmuflags);
|
// is the range we want to change in the middle of a mapping?
|
||||||
range->mmuflags = newmmuflags;
|
if (address > range->start && top < RANGE_TOP(range)) {
|
||||||
}
|
// split the mapping
|
||||||
} else if (address > range->start && top < rangetop) {
|
|
||||||
// split (entire change was within a single range)
|
// check for write permission if changing a shared file mapping
|
||||||
if (free == false && (flags & VMM_FLAGS_CREDCHECK) && (range->flags & VMM_FLAGS_SHARED) &&
|
if (free == false && (flags & VMM_FLAGS_CREDCHECK) && (range->flags & VMM_FLAGS_SHARED) &&
|
||||||
(range->flags & VMM_FLAGS_FILE) && canwritevnode(range) == false) {
|
(range->flags & VMM_FLAGS_FILE) && canwritevnode(range) == false) {
|
||||||
error = EACCES;
|
error = EACCES;
|
||||||
goto leave;
|
goto leave;
|
||||||
|
}
|
||||||
|
|
||||||
|
vmmrange_t *new = allocrange();
|
||||||
|
if (range == NULL) {
|
||||||
|
error = ENOMEM;
|
||||||
|
goto leave;
|
||||||
|
}
|
||||||
|
|
||||||
|
*new = *range; // copy most metadata
|
||||||
|
|
||||||
|
if (free) {
|
||||||
|
// release page data
|
||||||
|
destroyrange(range, (uintptr_t)address - (uintptr_t)range->start, size, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
// set up ranges
|
||||||
|
new->start = top;
|
||||||
|
new->size = (uintptr_t)RANGE_TOP(range) - (uintptr_t)new->start;
|
||||||
|
range->size = (uintptr_t)address - (uintptr_t)range->start;
|
||||||
|
|
||||||
|
if (range->flags & VMM_FLAGS_FILE) {
|
||||||
|
VOP_HOLD(range->vnode);
|
||||||
|
new->offset += range->size + size;
|
||||||
|
}
|
||||||
|
|
||||||
|
insertrange(space, new);
|
||||||
|
|
||||||
|
// if we are not just freeing memory, insert a new range
|
||||||
|
if (free == false) {
|
||||||
|
new_range->start = address;
|
||||||
|
new_range->size = size;
|
||||||
|
new_range->flags = range->flags;
|
||||||
|
new_range->mmuflags = new_mmuflags;
|
||||||
|
|
||||||
|
changemmurange(range, new_range->start, new_range->size, new_range->mmuflags);
|
||||||
|
|
||||||
|
if (range->flags & VMM_FLAGS_FILE) {
|
||||||
|
new_range->vnode = range->vnode;
|
||||||
|
new_range->offset = range->offset + range->size;
|
||||||
|
VOP_HOLD(range->vnode);
|
||||||
}
|
}
|
||||||
|
|
||||||
vmmrange_t *new = allocrange();
|
insertrange(space, new_range);
|
||||||
if (range == NULL) {
|
return 0; // dont free the new range
|
||||||
|
}
|
||||||
|
|
||||||
|
goto leave;
|
||||||
|
}
|
||||||
|
|
||||||
|
// are we starting from the end of a mapping?
|
||||||
|
if (address > range->start) {
|
||||||
|
// shrink the mapping
|
||||||
|
|
||||||
|
// permission checking
|
||||||
|
if (free == false && (flags & VMM_FLAGS_CREDCHECK) && (range->flags & VMM_FLAGS_SHARED) &&
|
||||||
|
(range->flags & VMM_FLAGS_FILE) && canwritevnode(range) == false) {
|
||||||
|
error = EACCES;
|
||||||
|
goto leave;
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t difference = (uintptr_t)RANGE_TOP(range) - (uintptr_t)address;
|
||||||
|
range->size -= difference;
|
||||||
|
|
||||||
|
rbtree = rbtree_successor(rbtree); // in case we insert a new mapping, get the next successor here already
|
||||||
|
|
||||||
|
if (free) {
|
||||||
|
destroyrange(range, range->size, difference, 0);
|
||||||
|
} else {
|
||||||
|
// create a new range for the changes
|
||||||
|
new_range->start = (void *)((uintptr_t)range->start + range->size);
|
||||||
|
new_range->size = difference;
|
||||||
|
new_range->flags = range->flags;
|
||||||
|
new_range->mmuflags = new_mmuflags;
|
||||||
|
|
||||||
|
changemmurange(range, new_range->start, new_range->size, new_range->mmuflags);
|
||||||
|
|
||||||
|
if (range->flags & VMM_FLAGS_FILE) {
|
||||||
|
new_range->vnode = range->vnode;
|
||||||
|
new_range->offset = range->offset + range->size;
|
||||||
|
VOP_HOLD(range->vnode);
|
||||||
|
}
|
||||||
|
|
||||||
|
insertrange(space, new_range);
|
||||||
|
new_range = allocrange();
|
||||||
|
if (new_range == NULL) {
|
||||||
error = ENOMEM;
|
error = ENOMEM;
|
||||||
goto leave;
|
goto leave;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
*new = *range;
|
// mappings that lie entirely in the range
|
||||||
|
for (;;) {
|
||||||
|
if (rbtree == NULL)
|
||||||
|
goto leave;
|
||||||
|
|
||||||
if (free) {
|
range = container_of(rbtree, vmmrange_t, rbtree_node);
|
||||||
destroyrange(range, (uintptr_t)address - (uintptr_t)range->start, size, 0);
|
if (RANGE_TOP(range) > top)
|
||||||
}
|
break;
|
||||||
|
|
||||||
new->start = top;
|
rbtree = rbtree_successor(rbtree);
|
||||||
new->size = (uintptr_t)rangetop - (uintptr_t)new->start;
|
|
||||||
range->size = (uintptr_t)address - (uintptr_t)range->start;
|
|
||||||
|
|
||||||
if (range->next)
|
// remove or change the flags of the range
|
||||||
range->next->prev = new;
|
if (free) {
|
||||||
|
rbtree_remove(&space->ranges, &range->rbtree_node);
|
||||||
// new->next is set by the copy in *new = *range
|
destroyrange(range, 0, range->size, 0);
|
||||||
new->prev = range;
|
freerange(range);
|
||||||
range->next = new;
|
} else {
|
||||||
|
// permission check
|
||||||
if (range->flags & VMM_FLAGS_FILE) {
|
if ((flags & VMM_FLAGS_CREDCHECK) && (range->flags & VMM_FLAGS_SHARED) &&
|
||||||
VOP_HOLD(range->vnode);
|
|
||||||
new->offset += range->size + size;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (free == false) {
|
|
||||||
newrange->start = address;
|
|
||||||
newrange->size = size;
|
|
||||||
newrange->flags = range->flags;
|
|
||||||
newrange->mmuflags = newmmuflags;
|
|
||||||
|
|
||||||
changemmurange(range, newrange->start, newrange->size, newrange->mmuflags);
|
|
||||||
|
|
||||||
if (range->flags & VMM_FLAGS_FILE) {
|
|
||||||
newrange->vnode = range->vnode;
|
|
||||||
newrange->offset = range->offset;
|
|
||||||
VOP_HOLD(range->vnode);
|
|
||||||
}
|
|
||||||
|
|
||||||
insertrange(space, newrange);
|
|
||||||
newrange = allocrange();
|
|
||||||
if (newrange == NULL) {
|
|
||||||
error = ENOMEM;
|
|
||||||
goto leave;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else if (top > range->start && range->start >= address) {
|
|
||||||
// partially change from start (end of change was within this range)
|
|
||||||
if (free == false && (flags & VMM_FLAGS_CREDCHECK) && (range->flags & VMM_FLAGS_SHARED) &&
|
|
||||||
(range->flags & VMM_FLAGS_FILE) && canwritevnode(range) == false) {
|
(range->flags & VMM_FLAGS_FILE) && canwritevnode(range) == false) {
|
||||||
error = EACCES;
|
error = EACCES;
|
||||||
goto leave;
|
goto leave;
|
||||||
}
|
}
|
||||||
|
|
||||||
size_t difference = (uintptr_t)top - (uintptr_t)range->start;
|
changemmurange(range, range->start, range->size, new_mmuflags);
|
||||||
if (free) {
|
range->mmuflags = new_mmuflags;
|
||||||
destroyrange(range, 0, difference, 0);
|
}
|
||||||
}
|
}
|
||||||
range->start = (void *)((uintptr_t)range->start + difference);
|
|
||||||
range->size -= difference;
|
|
||||||
|
|
||||||
if (range->flags & VMM_FLAGS_FILE)
|
// start of mapping is in the range
|
||||||
range->offset += difference;
|
if (range->start < top) {
|
||||||
|
// move the start of the range
|
||||||
|
|
||||||
if (free == false) {
|
// permission check
|
||||||
newrange->start = (void *)((uintptr_t)range->start - difference);
|
if (free == false && (flags & VMM_FLAGS_CREDCHECK) && (range->flags & VMM_FLAGS_SHARED) &&
|
||||||
newrange->size = difference;
|
(range->flags & VMM_FLAGS_FILE) && canwritevnode(range) == false) {
|
||||||
newrange->flags = range->flags;
|
error = EACCES;
|
||||||
newrange->mmuflags = newmmuflags;
|
goto leave;
|
||||||
|
|
||||||
changemmurange(range, newrange->start, newrange->size, newrange->mmuflags);
|
|
||||||
|
|
||||||
if (range->flags & VMM_FLAGS_FILE) {
|
|
||||||
newrange->vnode = range->vnode;
|
|
||||||
newrange->offset = range->offset - difference;
|
|
||||||
VOP_HOLD(range->vnode);
|
|
||||||
}
|
|
||||||
|
|
||||||
insertrange(space, newrange);
|
|
||||||
newrange = allocrange();
|
|
||||||
if (newrange == NULL) {
|
|
||||||
error = ENOMEM;
|
|
||||||
goto leave;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else if (address < rangetop && rangetop <= top) {
|
|
||||||
// partially change from end (start of change was within this range)
|
|
||||||
if (free == false && (flags & VMM_FLAGS_CREDCHECK) && (range->flags & VMM_FLAGS_SHARED) &&
|
|
||||||
(range->flags & VMM_FLAGS_FILE) && canwritevnode(range) == false) {
|
|
||||||
error = EACCES;
|
|
||||||
goto leave;
|
|
||||||
}
|
|
||||||
|
|
||||||
size_t difference = (uintptr_t)rangetop - (uintptr_t)address;
|
|
||||||
range->size -= difference;
|
|
||||||
if (free) {
|
|
||||||
destroyrange(range, range->size, difference, 0);
|
|
||||||
} else {
|
|
||||||
newrange->start = (void *)((uintptr_t)range->start + range->size);
|
|
||||||
newrange->size = difference;
|
|
||||||
newrange->flags = range->flags;
|
|
||||||
newrange->mmuflags = newmmuflags;
|
|
||||||
|
|
||||||
changemmurange(range, newrange->start, newrange->size, newrange->mmuflags);
|
|
||||||
|
|
||||||
if (range->flags & VMM_FLAGS_FILE) {
|
|
||||||
newrange->vnode = range->vnode;
|
|
||||||
newrange->offset = range->offset + range->size;
|
|
||||||
VOP_HOLD(range->vnode);
|
|
||||||
}
|
|
||||||
|
|
||||||
insertrange(space, newrange);
|
|
||||||
newrange = allocrange();
|
|
||||||
if (newrange == NULL) {
|
|
||||||
error = ENOMEM;
|
|
||||||
goto leave;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
range = nextsave;
|
size_t difference = (uintptr_t)top - (uintptr_t)range->start;
|
||||||
|
if (free)
|
||||||
|
destroyrange(range, 0, difference, 0);
|
||||||
|
|
||||||
|
range->start = (void *)((uintptr_t)range->start + difference);
|
||||||
|
range->size -= difference;
|
||||||
|
|
||||||
|
if (range->flags & VMM_FLAGS_FILE)
|
||||||
|
range->offset += difference;
|
||||||
|
|
||||||
|
if (free == false) {
|
||||||
|
// insert range at the start
|
||||||
|
new_range->start = (void *)((uintptr_t)range->start - difference);
|
||||||
|
new_range->size = difference;
|
||||||
|
new_range->flags = range->flags;
|
||||||
|
new_range->mmuflags = new_mmuflags;
|
||||||
|
|
||||||
|
changemmurange(range, new_range->start, new_range->size, new_range->mmuflags);
|
||||||
|
|
||||||
|
if (range->flags & VMM_FLAGS_FILE) {
|
||||||
|
new_range->vnode = range->vnode;
|
||||||
|
new_range->offset = range->offset - difference;
|
||||||
|
VOP_HOLD(range->vnode);
|
||||||
|
}
|
||||||
|
|
||||||
|
insertrange(space, new_range);
|
||||||
|
return 0; // don't free the range, just leave
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
leave:
|
leave:
|
||||||
if (free == false)
|
if (free == false)
|
||||||
freerange(newrange);
|
freerange(new_range);
|
||||||
|
|
||||||
return error;
|
return error;
|
||||||
}
|
}
|
||||||
@@ -519,10 +545,11 @@ int vmm_changemmuflags(void *base, size_t size, mmuflags_t mmuflags, int flags)
|
|||||||
|
|
||||||
static void printspace(vmmspace_t *space) {
|
static void printspace(vmmspace_t *space) {
|
||||||
printf("vmm: ranges:\n");
|
printf("vmm: ranges:\n");
|
||||||
vmmrange_t *range = space->ranges;
|
rbtree_t *rbtree = rbtree_first(space->ranges);
|
||||||
while (range) {
|
while (rbtree) {
|
||||||
|
vmmrange_t *range = container_of(rbtree, vmmrange_t, rbtree_node);
|
||||||
printf("vmm: address %p size %lx flags %x\n", range->start, range->size, range->flags);
|
printf("vmm: address %p size %lx flags %x\n", range->start, range->size, range->flags);
|
||||||
range = range->next;
|
rbtree = rbtree_successor(rbtree);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -873,9 +900,9 @@ vmmcontext_t *vmm_fork(vmmcontext_t *oldcontext) {
|
|||||||
|
|
||||||
MUTEX_ACQUIRE(&oldcontext->space.lock);
|
MUTEX_ACQUIRE(&oldcontext->space.lock);
|
||||||
|
|
||||||
vmmrange_t *range = oldcontext->space.ranges;
|
rbtree_t *rbtree = oldcontext->space.ranges ? rbtree_first(oldcontext->space.ranges) : NULL;
|
||||||
|
while (rbtree) {
|
||||||
while (range) {
|
vmmrange_t *range = container_of(rbtree, vmmrange_t, rbtree_node);
|
||||||
vmmrange_t *newrange = allocrange();
|
vmmrange_t *newrange = allocrange();
|
||||||
if (newrange == NULL)
|
if (newrange == NULL)
|
||||||
goto error;
|
goto error;
|
||||||
@@ -903,7 +930,7 @@ vmmcontext_t *vmm_fork(vmmcontext_t *oldcontext) {
|
|||||||
arch_mmu_remap(oldcontext->pagetable, phys, vaddr, newrange->mmuflags & ~ARCH_MMU_FLAGS_WRITE);
|
arch_mmu_remap(oldcontext->pagetable, phys, vaddr, newrange->mmuflags & ~ARCH_MMU_FLAGS_WRITE);
|
||||||
}
|
}
|
||||||
|
|
||||||
range = range->next;
|
rbtree = rbtree_successor(rbtree);
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO do only userspace invalidation as to not send ipi to all cores
|
// TODO do only userspace invalidation as to not send ipi to all cores
|
||||||
|
|||||||
Reference in New Issue
Block a user