added VMA list implementation
This commit is contained in:
parent
421e7ec66e
commit
3cd5a5853b
2 changed files with 354 additions and 73 deletions
|
@ -27,56 +27,102 @@
|
|||
#define __VMA_H__
|
||||
|
||||
#include <metalsvm/stddef.h>
|
||||
#include <asm/page.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/// Read access to this VMA is allowed
|
||||
#define VMA_READ (1 << 0)
|
||||
/// Write access to this VMA is allowed
|
||||
#define VMA_WRITE (1 << 1)
|
||||
/// Instructions fetches in this VMA are allowed
|
||||
#define VMA_EXECUTE (1 << 2)
|
||||
/// This VMA is cacheable
|
||||
#define VMA_CACHEABLE (1 << 3)
|
||||
#define VMA_NOACCESS (1 << 4)
|
||||
/// This VMA is not accessable
|
||||
#define VMA_NO_ACCESS (1 << 4)
|
||||
/// This VMA should be part of the userspace
|
||||
#define VMA_USER (1 << 5)
|
||||
/// A collection of flags used for the kernel heap (kmalloc)
|
||||
#define VMA_HEAP (VMA_READ|VMA_WRITE|VMA_CACHEABLE)
|
||||
|
||||
// boundaries for VAS allocation
|
||||
extern const void kernel_end;
|
||||
//#define VMA_KERN_MIN (((size_t) &kernel_end + PAGE_SIZE) & PAGE_MASK)
|
||||
#define VMA_KERN_MAX KERNEL_SPACE
|
||||
#define VMA_USER_MAX (1UL << 47) // TODO
|
||||
|
||||
struct vma;
|
||||
|
||||
/** @brief VMA structure definition */
|
||||
/** @brief VMA structure definition
|
||||
*
|
||||
* Each item in this linked list marks a used part of the virtual address space.
|
||||
* Its used by vm_alloc() to find holes between them.
|
||||
*/
|
||||
typedef struct vma {
|
||||
/// Start address of the memory area
|
||||
size_t start;
|
||||
/// End address of the memory area
|
||||
size_t end;
|
||||
/// Type flags field
|
||||
uint32_t type;
|
||||
uint32_t flags;
|
||||
/// Pointer of next VMA element in the list
|
||||
struct vma* next;
|
||||
/// Pointer to previous VMA element in the list
|
||||
struct vma* prev;
|
||||
} vma_t;
|
||||
|
||||
/** @brief Add a new virtual memory region to the list of VMAs
|
||||
/** @brief Add a new virtual memory area to the list of VMAs
|
||||
*
|
||||
* @param task Pointer to the task_t structure of the task
|
||||
* @param start Start address of the new region
|
||||
* @param end End address of the new region
|
||||
* @param type Type flags the new region shall have
|
||||
* @param start Start address of the new area
|
||||
* @param end End address of the new area
|
||||
* @param flags Type flags the new area shall have
|
||||
*
|
||||
* @return
|
||||
* - 0 on success
|
||||
* - -EINVAL (-22) or -EINVAL (-12) on failure
|
||||
*/
|
||||
int vma_add(struct task* task, size_t start, size_t end, uint32_t type);
|
||||
int vma_add(size_t start, size_t end, uint32_t flags);
|
||||
|
||||
/** @brief Dump information about this task's VMAs into the terminal.
|
||||
/** @brief Search for a free memory area
|
||||
*
|
||||
* This will print out Start, end and flags for each VMA in the task's list
|
||||
* @param size Size of requestes VMA in bytes
|
||||
* @param flags
|
||||
* @return Type flags the new area shall have
|
||||
* - 0 on failure
|
||||
* - the start address of a free area
|
||||
*/
|
||||
size_t vma_alloc(size_t size, uint32_t flags);
|
||||
|
||||
/** @brief Free an allocated memory area
|
||||
*
|
||||
* @param task The task's task_t structure
|
||||
* @param start Start address of the area to be freed
|
||||
* @param end End address of the to be freed
|
||||
* @return
|
||||
* - 0 on success
|
||||
* - -EINVAL (-22) on failure
|
||||
*/
|
||||
int vma_dump(struct task* task);
|
||||
int vma_free(size_t start, size_t end);
|
||||
|
||||
/** @brief Free all virtual memory areas
|
||||
*
|
||||
* @return
|
||||
* - 0 on success
|
||||
*/
|
||||
int drop_vma_list();
|
||||
|
||||
/** @brief Copy the VMA list of the current task to task
|
||||
*
|
||||
* @param task The task where the list should be copied to
|
||||
* @return
|
||||
* - 0 on success
|
||||
*/
|
||||
int copy_vma_list(struct task* task);
|
||||
|
||||
/** @brief Dump information about this task's VMAs into the terminal. */
|
||||
void vma_dump();
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
|
|
355
mm/vma.c
355
mm/vma.c
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright 2011 Stefan Lankes, Chair for Operating Systems,
|
||||
* Copyright 2011 Steffen Vogel, Chair for Operating Systems,
|
||||
* RWTH Aachen University
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
|
@ -17,87 +17,322 @@
|
|||
* This file is part of MetalSVM.
|
||||
*/
|
||||
|
||||
#include <metalsvm/vma.h>
|
||||
#include <metalsvm/stdlib.h>
|
||||
#include <metalsvm/stdio.h>
|
||||
#include <metalsvm/tasks_types.h>
|
||||
#include <metalsvm/spinlock.h>
|
||||
#include <metalsvm/vma.h>
|
||||
#include <metalsvm/errno.h>
|
||||
|
||||
/*
|
||||
* add a new virtual memory region to the list of VMAs
|
||||
* Kernel space VMA list and lock
|
||||
*
|
||||
* For bootstrapping we initialize the VMA list with one empty VMA
|
||||
* (start == end) and expand this VMA by calls to vma_alloc()
|
||||
*/
|
||||
int vma_add(task_t* task, size_t start, size_t end, uint32_t type)
|
||||
static vma_t vma_boot = { VMA_KERN_MAX, VMA_KERN_MAX, VMA_HEAP };
|
||||
static vma_t* vma_list = &vma_boot;
|
||||
static spinlock_t vma_lock = SPINLOCK_INIT;
|
||||
|
||||
size_t vma_alloc(size_t size, uint32_t flags)
|
||||
{
|
||||
vma_t* new_vma;
|
||||
|
||||
if (BUILTIN_EXPECT(!task || start > end, 0))
|
||||
task_t* task = per_core(current_task);
|
||||
spinlock_t* lock;
|
||||
vma_t** list;
|
||||
size_t ret = 0;
|
||||
|
||||
kprintf("vma_alloc(0x%lx, 0x%x)\n", size, flags);
|
||||
|
||||
size_t base, limit; // boundaries for search
|
||||
size_t start, end;
|
||||
|
||||
if (BUILTIN_EXPECT(!size, 0))
|
||||
return 0;
|
||||
|
||||
if (flags & VMA_USER) {
|
||||
base = VMA_KERN_MAX;
|
||||
limit = VMA_USER_MAX;
|
||||
list = &task->vma_list;
|
||||
lock = &task->vma_lock;
|
||||
}
|
||||
else {
|
||||
base = 0;
|
||||
limit = VMA_KERN_MAX;
|
||||
list = &vma_list;
|
||||
lock = &vma_lock;
|
||||
}
|
||||
|
||||
spinlock_lock(lock);
|
||||
|
||||
// "last" fit search for free memory area
|
||||
vma_t* pred = *list; // vma before current gap
|
||||
vma_t* succ = NULL; // vma after current gap
|
||||
do {
|
||||
start = (pred) ? pred->end : base;
|
||||
end = (succ) ? succ->start : limit;
|
||||
|
||||
if (end > start && end - start > size)
|
||||
break; // we found a gap
|
||||
|
||||
succ = pred;
|
||||
pred = (pred) ? pred->prev : NULL;
|
||||
} while (pred || succ);
|
||||
|
||||
if (BUILTIN_EXPECT(end > limit || end < start || end - start < size, 0)) {
|
||||
spinlock_unlock(lock);
|
||||
return 0;
|
||||
}
|
||||
|
||||
// resize existing vma
|
||||
if (succ && succ->flags == flags) {
|
||||
succ->start -= size;
|
||||
ret = succ->start;
|
||||
}
|
||||
// insert new vma
|
||||
else {
|
||||
vma_t* new = kmalloc(sizeof(vma_t));
|
||||
if (BUILTIN_EXPECT(!new, 0))
|
||||
return 0;
|
||||
|
||||
new->start = end-size;
|
||||
new->end = end;
|
||||
new->flags = flags;
|
||||
new->next = succ;
|
||||
new->prev = pred;
|
||||
|
||||
if (pred)
|
||||
pred->next = new;
|
||||
if (succ)
|
||||
succ->prev = new;
|
||||
else
|
||||
*list = new;
|
||||
|
||||
ret = new->start;
|
||||
}
|
||||
|
||||
spinlock_unlock(lock);
|
||||
return ret;
|
||||
}
|
||||
|
||||
int vma_free(size_t start, size_t end)
|
||||
{
|
||||
task_t* task = per_core(current_task);
|
||||
spinlock_t* lock;
|
||||
vma_t* vma;
|
||||
vma_t** list;
|
||||
|
||||
if (BUILTIN_EXPECT(start >= end, 0))
|
||||
return -EINVAL;
|
||||
|
||||
new_vma = kmalloc(sizeof(new_vma));
|
||||
if (!new_vma)
|
||||
return -ENOMEM;
|
||||
if (end <= VMA_KERN_MAX) {
|
||||
lock = &vma_lock;
|
||||
list = &vma_list;
|
||||
}
|
||||
else if (start >= VMA_KERN_MAX) {
|
||||
lock = &task->vma_lock;
|
||||
list = &task->vma_list;
|
||||
}
|
||||
else
|
||||
return -EINVAL;
|
||||
|
||||
if (BUILTIN_EXPECT(!*list, 0))
|
||||
return -EINVAL;
|
||||
|
||||
spinlock_lock(lock);
|
||||
|
||||
// search vma
|
||||
vma = *list;
|
||||
while (vma) {
|
||||
if (start >= vma->start && end <= vma->end) break;
|
||||
vma = vma->prev;
|
||||
}
|
||||
|
||||
if (BUILTIN_EXPECT(!vma, 0)) {
|
||||
spinlock_unlock(lock);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
// free/resize vma
|
||||
if (start == vma->start && end == vma->end) {
|
||||
if (vma == *list)
|
||||
*list = vma->next; // update list head
|
||||
if (vma->prev)
|
||||
vma->prev->next = vma->next;
|
||||
if (vma->next)
|
||||
vma->next->prev = vma->prev;
|
||||
kfree(vma);
|
||||
}
|
||||
else if (start == vma->start)
|
||||
vma->start = end;
|
||||
else if (end == vma->end)
|
||||
vma->end = start;
|
||||
else {
|
||||
vma_t* new = kmalloc(sizeof(vma_t));
|
||||
if (BUILTIN_EXPECT(!new, 0)) {
|
||||
spinlock_unlock(lock);
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
new->start = end;
|
||||
vma->end = start;
|
||||
|
||||
new->end = vma->end;
|
||||
new->next = vma->next;
|
||||
new->prev = vma;
|
||||
vma->next = new;
|
||||
}
|
||||
|
||||
spinlock_unlock(lock);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int vma_add(size_t start, size_t end, uint32_t flags)
|
||||
{
|
||||
task_t* task = per_core(current_task);
|
||||
spinlock_t* lock;
|
||||
vma_t** list;
|
||||
|
||||
kprintf("vma_add(0x%lx, 0x%lx, 0x%x)\n", start, end, flags);
|
||||
|
||||
if (BUILTIN_EXPECT(start >= end, 0))
|
||||
return -EINVAL;
|
||||
|
||||
if (flags & VMA_USER) {
|
||||
list = &task->vma_list;
|
||||
lock = &task->vma_lock;
|
||||
|
||||
// check if address is in userspace
|
||||
if (BUILTIN_EXPECT(start < VMA_KERN_MAX, 0))
|
||||
return -EINVAL;
|
||||
}
|
||||
else {
|
||||
list = &vma_list;
|
||||
lock = &vma_lock;
|
||||
|
||||
// check if address is in kernelspace
|
||||
if (BUILTIN_EXPECT(end > VMA_KERN_MAX, 0))
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
spinlock_lock(lock);
|
||||
|
||||
// search gap
|
||||
vma_t* pred = *list;
|
||||
vma_t* succ = NULL;
|
||||
while (pred) {
|
||||
if ((!pred || pred->end <= start) &&
|
||||
(!succ || succ->start >= end))
|
||||
break;
|
||||
|
||||
succ = pred;
|
||||
pred = pred->prev;
|
||||
}
|
||||
|
||||
// resize existing vma
|
||||
if (pred && pred->end == start && pred->flags == flags)
|
||||
pred->end = end;
|
||||
else if (succ && succ->start == end && succ->flags == flags)
|
||||
succ->start = start;
|
||||
// insert new vma
|
||||
else {
|
||||
vma_t* new = kmalloc(sizeof(vma_t));
|
||||
if (BUILTIN_EXPECT(!new, 0))
|
||||
return 0;
|
||||
|
||||
new->start = start;
|
||||
new->end = end;
|
||||
new->flags = flags;
|
||||
new->next = succ;
|
||||
new->prev = pred;
|
||||
|
||||
if (pred)
|
||||
pred->next = new;
|
||||
if (succ)
|
||||
succ->prev = new;
|
||||
else
|
||||
*list = new;
|
||||
}
|
||||
|
||||
spinlock_unlock(lock);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int copy_vma_list(task_t* task)
|
||||
{
|
||||
task_t* parent_task = per_core(current_task);
|
||||
|
||||
spinlock_init(&task->vma_lock);
|
||||
spinlock_lock(&parent_task->vma_lock);
|
||||
spinlock_lock(&task->vma_lock);
|
||||
|
||||
int ret = 0;
|
||||
vma_t* last = NULL;
|
||||
vma_t* parent = parent_task->vma_list;
|
||||
|
||||
while (parent) {
|
||||
vma_t *new = kmalloc(sizeof(vma_t));
|
||||
if (BUILTIN_EXPECT(!new, 0)) {
|
||||
ret = -ENOMEM;
|
||||
goto out;
|
||||
}
|
||||
|
||||
new->start = parent->start;
|
||||
new->end = parent->end;
|
||||
new->flags = parent->flags;
|
||||
new->prev = last;
|
||||
|
||||
if (last)
|
||||
last->next = new;
|
||||
else
|
||||
task->vma_list = new;
|
||||
|
||||
last = new;
|
||||
parent = parent->next;
|
||||
}
|
||||
|
||||
out:
|
||||
spinlock_unlock(&task->vma_lock);
|
||||
spinlock_unlock(&parent_task->vma_lock);
|
||||
return ret;
|
||||
}
|
||||
|
||||
int drop_vma_list()
|
||||
{
|
||||
task_t* task = per_core(current_task);
|
||||
|
||||
spinlock_lock(&task->vma_lock);
|
||||
|
||||
new_vma->start = start;
|
||||
new_vma->end = end;
|
||||
new_vma->type = type;
|
||||
|
||||
if (!(task->vma_list)) {
|
||||
new_vma->next = new_vma->prev = NULL;
|
||||
task->vma_list = new_vma;
|
||||
} else {
|
||||
vma_t* tmp = task->vma_list;
|
||||
|
||||
while (tmp->next && tmp->start < start)
|
||||
tmp = tmp->next;
|
||||
|
||||
new_vma->next = tmp->next;
|
||||
new_vma->prev = tmp;
|
||||
tmp->next = new_vma;
|
||||
}
|
||||
while(task->vma_list)
|
||||
pfree((void*) task->vma_list->start, task->vma_list->end - task->vma_list->start);
|
||||
|
||||
spinlock_unlock(&task->vma_lock);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int vma_dump(task_t* task)
|
||||
void vma_dump()
|
||||
{
|
||||
vma_t* tmp;
|
||||
|
||||
if (BUILTIN_EXPECT(!task, 0))
|
||||
return -EINVAL;
|
||||
|
||||
spinlock_lock(&task->vma_lock);
|
||||
|
||||
int cnt = 0;
|
||||
tmp = task->vma_list;
|
||||
while (tmp) {
|
||||
kprintf("#%d\t%8x - %8x: size=%6x, flags=", cnt, tmp->start, tmp->end, tmp->end - tmp->start);
|
||||
|
||||
if (tmp->type & VMA_READ)
|
||||
kputs("r");
|
||||
else
|
||||
kputs("-");
|
||||
|
||||
if (tmp->type & VMA_WRITE)
|
||||
kputs("w");
|
||||
else
|
||||
kputs("-");
|
||||
|
||||
if (tmp->type & VMA_EXECUTE)
|
||||
kputs("x");
|
||||
else
|
||||
kputs("-");
|
||||
kputs("\n");
|
||||
|
||||
tmp = tmp->next;
|
||||
cnt++;
|
||||
void print_vma(vma_t *vma) {
|
||||
while (vma) {
|
||||
kprintf("0x%lx - 0x%lx: size=%x, flags=%c%c%c\n", vma->start, vma->end, vma->end - vma->start,
|
||||
(vma->flags & VMA_READ) ? 'r' : '-',
|
||||
(vma->flags & VMA_WRITE) ? 'w' : '-',
|
||||
(vma->flags & VMA_EXECUTE) ? 'x' : '-');
|
||||
vma = vma->prev;
|
||||
}
|
||||
}
|
||||
|
||||
spinlock_unlock(&task->vma_lock);
|
||||
task_t* task = per_core(current_task);
|
||||
|
||||
return 0;
|
||||
kputs("Kernelspace VMAs:\n");
|
||||
spinlock_lock(&vma_lock);
|
||||
print_vma(vma_list);
|
||||
spinlock_unlock(&vma_lock);
|
||||
|
||||
kputs("Userspace VMAs:\n");
|
||||
spinlock_lock(&task->vma_lock);
|
||||
print_vma(task->vma_list);
|
||||
spinlock_unlock(&task->vma_lock);
|
||||
}
|
||||
|
|
Loading…
Add table
Reference in a new issue