Compare commits

...

2 Commits

3 changed files with 361 additions and 358 deletions

View File

@@ -7,7 +7,7 @@
#define VM_REGION_NAME_MAX 64 #define VM_REGION_NAME_MAX 64
#define VM_REGION_ANY_MAP_ADDRESS ((virt_addr_t) - 1) #define VM_REGION_ANY_OFFSET ((off_t) - 1)
struct vm_region; struct vm_region;
struct vm_object; struct vm_object;
@@ -22,8 +22,8 @@ struct vm_region_entry {
struct btree_node e_node; struct btree_node e_node;
struct vm_region_entry *e_parent; struct vm_region_entry *e_parent;
enum vm_region_entry_type e_type; enum vm_region_entry_type e_type;
/* absolute virtual address of the entry */ /* offset in bytes of this entry within its immediate parent. */
virt_addr_t e_base_address; off_t e_offset;
/* size of the entry in bytes */ /* size of the entry in bytes */
size_t e_size; size_t e_size;
}; };
@@ -66,59 +66,48 @@ struct vm_region {
extern kern_status_t vm_region_type_init(void); extern kern_status_t vm_region_type_init(void);
/* create a new vm-region, optionally within a parent region.
* `offset` is the byte offset within the parent region where the new region
* should start.
* if no parent is specified, `offset` is the absolute virtual address of the
* start of the region.
* in both cases, `len` is the length of the new region in bytes. */
extern kern_status_t vm_region_create( extern kern_status_t vm_region_create(
struct vm_region *parent, struct vm_region *parent,
const char *name, const char *name,
virt_addr_t base, off_t offset,
size_t len, size_t len,
enum vm_prot prot, enum vm_prot prot,
struct vm_region **out); struct vm_region **out);
/* find the child region that has jurisdiction over the specified virtual /* map a vm-object into a vm-region.
* address. returns the lowest-nested region that covers the specified virtual * [region_offset,length] must fall within exactly one region, and cannot span
* address. */ * multiple sibling regions.
extern struct vm_region *vm_region_find_child( * if [region_offset,length] falls within a child region, the map operation
struct vm_region *region, * will be transparently redirected to the relevant region.
virt_addr_t addr); * `prot` must be allowed both by the region into which the mapping is being
* created AND the vm-object being mapped. */
/* find the child region that has jurisdiction over the specified virtual
* address area. returns the lowest-nested region that covers the specified
* virtual address area. the area must be fully contained within a region, with
* no partial overlaps. if an area is covered by multiple regions, or is only
* partially within a region, returns NULL. */
extern struct vm_region *vm_region_find_child_for_area(
struct vm_region *region,
virt_addr_t addr,
size_t len);
extern struct vm_region_mapping *vm_region_find_mapping(
struct vm_region *region,
virt_addr_t addr);
extern kern_status_t vm_region_map_object( extern kern_status_t vm_region_map_object(
struct vm_region *region, struct vm_region *region,
virt_addr_t map_address, off_t region_offset,
struct vm_object *object, struct vm_object *object,
off_t object_offset, off_t object_offset,
size_t length, size_t length,
enum vm_prot prot, enum vm_prot prot,
virt_addr_t *out); virt_addr_t *out);
/* returns true if the memory area defined by [base, base+len] contains: /* find the mapping corresponding to the given virtual address, and page-in the
* - no child regions * necessary vm_page to allow the memory access to succeed. if the relevant
* - no vm_object mappings * vm-object page hasn't been allocated yet, it will be allocated here. */
* if any child regions or mappings exist in the memory area, returns false.
* if the memory area exceeds the bounds of the region, returns false.
*/
extern bool vm_region_is_area_free(
const struct vm_region *region,
virt_addr_t base,
size_t len);
extern kern_status_t vm_region_demand_map( extern kern_status_t vm_region_demand_map(
struct vm_region *region, struct vm_region *region,
virt_addr_t addr, virt_addr_t addr,
enum pmap_fault_flags flags); enum pmap_fault_flags flags);
/* get the absolute base virtual address of a region within its
* parent/ancestors. */
extern virt_addr_t vm_region_get_base_address(const struct vm_region *region);
extern void vm_region_dump(struct vm_region *region, int depth); extern void vm_region_dump(struct vm_region *region, int depth);
DEFINE_OBJECT_LOCK_FUNCTION(vm_region, vr_base) DEFINE_OBJECT_LOCK_FUNCTION(vm_region, vr_base)

View File

@@ -86,7 +86,7 @@ static kern_status_t map_executable(
status = vm_region_create( status = vm_region_create(
task->t_address_space, task->t_address_space,
"exec", "exec",
VM_REGION_ANY_MAP_ADDRESS, VM_REGION_ANY_OFFSET,
exec_size, exec_size,
VM_PROT_READ | VM_PROT_WRITE | VM_PROT_EXEC | VM_PROT_USER, VM_PROT_READ | VM_PROT_WRITE | VM_PROT_EXEC | VM_PROT_USER,
&region); &region);
@@ -103,25 +103,26 @@ static kern_status_t map_executable(
return KERN_NO_MEMORY; return KERN_NO_MEMORY;
} }
off_t text_offset = bsp->bsp_trailer.bsp_exec_offset virt_addr_t text_base = 0, data_base = 0;
+ bsp->bsp_trailer.bsp_text_faddr;
off_t data_offset = 0;
virt_addr_t text_base = region->vr_entry.e_base_address
+ bsp->bsp_trailer.bsp_text_vaddr;
virt_addr_t data_base = region->vr_entry.e_base_address
+ bsp->bsp_trailer.bsp_data_vaddr;
tracek("exec_offset=%llx, text_faddr=%llx", off_t text_foffset = bsp->bsp_trailer.bsp_exec_offset
bsp->bsp_trailer.bsp_exec_offset, + bsp->bsp_trailer.bsp_text_faddr;
bsp->bsp_trailer.bsp_text_faddr); off_t data_foffset = 0;
tracek("text_offset=%llx, data_offset=%llx", text_offset, data_offset); off_t text_voffset = bsp->bsp_trailer.bsp_text_vaddr;
tracek("text_base=%llx, data_base=%llx", text_base, data_base); off_t data_voffset = bsp->bsp_trailer.bsp_data_vaddr;
tracek("text_foffset=%06llx, data_foffset=%06llx",
text_foffset,
data_foffset);
tracek("text_voffset=%08llx, data_voffset=%08llx",
text_voffset,
data_voffset);
status = vm_region_map_object( status = vm_region_map_object(
region, region,
text_base, text_voffset,
bsp->bsp_vmo, bsp->bsp_vmo,
text_offset, text_foffset,
bsp->bsp_trailer.bsp_text_size, bsp->bsp_trailer.bsp_text_size,
VM_PROT_READ | VM_PROT_EXEC | VM_PROT_USER, VM_PROT_READ | VM_PROT_EXEC | VM_PROT_USER,
&text_base); &text_base);
@@ -131,9 +132,9 @@ static kern_status_t map_executable(
status = vm_region_map_object( status = vm_region_map_object(
region, region,
data_base, data_voffset,
data, data,
data_offset, data_foffset,
bsp->bsp_trailer.bsp_data_size, bsp->bsp_trailer.bsp_data_size,
VM_PROT_READ | VM_PROT_WRITE | VM_PROT_USER, VM_PROT_READ | VM_PROT_WRITE | VM_PROT_USER,
&data_base); &data_base);
@@ -141,6 +142,8 @@ static kern_status_t map_executable(
return status; return status;
} }
tracek("text_base=%08llx, data_base=%08llx", text_base, data_base);
*entry = text_base + bsp->bsp_trailer.bsp_exec_entry; *entry = text_base + bsp->bsp_trailer.bsp_exec_entry;
return KERN_OK; return KERN_OK;
} }
@@ -164,7 +167,7 @@ kern_status_t bsp_launch_async(struct bsp *bsp, struct task *task)
status = vm_region_map_object( status = vm_region_map_object(
task->t_address_space, task->t_address_space,
VM_REGION_ANY_MAP_ADDRESS, VM_REGION_ANY_OFFSET,
user_stack, user_stack,
0, 0,
BOOTSTRAP_STACK_SIZE, BOOTSTRAP_STACK_SIZE,

View File

@@ -7,8 +7,20 @@
#include <mango/vm-object.h> #include <mango/vm-object.h>
#include <mango/vm-region.h> #include <mango/vm-region.h>
/*** STATIC DATA + MACROS *****************************************************/
#undef ASLR #undef ASLR
#define INVALID_OFFSET ((off_t) - 1)
#ifdef ASLR
#define region_find_free_area(region, length) \
region_find_free_area_random(region, length)
#else
#define region_find_free_area(region, length) \
region_find_free_area_linear(region, length)
#endif
enum search_direction { enum search_direction {
SEARCH_LEFT, SEARCH_LEFT,
SEARCH_RIGHT, SEARCH_RIGHT,
@@ -28,24 +40,41 @@ static struct vm_cache mapping_cache = {
.c_obj_size = sizeof(struct vm_region_mapping), .c_obj_size = sizeof(struct vm_region_mapping),
}; };
struct entry_pair { /*** INTERNAL UTILITY FUNCTION ************************************************/
struct vm_region_entry *p_left, *p_right;
};
kern_status_t vm_region_type_init(void) static struct vm_region *region_from_entry(struct vm_region_entry *entry)
{ {
vm_cache_init(&mapping_cache); if (!entry || entry->e_type != VM_REGION_ENTRY_REGION) {
return object_type_register(&vm_region_type); return NULL;
}
return BTREE_CONTAINER(struct vm_region, vr_entry, entry);
} }
static virt_addr_t find_free_area_linear( static struct vm_region_mapping *mapping_from_entry(
struct vm_region *region, struct vm_region_entry *entry)
size_t target_length); {
static virt_addr_t find_free_area_random( if (!entry || entry->e_type != VM_REGION_ENTRY_MAPPING) {
struct vm_region *region, return NULL;
size_t target_length); }
static void put_entry(struct vm_region *parent, struct vm_region_entry *child) return BTREE_CONTAINER(struct vm_region_mapping, m_entry, entry);
}
static virt_addr_t entry_absolute_address(struct vm_region_entry *entry)
{
virt_addr_t result = 0;
while (entry) {
result += entry->e_offset;
entry = entry->e_parent;
}
return result;
}
static void region_put_entry(
struct vm_region *parent,
struct vm_region_entry *child)
{ {
struct btree_node *cur = parent->vr_entries.b_root; struct btree_node *cur = parent->vr_entries.b_root;
if (!cur) { if (!cur) {
@@ -54,16 +83,16 @@ static void put_entry(struct vm_region *parent, struct vm_region_entry *child)
return; return;
} }
virt_addr_t child_base = child->e_base_address; off_t child_base = child->e_offset;
virt_addr_t child_limit = child_base + child->e_size - 1; off_t child_limit = child_base + child->e_size - 1;
while (cur) { while (cur) {
struct vm_region_entry *cur_entry struct vm_region_entry *cur_entry
= BTREE_CONTAINER(struct vm_region_entry, e_node, cur); = BTREE_CONTAINER(struct vm_region_entry, e_node, cur);
struct btree_node *next = NULL; struct btree_node *next = NULL;
virt_addr_t cur_base = cur_entry->e_base_address; off_t cur_base = cur_entry->e_offset;
virt_addr_t cur_limit = cur_base + cur_entry->e_size - 1; off_t cur_limit = cur_base + cur_entry->e_size - 1;
if (child_limit < cur_base) { if (child_limit < cur_base) {
next = btree_left(cur); next = btree_left(cur);
@@ -89,95 +118,12 @@ static void put_entry(struct vm_region *parent, struct vm_region_entry *child)
} }
} }
static struct vm_region *vm_region_from_entry(struct vm_region_entry *entry) /* find the child entry that covers the specified offset.
{ * DOES NOT search recursively! */
if (entry->e_type != VM_REGION_ENTRY_REGION) { static struct vm_region_entry *region_get_entry(
return NULL;
}
return BTREE_CONTAINER(struct vm_region, vr_entry, entry);
}
static struct vm_region_mapping *vm_region_mapping_from_entry(
struct vm_region_entry *entry)
{
if (entry->e_type != VM_REGION_ENTRY_MAPPING) {
return NULL;
}
return BTREE_CONTAINER(struct vm_region_mapping, m_entry, entry);
}
kern_status_t vm_region_create(
struct vm_region *parent,
const char *name,
virt_addr_t base,
size_t len,
enum vm_prot prot,
struct vm_region **out)
{
if (!base || !len) {
return KERN_INVALID_ARGUMENT;
}
if (len & VM_PAGE_MASK) {
len &= ~VM_PAGE_MASK;
len += VM_PAGE_SIZE;
}
if (parent) {
if ((prot & parent->vr_prot) != prot) {
/* child region protection must match or be a
* subset of parent region protection */
return KERN_INVALID_ARGUMENT;
}
if (base == VM_REGION_ANY_MAP_ADDRESS) {
#ifdef ASLR
map_address = find_free_area_random(region, length);
#else
base = find_free_area_linear(parent, len);
#endif
base &= ~VM_PAGE_MASK;
if (base == 0) {
return KERN_NO_MEMORY;
}
} else if (!vm_region_is_area_free(parent, base, len)) {
return KERN_INVALID_ARGUMENT;
}
}
struct object *region_object = object_create(&vm_region_type);
if (!region_object) {
return KERN_NO_MEMORY;
}
struct vm_region *region = VM_REGION_CAST(region_object);
region->vr_prot = prot;
region->vr_entry.e_type = VM_REGION_ENTRY_REGION;
region->vr_entry.e_base_address = base;
region->vr_entry.e_size = len;
if (parent) {
region->vr_entry.e_parent = &parent->vr_entry;
region->vr_pmap = parent->vr_pmap;
put_entry(parent, &region->vr_entry);
}
if (name) {
strncpy(region->vr_name, name, sizeof region->vr_name);
region->vr_name[sizeof region->vr_name - 1] = '\0';
}
*out = region;
return KERN_OK;
}
static struct vm_region_entry *vm_region_find_entry(
struct vm_region *region, struct vm_region *region,
virt_addr_t addr) off_t offset,
size_t len)
{ {
struct btree_node *cur = region->vr_entries.b_root; struct btree_node *cur = region->vr_entries.b_root;
if (!cur) { if (!cur) {
@@ -186,17 +132,19 @@ static struct vm_region_entry *vm_region_find_entry(
struct vm_region_entry *result = NULL; struct vm_region_entry *result = NULL;
off_t base = offset, limit = offset + len - 1;
while (cur) { while (cur) {
struct vm_region_entry *child struct vm_region_entry *child
= BTREE_CONTAINER(struct vm_region_entry, e_node, cur); = BTREE_CONTAINER(struct vm_region_entry, e_node, cur);
struct btree_node *next = NULL; struct btree_node *next = NULL;
virt_addr_t child_limit off_t child_base = child->e_offset;
= child->e_base_address + child->e_size - 1; off_t child_limit = child->e_offset + child->e_size - 1;
if (addr < child->e_base_address) { if (limit < child_base) {
next = btree_left(cur); next = btree_left(cur);
} else if (addr > child_limit) { } else if (base > child_limit) {
next = btree_right(cur); next = btree_right(cur);
} else { } else {
result = child; result = child;
@@ -209,81 +157,68 @@ static struct vm_region_entry *vm_region_find_entry(
return result; return result;
} }
struct vm_region *vm_region_find_child( /* find the child region that covers the area [*offp,len]. searches recursively
* the value in `offp` is updated to the offset of the returned entry relative
* to its parent */
static struct vm_region *region_get_child_region_recursive(
struct vm_region *region, struct vm_region *region,
virt_addr_t addr) off_t *offp,
{
struct vm_region_entry *result = vm_region_find_entry(region, addr);
if (!result || result->e_type != VM_REGION_ENTRY_REGION) {
return region;
}
return vm_region_from_entry(result);
}
struct vm_region *vm_region_find_child_for_area(
struct vm_region *region,
virt_addr_t base,
size_t len) size_t len)
{ {
virt_addr_t limit = base + len - 1; off_t offset = *offp;
if (offset >= region->vr_entry.e_size) {
while (region) {
struct btree_node *cur = region->vr_entries.b_root;
if (!cur) {
break;
}
bool found_new_region = false;
while (cur) {
struct vm_region_entry *child = BTREE_CONTAINER(
struct vm_region_entry,
e_node,
cur);
struct btree_node *next = NULL;
virt_addr_t child_base = child->e_base_address;
virt_addr_t child_limit
= child_base + child->e_size - 1;
if (limit < child_base) {
next = btree_left(cur);
} else if (base > child_limit) {
next = btree_right(cur);
} else if (base >= child_base && limit <= child_limit) {
region = vm_region_from_entry(child);
found_new_region = true;
break;
} else {
return NULL; return NULL;
} }
cur = next; while (1) {
} struct vm_region_entry *next
= region_get_entry(region, offset, len);
if (!found_new_region) { struct vm_region *next_region = region_from_entry(next);
if (next_region) {
offset -= next->e_offset;
region = next_region;
} else {
break; break;
} }
} }
*offp = offset;
return region; return region;
} }
struct vm_region_mapping *vm_region_find_mapping( static struct vm_region_mapping *region_get_mapping_recursive(
struct vm_region *region, struct vm_region *region,
virt_addr_t addr) off_t *offp,
size_t len)
{ {
struct vm_region_entry *result = vm_region_find_entry(region, addr); off_t offset = *offp;
region = region_get_child_region_recursive(region, &offset, len);
if (!result) { if (!region) {
return NULL; return NULL;
} }
return vm_region_mapping_from_entry(result); struct vm_region_entry *entry = region_get_entry(region, offset, len);
*offp = offset;
return mapping_from_entry(entry);
} }
static struct vm_region_entry *get_random_child(struct vm_region *region) static off_t generate_random_address(
off_t area_base,
size_t area_length,
size_t target_length)
{
size_t random_range = area_length - target_length;
off_t offset = 0;
fill_random(&offset, sizeof offset);
offset %= random_range;
return area_base + offset;
}
static struct vm_region_entry *region_get_random_entry(struct vm_region *region)
{ {
enum { enum {
STEP_LEFT = 0, STEP_LEFT = 0,
@@ -333,11 +268,12 @@ static struct vm_region_entry *get_random_child(struct vm_region *region)
return BTREE_CONTAINER(struct vm_region_entry, e_node, result); return BTREE_CONTAINER(struct vm_region_entry, e_node, result);
} }
static virt_addr_t find_free_area_linear_ex( static virt_addr_t region_find_free_area_ex(
struct vm_region *region, struct vm_region *region,
size_t target_length, size_t target_length,
struct btree_node *start, struct btree_node *start,
enum search_direction direction) enum search_direction direction,
bool random)
{ {
if (region->vr_entry.e_size < target_length) { if (region->vr_entry.e_size < target_length) {
return 0; return 0;
@@ -374,27 +310,36 @@ static virt_addr_t find_free_area_linear_ex(
/* addresses of the first and last free bytes in the area /* addresses of the first and last free bytes in the area
* respectively. */ * respectively. */
virt_addr_t area_base, area_limit; off_t area_base, area_limit;
if (left && right) { if (left && right) {
area_base = left->e_base_address + left->e_size; area_base = left->e_offset + left->e_size;
area_limit = right->e_base_address - 1; area_limit = right->e_offset - 1;
} else if (right) { } else if (right) {
area_base = region->vr_entry.e_base_address; area_base = region->vr_entry.e_offset;
area_limit = left->e_base_address - 1; area_limit = left->e_offset - 1;
} else if (left) { } else if (left) {
area_base = left->e_base_address + left->e_size; area_base = left->e_offset + left->e_size;
area_limit = region->vr_entry.e_base_address area_limit = region->vr_entry.e_offset
+ region->vr_entry.e_size - 1; + region->vr_entry.e_size - 1;
} else { } else {
return 0; return 0;
} }
area_base &= ~VM_PAGE_MASK;
size_t area_size = 0; size_t area_size = 0;
if (area_limit >= area_base) { if (area_limit >= area_base) {
area_size = area_limit - area_base + 1; area_size = area_limit - area_base + 1;
} }
if (area_size >= target_length) { if (area_size >= target_length) {
if (random) {
area_base = generate_random_address(
area_base,
area_size,
target_length);
area_base &= ~VM_PAGE_MASK;
}
return area_base; return area_base;
} }
@@ -410,81 +355,186 @@ static virt_addr_t find_free_area_linear_ex(
return 0; return 0;
} }
static virt_addr_t find_free_area_linear( static off_t region_find_free_area_linear(
struct vm_region *region, struct vm_region *region,
size_t target_length) size_t target_length)
{ {
if (!region->vr_entries.b_root) { if (!region->vr_entries.b_root) {
return region->vr_entry.e_base_address; return region->vr_entry.e_offset;
} }
return find_free_area_linear_ex( return region_find_free_area_ex(
region, region,
target_length, target_length,
btree_first(&region->vr_entries), btree_first(&region->vr_entries),
SEARCH_RIGHT); SEARCH_RIGHT,
false);
} }
static virt_addr_t random_address( static off_t region_find_free_area_random(
virt_addr_t area_base,
size_t area_length,
size_t target_length)
{
size_t random_range = area_length - target_length;
off_t offset = 0;
fill_random(&offset, sizeof offset);
offset %= random_range;
return area_base + offset;
}
static virt_addr_t find_free_area_random(
struct vm_region *region, struct vm_region *region,
size_t target_length) size_t target_length)
{ {
if (!region->vr_entries.b_root) {
off_t offset = generate_random_address(
0,
region->vr_entry.e_size,
target_length);
return offset & ~VM_PAGE_MASK;
}
int tmp = 0; int tmp = 0;
struct btree_node *node = NULL; struct vm_region_entry *basis = region_get_random_entry(region);
struct vm_region_entry *basis = get_random_child(region);
fill_random(&tmp, sizeof tmp); fill_random(&tmp, sizeof tmp);
enum search_direction direction = tmp % 2; enum search_direction direction = tmp % 2;
struct vm_region_entry *left = NULL, *right = NULL; return region_find_free_area_ex(
if (direction == SEARCH_LEFT) { region,
node = basis ? btree_left(&basis->e_node) : NULL; target_length,
right = basis; &basis->e_node,
left = BTREE_CONTAINER(struct vm_region_entry, e_node, node); direction,
true);
}
static bool region_is_area_free(
const struct vm_region *region,
off_t base,
size_t len)
{
if (len >= region->vr_entry.e_size) {
return false;
}
if (base + len > region->vr_entry.e_size) {
return false;
}
off_t limit = base + len - 1;
struct btree_node *cur = region->vr_entries.b_root;
if (!cur) {
return true;
}
while (cur) {
struct vm_region_entry *entry
= BTREE_CONTAINER(struct vm_region_entry, e_node, cur);
struct btree_node *next = NULL;
off_t entry_limit = entry->e_offset + entry->e_size - 1;
if (base > entry_limit) {
next = btree_right(cur);
} else if (limit < entry->e_offset) {
next = btree_left(cur);
} else { } else {
node = basis ? btree_right(&basis->e_node) : NULL; return false;
left = basis;
right = BTREE_CONTAINER(struct vm_region_entry, e_node, node);
} }
virt_addr_t base = region->vr_entry.e_base_address, cur = next;
limit = base + region->vr_entry.e_size - 1;
if (left) {
base = left->e_base_address;
} }
if (right) { return true;
limit = right->e_base_address + right->e_size - 1; }
static kern_status_t region_validate_allocation(
struct vm_region *parent,
enum vm_prot prot,
off_t *offp,
size_t len)
{
off_t offset = *offp;
if ((prot & parent->vr_prot) != prot) {
/* child region protection must match or be a
* subset of parent region protection */
return KERN_INVALID_ARGUMENT;
} }
return random_address(base, limit - base + 1, target_length); if (offset == VM_REGION_ANY_OFFSET) {
offset = region_find_free_area(parent, len);
if (offset == 0) {
return KERN_NO_MEMORY;
}
} else if (!region_is_area_free(parent, offset, len)) {
return KERN_INVALID_ARGUMENT;
}
*offp = offset;
return KERN_OK;
}
/*** PUBLIC API ***************************************************************/
kern_status_t vm_region_type_init(void)
{
vm_cache_init(&mapping_cache);
return object_type_register(&vm_region_type);
}
kern_status_t vm_region_create(
struct vm_region *parent,
const char *name,
off_t offset,
size_t len,
enum vm_prot prot,
struct vm_region **out)
{
if (!offset || !len) {
return KERN_INVALID_ARGUMENT;
}
if (len & VM_PAGE_MASK) {
len &= ~VM_PAGE_MASK;
len += VM_PAGE_SIZE;
}
kern_status_t status = KERN_OK;
if (parent) {
status = region_validate_allocation(parent, prot, &offset, len);
}
if (status != KERN_OK) {
return status;
}
struct object *region_object = object_create(&vm_region_type);
if (!region_object) {
return KERN_NO_MEMORY;
}
struct vm_region *region = VM_REGION_CAST(region_object);
region->vr_prot = prot;
region->vr_entry.e_type = VM_REGION_ENTRY_REGION;
region->vr_entry.e_offset = offset;
region->vr_entry.e_size = len;
if (parent) {
region->vr_entry.e_parent = &parent->vr_entry;
region->vr_pmap = parent->vr_pmap;
region_put_entry(parent, &region->vr_entry);
}
if (name) {
strncpy(region->vr_name, name, sizeof region->vr_name);
region->vr_name[sizeof region->vr_name - 1] = '\0';
}
*out = region;
return KERN_OK;
} }
kern_status_t vm_region_map_object( kern_status_t vm_region_map_object(
struct vm_region *region, struct vm_region *region,
virt_addr_t map_address, off_t region_offset,
struct vm_object *object, struct vm_object *object,
off_t object_offset, off_t object_offset,
size_t length, size_t length,
enum vm_prot prot, enum vm_prot prot,
virt_addr_t *out) virt_addr_t *out)
{ {
object_offset &= ~VM_PAGE_MASK; object_offset &= ~VM_PAGE_MASK;
if (length & VM_PAGE_MASK) { if (length & VM_PAGE_MASK) {
@@ -508,10 +558,10 @@ kern_status_t vm_region_map_object(
return KERN_INVALID_ARGUMENT; return KERN_INVALID_ARGUMENT;
} }
if (map_address != VM_REGION_ANY_MAP_ADDRESS) { if (region_offset != VM_REGION_ANY_OFFSET) {
region = vm_region_find_child_for_area( region = region_get_child_region_recursive(
region, region,
map_address, &region_offset,
length); length);
} }
@@ -519,18 +569,13 @@ kern_status_t vm_region_map_object(
return KERN_INVALID_ARGUMENT; return KERN_INVALID_ARGUMENT;
} }
if (map_address == VM_REGION_ANY_MAP_ADDRESS) { if (region_offset == VM_REGION_ANY_OFFSET) {
#ifdef ASLR region_offset = region_find_free_area(region, length);
map_address = find_free_area_random(region, length);
#else
map_address = find_free_area_linear(region, length);
#endif
map_address &= ~VM_PAGE_MASK;
if (map_address == 0) { if (region_offset == INVALID_OFFSET) {
return KERN_NO_MEMORY; return KERN_NO_MEMORY;
} }
} else if (!vm_region_is_area_free(region, map_address, length)) { } else if (!region_is_area_free(region, region_offset, length)) {
return KERN_INVALID_ARGUMENT; return KERN_INVALID_ARGUMENT;
} }
@@ -540,93 +585,59 @@ kern_status_t vm_region_map_object(
return KERN_NO_MEMORY; return KERN_NO_MEMORY;
} }
tracek("mapping %s at [%llx-%llx]",
object->vo_name,
map_address,
map_address + length);
mapping->m_object = object; mapping->m_object = object;
mapping->m_prot = prot; mapping->m_prot = prot;
mapping->m_object_offset = object_offset; mapping->m_object_offset = object_offset;
mapping->m_entry.e_type = VM_REGION_ENTRY_MAPPING; mapping->m_entry.e_type = VM_REGION_ENTRY_MAPPING;
mapping->m_entry.e_parent = &region->vr_entry; mapping->m_entry.e_parent = &region->vr_entry;
mapping->m_entry.e_base_address = map_address; mapping->m_entry.e_offset = region_offset;
mapping->m_entry.e_size = length; mapping->m_entry.e_size = length;
put_entry(region, &mapping->m_entry); region_put_entry(region, &mapping->m_entry);
queue_push_back(&object->vo_mappings, &mapping->m_object_entry); queue_push_back(&object->vo_mappings, &mapping->m_object_entry);
*out = map_address; #ifdef TRACE
virt_addr_t abs_base = entry_absolute_address(&mapping->m_entry);
tracek("mapping %s at [%llx-%llx]",
object->vo_name,
abs_base,
abs_base + length);
#endif
*out = entry_absolute_address(&mapping->m_entry);
return KERN_OK; return KERN_OK;
} }
bool vm_region_is_area_free(
const struct vm_region *region,
virt_addr_t base,
size_t len)
{
/* address of the last byte in the region */
virt_addr_t region_limit
= region->vr_entry.e_base_address + region->vr_entry.e_size - 1;
if (base < region->vr_entry.e_base_address || base > region_limit) {
return false;
}
if (base + len - 1 > region_limit) {
return false;
}
virt_addr_t limit = base + len - 1;
struct btree_node *cur = region->vr_entries.b_root;
if (!cur) {
return true;
}
while (cur) {
struct vm_region_entry *entry
= BTREE_CONTAINER(struct vm_region_entry, e_node, cur);
struct btree_node *next = NULL;
virt_addr_t entry_limit
= entry->e_base_address + entry->e_size - 1;
if (base > entry_limit) {
next = btree_right(cur);
} else if (limit < entry->e_base_address) {
next = btree_left(cur);
} else {
return false;
}
cur = next;
}
return true;
}
kern_status_t vm_region_demand_map( kern_status_t vm_region_demand_map(
struct vm_region *region, struct vm_region *region,
virt_addr_t addr, virt_addr_t addr,
enum pmap_fault_flags flags) enum pmap_fault_flags flags)
{ {
addr &= ~VM_PAGE_MASK; addr &= ~VM_PAGE_MASK;
region = vm_region_find_child(region, addr); if (addr < region->vr_entry.e_offset
|| addr > region->vr_entry.e_offset + region->vr_entry.e_size) {
return KERN_NO_ENTRY;
}
off_t region_offset = addr - region->vr_entry.e_offset;
struct vm_region_mapping *mapping struct vm_region_mapping *mapping
= vm_region_find_mapping(region, addr); = region_get_mapping_recursive(region, &region_offset, 1);
if (!mapping) { if (!mapping) {
return KERN_NO_ENTRY; return KERN_NO_ENTRY;
} }
off_t offset = addr - mapping->m_entry.e_base_address off_t object_offset = region_offset - mapping->m_entry.e_offset
+ mapping->m_object_offset; + mapping->m_object_offset;
tracek("vm: tried to access vm-object %s at offset=%05llx", tracek("vm: tried to access vm-object %s at offset=%05llx",
mapping->m_object->vo_name, mapping->m_object->vo_name,
offset); object_offset);
struct vm_page *pg struct vm_page *pg = vm_object_alloc_page(
= vm_object_alloc_page(mapping->m_object, offset, VM_PAGE_4K); mapping->m_object,
object_offset,
VM_PAGE_4K);
tracek("vm: mapping %07llx -> %10llx", vm_page_get_paddr(pg), addr); tracek("vm: mapping %07llx -> %10llx", vm_page_get_paddr(pg), addr);
return pmap_add( return pmap_add(
region->vr_pmap, region->vr_pmap,
@@ -650,8 +661,8 @@ void vm_region_dump(struct vm_region *region, int depth)
sizeof line - p, sizeof line - p,
"region: %s [%llx-%llx]", "region: %s [%llx-%llx]",
region->vr_name, region->vr_name,
region->vr_entry.e_base_address, region->vr_entry.e_offset,
region->vr_entry.e_base_address + region->vr_entry.e_size); region->vr_entry.e_offset + region->vr_entry.e_size);
printk("%s", line); printk("%s", line);
@@ -666,9 +677,9 @@ void vm_region_dump(struct vm_region *region, int depth)
struct vm_region_entry *entry struct vm_region_entry *entry
= BTREE_CONTAINER(struct vm_region_entry, e_node, cur); = BTREE_CONTAINER(struct vm_region_entry, e_node, cur);
struct vm_region *child_region = vm_region_from_entry(entry); struct vm_region *child_region = region_from_entry(entry);
struct vm_region_mapping *child_mapping struct vm_region_mapping *child_mapping
= vm_region_mapping_from_entry(entry); = mapping_from_entry(entry);
switch (entry->e_type) { switch (entry->e_type) {
case VM_REGION_ENTRY_REGION: case VM_REGION_ENTRY_REGION:
@@ -677,13 +688,13 @@ void vm_region_dump(struct vm_region *region, int depth)
p += snprintf( p += snprintf(
line + p, line + p,
sizeof line - p, sizeof line - p,
"mapping: %s [%llx-%llx] -> [%llx-%llx]", "mapping: %s p:[%llx-%llx] -> v:[%llx-%llx]",
child_mapping->m_object->vo_name, child_mapping->m_object->vo_name,
child_mapping->m_object_offset, child_mapping->m_object_offset,
child_mapping->m_object_offset child_mapping->m_object_offset
+ child_mapping->m_entry.e_size, + child_mapping->m_entry.e_size,
child_mapping->m_entry.e_base_address, child_mapping->m_entry.e_offset,
child_mapping->m_entry.e_base_address child_mapping->m_entry.e_offset
+ child_mapping->m_entry.e_size); + child_mapping->m_entry.e_size);
printk("%s", line); printk("%s", line);
break; break;