2011-02-24 09:29:38 +01:00
|
|
|
/*
|
2013-11-20 11:26:55 +01:00
|
|
|
* Copyright 2011 Steffen Vogel, Chair for Operating Systems,
|
2011-02-24 09:29:38 +01:00
|
|
|
* RWTH Aachen University
|
|
|
|
*
|
|
|
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
|
|
* you may not use this file except in compliance with the License.
|
|
|
|
* You may obtain a copy of the License at
|
|
|
|
*
|
|
|
|
* http://www.apache.org/licenses/LICENSE-2.0
|
|
|
|
*
|
|
|
|
* Unless required by applicable law or agreed to in writing, software
|
|
|
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
|
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
|
|
* See the License for the specific language governing permissions and
|
|
|
|
* limitations under the License.
|
|
|
|
*
|
|
|
|
* This file is part of MetalSVM.
|
|
|
|
*/
|
|
|
|
|
2013-11-20 11:26:55 +01:00
|
|
|
#include <metalsvm/vma.h>
|
2011-02-24 09:29:38 +01:00
|
|
|
#include <metalsvm/stdlib.h>
|
|
|
|
#include <metalsvm/stdio.h>
|
|
|
|
#include <metalsvm/tasks_types.h>
|
|
|
|
#include <metalsvm/spinlock.h>
|
|
|
|
#include <metalsvm/errno.h>
|
|
|
|
|
2014-02-18 12:50:59 +01:00
|
|
|
#ifdef CONFIG_MULTIBOOT
|
|
|
|
#include <asm/multiboot.h>
|
|
|
|
#endif
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Note that linker symbols are not variables, they have no memory allocated for
|
|
|
|
* maintaining a value, rather their address is their value.
|
|
|
|
*/
|
|
|
|
extern const void kernel_start;
|
|
|
|
extern const void kernel_end;
|
|
|
|
|
2011-02-24 09:29:38 +01:00
|
|
|
/*
|
2013-11-20 11:26:55 +01:00
|
|
|
* 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()
|
2011-02-24 09:29:38 +01:00
|
|
|
*/
|
2014-01-09 13:45:20 +01:00
|
|
|
static vma_t vma_boot = { VMA_KERN_MIN, VMA_KERN_MIN, VMA_HEAP };
|
2013-11-20 11:26:55 +01:00
|
|
|
static vma_t* vma_list = &vma_boot;
|
|
|
|
static spinlock_t vma_lock = SPINLOCK_INIT;
|
|
|
|
|
2014-02-18 12:50:59 +01:00
|
|
|
// TODO: we might move the architecture specific VMA regions to a
|
|
|
|
// seperate function arch_vma_init()
|
|
|
|
int vma_init()
|
|
|
|
{
|
|
|
|
int ret;
|
|
|
|
|
|
|
|
// add Kernel
|
|
|
|
ret = vma_add(PAGE_CEIL((size_t) &kernel_start),
|
|
|
|
PAGE_FLOOR((size_t) &kernel_end),
|
|
|
|
VMA_READ|VMA_WRITE|VMA_EXECUTE|VMA_CACHEABLE);
|
2014-02-18 12:54:52 +01:00
|
|
|
if (BUILTIN_EXPECT(ret, 0))
|
2014-02-18 12:50:59 +01:00
|
|
|
goto out;
|
|
|
|
|
|
|
|
#ifdef CONFIG_VGA
|
|
|
|
// add VGA video memory
|
|
|
|
ret = vma_add(VIDEO_MEM_ADDR, VIDEO_MEM_ADDR + PAGE_SIZE, VMA_READ|VMA_WRITE);
|
2014-02-18 12:54:52 +01:00
|
|
|
if (BUILTIN_EXPECT(ret, 0))
|
2014-02-18 12:50:59 +01:00
|
|
|
goto out;
|
|
|
|
#endif
|
|
|
|
|
|
|
|
#if MAX_CORES > 1
|
|
|
|
// add SMP boot page
|
|
|
|
ret = vma_add(SMP_SETUP_ADDR, SMP_SETUP_ADDR + PAGE_SIZE,
|
|
|
|
VMA_READ|VMA_WRITE|VMA_EXECUTE|VMA_CACHEABLE);
|
2014-02-18 12:54:52 +01:00
|
|
|
if (BUILTIN_EXPECT(ret, 0))
|
2014-02-18 12:50:59 +01:00
|
|
|
goto out;
|
|
|
|
#endif
|
|
|
|
|
|
|
|
#ifdef CONFIG_MULTIBOOT
|
|
|
|
// add Multiboot structures as modules
|
|
|
|
if (mb_info) {
|
|
|
|
ret = vma_add(PAGE_CEIL((size_t) mb_info),
|
|
|
|
PAGE_FLOOR((size_t) mb_info + sizeof(multiboot_info_t)),
|
|
|
|
VMA_READ|VMA_CACHEABLE);
|
2014-02-18 12:54:52 +01:00
|
|
|
if (BUILTIN_EXPECT(ret, 0))
|
2014-02-18 12:50:59 +01:00
|
|
|
goto out;
|
|
|
|
|
|
|
|
if (mb_info->flags & MULTIBOOT_INFO_MEM_MAP) {
|
|
|
|
ret = vma_add(PAGE_CEIL((size_t) mb_info->mmap_addr),
|
|
|
|
PAGE_FLOOR((size_t) mb_info->mmap_addr + mb_info->mmap_length),
|
|
|
|
VMA_READ|VMA_CACHEABLE);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (mb_info->flags & MULTIBOOT_INFO_MODS) {
|
|
|
|
multiboot_module_t* mmodule = (multiboot_module_t*) ((size_t) mb_info->mods_addr);
|
|
|
|
|
|
|
|
ret = vma_add(PAGE_CEIL((size_t) mb_info->mods_addr),
|
|
|
|
PAGE_FLOOR((size_t) mb_info->mods_addr + mb_info->mods_count*sizeof(multiboot_module_t)),
|
|
|
|
VMA_READ|VMA_CACHEABLE);
|
|
|
|
|
|
|
|
int i;
|
|
|
|
for(i=0; i<mb_info->mods_count; i++) {
|
|
|
|
ret = vma_add(PAGE_CEIL(mmodule[i].mod_start),
|
|
|
|
PAGE_FLOOR(mmodule[i].mod_end),
|
|
|
|
VMA_READ|VMA_WRITE|VMA_CACHEABLE);
|
2014-02-18 12:54:52 +01:00
|
|
|
if (BUILTIN_EXPECT(ret, 0))
|
2014-02-18 12:50:59 +01:00
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
|
|
|
out:
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
2013-11-20 11:26:55 +01:00
|
|
|
size_t vma_alloc(size_t size, uint32_t flags)
|
|
|
|
{
|
|
|
|
task_t* task = per_core(current_task);
|
|
|
|
spinlock_t* lock;
|
|
|
|
vma_t** list;
|
|
|
|
|
2014-02-18 12:54:52 +01:00
|
|
|
kprintf("vma_alloc: size = %#lx, flags = %#x\n", size, flags); // TODO: remove
|
2013-11-20 11:26:55 +01:00
|
|
|
|
|
|
|
size_t base, limit; // boundaries for search
|
2014-04-07 15:53:35 +02:00
|
|
|
size_t start, end; // boundaries of free gaps
|
2013-11-20 11:26:55 +01:00
|
|
|
|
|
|
|
if (flags & VMA_USER) {
|
2014-04-07 15:53:35 +02:00
|
|
|
base = VMA_USER_MIN;
|
2013-11-20 11:26:55 +01:00
|
|
|
limit = VMA_USER_MAX;
|
|
|
|
list = &task->vma_list;
|
|
|
|
lock = &task->vma_lock;
|
|
|
|
}
|
|
|
|
else {
|
2014-01-09 13:45:20 +01:00
|
|
|
base = VMA_KERN_MIN;
|
2013-11-20 11:26:55 +01:00
|
|
|
limit = VMA_KERN_MAX;
|
|
|
|
list = &vma_list;
|
|
|
|
lock = &vma_lock;
|
|
|
|
}
|
|
|
|
|
|
|
|
spinlock_lock(lock);
|
|
|
|
|
2014-01-09 13:45:20 +01:00
|
|
|
// first fit search for free memory area
|
|
|
|
vma_t* pred = NULL; // vma before current gap
|
|
|
|
vma_t* succ = *list; // vma after current gap
|
2013-11-20 11:26:55 +01:00
|
|
|
do {
|
|
|
|
start = (pred) ? pred->end : base;
|
|
|
|
end = (succ) ? succ->start : limit;
|
|
|
|
|
2014-04-07 15:53:35 +02:00
|
|
|
if (start + size < end && start >= base && start + size < limit)
|
|
|
|
goto found; // we found a gap which is large enough and in the bounds
|
2013-11-20 11:26:55 +01:00
|
|
|
|
2014-01-09 13:45:20 +01:00
|
|
|
pred = succ;
|
2014-04-07 15:53:35 +02:00
|
|
|
succ = (pred) ? pred->next : NULL;
|
2013-11-20 11:26:55 +01:00
|
|
|
} while (pred || succ);
|
|
|
|
|
2014-04-07 15:53:35 +02:00
|
|
|
fail:
|
|
|
|
spinlock_unlock(lock); // we were unlucky to find a free gap
|
2013-11-20 11:26:55 +01:00
|
|
|
|
2014-04-07 15:53:35 +02:00
|
|
|
return 0;
|
2014-01-09 13:45:20 +01:00
|
|
|
|
2014-04-07 15:53:35 +02:00
|
|
|
found:
|
|
|
|
if (pred && pred->flags == flags)
|
|
|
|
pred->end = start + size; // resize VMA
|
2013-11-20 11:26:55 +01:00
|
|
|
else {
|
2014-01-09 13:45:20 +01:00
|
|
|
// insert new VMA
|
2013-11-20 11:26:55 +01:00
|
|
|
vma_t* new = kmalloc(sizeof(vma_t));
|
2014-04-07 15:53:35 +02:00
|
|
|
if (BUILTIN_EXPECT(!new, 0))
|
|
|
|
goto fail;
|
2013-11-20 11:26:55 +01:00
|
|
|
|
2014-01-09 13:45:20 +01:00
|
|
|
new->start = start;
|
2014-04-07 15:53:35 +02:00
|
|
|
new->end = start + size;
|
2013-11-20 11:26:55 +01:00
|
|
|
new->flags = flags;
|
|
|
|
new->next = succ;
|
|
|
|
new->prev = pred;
|
|
|
|
|
|
|
|
if (succ)
|
|
|
|
succ->prev = new;
|
2014-01-09 13:45:20 +01:00
|
|
|
if (pred)
|
|
|
|
pred->next = new;
|
2013-11-20 11:26:55 +01:00
|
|
|
else
|
|
|
|
*list = new;
|
|
|
|
}
|
|
|
|
|
|
|
|
spinlock_unlock(lock);
|
2014-01-09 13:45:20 +01:00
|
|
|
|
|
|
|
return start;
|
2013-11-20 11:26:55 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
int vma_free(size_t start, size_t end)
|
2011-02-24 09:29:38 +01:00
|
|
|
{
|
2013-11-20 11:26:55 +01:00
|
|
|
task_t* task = per_core(current_task);
|
|
|
|
spinlock_t* lock;
|
|
|
|
vma_t* vma;
|
|
|
|
vma_t** list;
|
|
|
|
|
2014-02-18 12:54:52 +01:00
|
|
|
kprintf("vma_free: start = %#lx, end = %#lx\n", start, end); // TODO: remove
|
|
|
|
|
2013-11-20 11:26:55 +01:00
|
|
|
if (BUILTIN_EXPECT(start >= end, 0))
|
|
|
|
return -EINVAL;
|
|
|
|
|
2014-01-09 13:45:20 +01:00
|
|
|
if (end < VMA_KERN_MAX) {
|
2013-11-20 11:26:55 +01:00
|
|
|
lock = &vma_lock;
|
|
|
|
list = &vma_list;
|
|
|
|
}
|
|
|
|
else if (start >= VMA_KERN_MAX) {
|
|
|
|
lock = &task->vma_lock;
|
|
|
|
list = &task->vma_list;
|
|
|
|
}
|
2011-02-24 09:29:38 +01:00
|
|
|
|
2013-11-20 11:26:55 +01:00
|
|
|
if (BUILTIN_EXPECT(!*list, 0))
|
|
|
|
return -EINVAL;
|
2011-02-24 09:29:38 +01:00
|
|
|
|
2013-11-20 11:26:55 +01:00
|
|
|
spinlock_lock(lock);
|
|
|
|
|
|
|
|
// search vma
|
|
|
|
vma = *list;
|
|
|
|
while (vma) {
|
|
|
|
if (start >= vma->start && end <= vma->end) break;
|
2014-01-09 13:45:20 +01:00
|
|
|
vma = vma->next;
|
2013-11-20 11:26:55 +01:00
|
|
|
}
|
2011-02-24 09:29:38 +01:00
|
|
|
|
2013-11-20 11:26:55 +01:00
|
|
|
if (BUILTIN_EXPECT(!vma, 0)) {
|
|
|
|
spinlock_unlock(lock);
|
|
|
|
return -EINVAL;
|
|
|
|
}
|
2011-02-24 09:29:38 +01:00
|
|
|
|
2013-11-20 11:26:55 +01:00
|
|
|
// 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;
|
|
|
|
}
|
2011-02-24 09:29:38 +01:00
|
|
|
|
2014-01-09 13:45:20 +01:00
|
|
|
new->end = vma->end;
|
2013-11-20 11:26:55 +01:00
|
|
|
vma->end = start;
|
2014-01-09 13:45:20 +01:00
|
|
|
new->start = end;
|
2011-02-24 09:29:38 +01:00
|
|
|
|
2013-11-20 11:26:55 +01:00
|
|
|
new->next = vma->next;
|
|
|
|
vma->next = new;
|
2014-01-09 13:45:20 +01:00
|
|
|
new->prev = vma;
|
2011-02-24 09:29:38 +01:00
|
|
|
}
|
|
|
|
|
2013-11-20 11:26:55 +01:00
|
|
|
spinlock_unlock(lock);
|
2011-02-24 09:29:38 +01:00
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2013-11-20 11:26:55 +01:00
|
|
|
int vma_add(size_t start, size_t end, uint32_t flags)
|
2011-02-24 09:29:38 +01:00
|
|
|
{
|
2013-11-20 11:26:55 +01:00
|
|
|
task_t* task = per_core(current_task);
|
|
|
|
spinlock_t* lock;
|
|
|
|
vma_t** list;
|
|
|
|
|
|
|
|
if (BUILTIN_EXPECT(start >= end, 0))
|
2011-02-24 09:29:38 +01:00
|
|
|
return -EINVAL;
|
|
|
|
|
2013-11-20 11:26:55 +01:00
|
|
|
if (flags & VMA_USER) {
|
|
|
|
list = &task->vma_list;
|
|
|
|
lock = &task->vma_lock;
|
2011-02-24 09:29:38 +01:00
|
|
|
|
2013-11-20 11:26:55 +01:00
|
|
|
// check if address is in userspace
|
|
|
|
if (BUILTIN_EXPECT(start < VMA_KERN_MAX, 0))
|
|
|
|
return -EINVAL;
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
list = &vma_list;
|
|
|
|
lock = &vma_lock;
|
2011-02-24 09:29:38 +01:00
|
|
|
|
2013-11-20 11:26:55 +01:00
|
|
|
// check if address is in kernelspace
|
2014-01-09 13:45:20 +01:00
|
|
|
if (BUILTIN_EXPECT(end >= VMA_KERN_MAX, 0))
|
2013-11-20 11:26:55 +01:00
|
|
|
return -EINVAL;
|
|
|
|
}
|
|
|
|
|
2014-02-18 12:54:52 +01:00
|
|
|
kprintf("vma_add: start = %#lx, end = %#lx, flags = %#x\n", start, end, flags); // TODO: remove
|
|
|
|
|
2013-11-20 11:26:55 +01:00
|
|
|
spinlock_lock(lock);
|
|
|
|
|
|
|
|
// search gap
|
2014-01-09 13:45:20 +01:00
|
|
|
vma_t* pred = NULL;
|
|
|
|
vma_t* succ = *list;
|
2014-02-03 09:58:32 +01:00
|
|
|
|
|
|
|
while (pred || succ) {
|
2013-11-20 11:26:55 +01:00
|
|
|
if ((!pred || pred->end <= start) &&
|
|
|
|
(!succ || succ->start >= end))
|
|
|
|
break;
|
|
|
|
|
2014-01-09 13:45:20 +01:00
|
|
|
pred = succ;
|
2014-02-03 09:58:32 +01:00
|
|
|
succ = (succ) ? succ->next : NULL;
|
|
|
|
}
|
2013-11-20 11:26:55 +01:00
|
|
|
|
2014-02-03 09:58:32 +01:00
|
|
|
if (BUILTIN_EXPECT(*list && !pred && !succ, 0)) {
|
|
|
|
spinlock_unlock(lock);
|
|
|
|
return -EINVAL;
|
|
|
|
}
|
2011-02-24 09:29:38 +01:00
|
|
|
|
2014-01-09 13:45:20 +01:00
|
|
|
// insert new VMA
|
|
|
|
vma_t* new = kmalloc(sizeof(vma_t));
|
|
|
|
if (BUILTIN_EXPECT(!new, 0)) {
|
|
|
|
spinlock_unlock(lock);
|
|
|
|
return -ENOMEM;
|
2013-11-20 11:26:55 +01:00
|
|
|
}
|
|
|
|
|
2014-01-09 13:45:20 +01:00
|
|
|
new->start = start;
|
|
|
|
new->end = end;
|
|
|
|
new->flags = flags;
|
|
|
|
new->next = succ;
|
|
|
|
new->prev = pred;
|
|
|
|
|
|
|
|
if (succ)
|
|
|
|
succ->prev = new;
|
|
|
|
if (pred)
|
|
|
|
pred->next = new;
|
|
|
|
else
|
|
|
|
*list = new;
|
|
|
|
|
2013-11-20 11:26:55 +01:00
|
|
|
spinlock_unlock(lock);
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2014-04-07 15:54:09 +02:00
|
|
|
int copy_vma_list(task_t* src, task_t* dest)
|
2013-11-20 11:26:55 +01:00
|
|
|
{
|
2014-04-07 15:54:09 +02:00
|
|
|
spinlock_init(&dest->vma_lock);
|
2013-11-20 11:26:55 +01:00
|
|
|
|
2014-04-07 15:54:09 +02:00
|
|
|
spinlock_lock(&src->vma_lock);
|
|
|
|
spinlock_lock(&dest->vma_lock);
|
2013-11-20 11:26:55 +01:00
|
|
|
|
|
|
|
vma_t* last = NULL;
|
2014-04-07 15:54:09 +02:00
|
|
|
vma_t* old;
|
|
|
|
for (old=src->vma_list; old; old=old->next) {
|
2013-11-20 11:26:55 +01:00
|
|
|
vma_t *new = kmalloc(sizeof(vma_t));
|
|
|
|
if (BUILTIN_EXPECT(!new, 0)) {
|
2014-04-07 15:54:09 +02:00
|
|
|
spinlock_unlock(&dest->vma_lock);
|
|
|
|
spinlock_unlock(&src->vma_lock);
|
2014-01-09 13:45:20 +01:00
|
|
|
return -ENOMEM;
|
2013-11-20 11:26:55 +01:00
|
|
|
}
|
|
|
|
|
2014-04-07 15:54:09 +02:00
|
|
|
new->start = old->start;
|
|
|
|
new->end = old->end;
|
|
|
|
new->flags = old->flags;
|
2013-11-20 11:26:55 +01:00
|
|
|
new->prev = last;
|
|
|
|
|
|
|
|
if (last)
|
|
|
|
last->next = new;
|
2011-02-24 09:29:38 +01:00
|
|
|
else
|
2014-04-07 15:54:09 +02:00
|
|
|
dest->vma_list = new;
|
2011-02-24 09:29:38 +01:00
|
|
|
|
2013-11-20 11:26:55 +01:00
|
|
|
last = new;
|
2011-02-24 09:29:38 +01:00
|
|
|
}
|
|
|
|
|
2014-04-07 15:54:09 +02:00
|
|
|
spinlock_unlock(&dest->vma_lock);
|
|
|
|
spinlock_unlock(&src->vma_lock);
|
2014-02-03 09:58:32 +01:00
|
|
|
|
2014-01-09 13:45:20 +01:00
|
|
|
return 0;
|
2013-11-20 11:26:55 +01:00
|
|
|
}
|
|
|
|
|
2014-04-07 15:54:09 +02:00
|
|
|
int drop_vma_list(task_t *task)
|
2013-11-20 11:26:55 +01:00
|
|
|
{
|
2014-01-09 13:45:20 +01:00
|
|
|
vma_t* vma;
|
|
|
|
|
2013-11-20 11:26:55 +01:00
|
|
|
spinlock_lock(&task->vma_lock);
|
|
|
|
|
2014-01-09 15:20:22 +01:00
|
|
|
while ((vma = task->vma_list)) {
|
2014-01-09 13:45:20 +01:00
|
|
|
task->vma_list = vma->next;
|
|
|
|
kfree(vma);
|
|
|
|
}
|
2013-11-20 11:26:55 +01:00
|
|
|
|
2011-02-24 09:29:38 +01:00
|
|
|
spinlock_unlock(&task->vma_lock);
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
2013-11-20 11:26:55 +01:00
|
|
|
|
|
|
|
void vma_dump()
|
|
|
|
{
|
|
|
|
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' : '-');
|
2014-01-09 13:45:20 +01:00
|
|
|
vma = vma->next;
|
2013-11-20 11:26:55 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
task_t* task = per_core(current_task);
|
|
|
|
|
|
|
|
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);
|
|
|
|
}
|