2015-05-23 14:35:45 +02:00
|
|
|
/*
|
|
|
|
* Copyright (c) 2014, Steffen Vogel, RWTH Aachen University
|
|
|
|
* All rights reserved.
|
|
|
|
*
|
|
|
|
* Redistribution and use in source and binary forms, with or without
|
|
|
|
* modification, are permitted provided that the following conditions are met:
|
|
|
|
* * Redistributions of source code must retain the above copyright
|
|
|
|
* notice, this list of conditions and the following disclaimer.
|
|
|
|
* * Redistributions in binary form must reproduce the above copyright
|
|
|
|
* notice, this list of conditions and the following disclaimer in the
|
|
|
|
* documentation and/or other materials provided with the distribution.
|
|
|
|
* * Neither the name of the University nor the names of its contributors
|
|
|
|
* may be used to endorse or promote products derived from this
|
|
|
|
* software without specific prior written permission.
|
|
|
|
*
|
|
|
|
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
|
|
|
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
|
|
|
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
|
|
|
* DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE FOR ANY
|
|
|
|
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
|
|
|
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
|
|
|
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
|
|
|
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
|
|
|
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
|
|
|
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
|
|
*/
|
|
|
|
|
|
|
|
#include <hermit/vma.h>
|
|
|
|
#include <hermit/stdlib.h>
|
|
|
|
#include <hermit/stdio.h>
|
|
|
|
#include <hermit/tasks_types.h>
|
|
|
|
#include <hermit/spinlock.h>
|
|
|
|
#include <hermit/errno.h>
|
2016-11-04 12:09:43 +01:00
|
|
|
#include <hermit/logging.h>
|
2015-05-23 14:35:45 +02:00
|
|
|
|
2017-03-06 23:32:42 +01:00
|
|
|
/*
|
2015-05-23 14:35:45 +02:00
|
|
|
* 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;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Kernel space VMA list and lock
|
2017-03-06 23:32:42 +01:00
|
|
|
*
|
2015-05-23 14:35:45 +02:00
|
|
|
* For bootstrapping we initialize the VMA list with one empty VMA
|
|
|
|
* (start == end) and expand this VMA by calls to vma_alloc()
|
|
|
|
*/
|
2016-03-21 00:18:45 +01:00
|
|
|
static vma_t vma_boot = { VMA_MIN, VMA_MIN, VMA_HEAP };
|
2015-05-23 14:35:45 +02:00
|
|
|
static vma_t* vma_list = &vma_boot;
|
2017-09-23 23:27:12 +02:00
|
|
|
spinlock_irqsave_t hermit_mm_lock = SPINLOCK_IRQSAVE_INIT;
|
2015-05-23 14:35:45 +02:00
|
|
|
|
|
|
|
int vma_init(void)
|
|
|
|
{
|
|
|
|
int ret;
|
|
|
|
|
2016-11-04 12:09:43 +01:00
|
|
|
LOG_INFO("vma_init: reserve vma region 0x%llx - 0x%llx\n",
|
2017-07-15 16:24:08 +02:00
|
|
|
PAGE_2M_FLOOR((size_t) &kernel_start),
|
|
|
|
PAGE_2M_CEIL((size_t) &kernel_start + image_size));
|
2016-01-03 11:15:47 +01:00
|
|
|
|
2015-05-23 14:35:45 +02:00
|
|
|
// add Kernel
|
2017-07-15 16:24:08 +02:00
|
|
|
ret = vma_add(PAGE_2M_FLOOR((size_t) &kernel_start),
|
|
|
|
PAGE_2M_CEIL((size_t) &kernel_start + image_size),
|
2015-05-23 14:35:45 +02:00
|
|
|
VMA_READ|VMA_WRITE|VMA_EXECUTE|VMA_CACHEABLE);
|
|
|
|
if (BUILTIN_EXPECT(ret, 0))
|
|
|
|
goto out;
|
|
|
|
|
2016-11-27 23:21:47 +01:00
|
|
|
// reserve space for the heap
|
|
|
|
ret = vma_add(HEAP_START, HEAP_START+HEAP_SIZE, VMA_NO_ACCESS);
|
|
|
|
if (BUILTIN_EXPECT(ret, 0))
|
|
|
|
goto out;
|
|
|
|
|
2017-03-06 23:32:42 +01:00
|
|
|
// we might move the architecture specific VMA regions to a
|
|
|
|
// seperate function vma_arch_init()
|
|
|
|
ret = vma_arch_init();
|
2016-08-26 23:53:22 +02:00
|
|
|
|
2015-05-23 14:35:45 +02:00
|
|
|
out:
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
size_t vma_alloc(size_t size, uint32_t flags)
|
|
|
|
{
|
2017-09-23 23:27:12 +02:00
|
|
|
spinlock_irqsave_t* lock = &hermit_mm_lock;
|
2016-03-21 00:18:45 +01:00
|
|
|
vma_t** list = &vma_list;
|
2015-05-23 14:35:45 +02:00
|
|
|
|
2016-11-04 12:09:43 +01:00
|
|
|
LOG_DEBUG("vma_alloc: size = %#lx, flags = %#x\n", size, flags);
|
2015-05-23 14:35:45 +02:00
|
|
|
|
2016-03-21 00:18:45 +01:00
|
|
|
// boundaries of free gaps
|
|
|
|
size_t start, end;
|
2015-05-23 14:35:45 +02:00
|
|
|
|
2016-03-21 00:18:45 +01:00
|
|
|
// boundaries for search
|
|
|
|
size_t base = VMA_MIN;
|
|
|
|
size_t limit = VMA_MAX;
|
2015-05-23 14:35:45 +02:00
|
|
|
|
2017-11-05 15:45:21 +01:00
|
|
|
size = PAGE_CEIL(size);
|
|
|
|
|
2016-08-31 13:28:22 +02:00
|
|
|
spinlock_irqsave_lock(lock);
|
2015-05-23 14:35:45 +02:00
|
|
|
|
|
|
|
// first fit search for free memory area
|
|
|
|
vma_t* pred = NULL; // vma before current gap
|
|
|
|
vma_t* succ = *list; // vma after current gap
|
|
|
|
do {
|
|
|
|
start = (pred) ? pred->end : base;
|
|
|
|
end = (succ) ? succ->start : limit;
|
|
|
|
|
|
|
|
if (start + size < end && start >= base && start + size < limit)
|
|
|
|
goto found; // we found a gap which is large enough and in the bounds
|
|
|
|
|
|
|
|
pred = succ;
|
|
|
|
succ = (pred) ? pred->next : NULL;
|
|
|
|
} while (pred || succ);
|
|
|
|
|
|
|
|
fail:
|
2016-08-31 13:28:22 +02:00
|
|
|
spinlock_irqsave_unlock(lock); // we were unlucky to find a free gap
|
2015-05-23 14:35:45 +02:00
|
|
|
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
found:
|
2016-05-26 10:03:13 +02:00
|
|
|
if (pred && pred->flags == flags) {
|
|
|
|
pred->end += size; // resize VMA
|
2016-11-04 12:09:43 +01:00
|
|
|
LOG_DEBUG("vma_alloc: resize vma, start 0x%zx, pred->start 0x%zx, pred->end 0x%zx\n", start, pred->start, pred->end);
|
2016-05-26 10:03:13 +02:00
|
|
|
} else {
|
2015-05-23 14:35:45 +02:00
|
|
|
// insert new VMA
|
|
|
|
vma_t* new = kmalloc(sizeof(vma_t));
|
|
|
|
if (BUILTIN_EXPECT(!new, 0))
|
|
|
|
goto fail;
|
|
|
|
|
|
|
|
new->start = start;
|
|
|
|
new->end = start + size;
|
|
|
|
new->flags = flags;
|
|
|
|
new->next = succ;
|
|
|
|
new->prev = pred;
|
2016-11-27 23:21:47 +01:00
|
|
|
LOG_DEBUG("vma_alloc: create new vma, new->start 0x%zx, new->end 0x%zx\n", new->start, new->end);
|
2015-05-23 14:35:45 +02:00
|
|
|
|
|
|
|
if (succ)
|
|
|
|
succ->prev = new;
|
|
|
|
if (pred)
|
|
|
|
pred->next = new;
|
|
|
|
else
|
|
|
|
*list = new;
|
|
|
|
}
|
|
|
|
|
2016-08-31 13:28:22 +02:00
|
|
|
spinlock_irqsave_unlock(lock);
|
2015-05-23 14:35:45 +02:00
|
|
|
|
|
|
|
return start;
|
|
|
|
}
|
|
|
|
|
|
|
|
int vma_free(size_t start, size_t end)
|
|
|
|
{
|
2017-09-23 23:27:12 +02:00
|
|
|
spinlock_irqsave_t* lock = &hermit_mm_lock;
|
2015-05-23 14:35:45 +02:00
|
|
|
vma_t* vma;
|
2016-03-21 00:18:45 +01:00
|
|
|
vma_t** list = &vma_list;
|
2015-05-23 14:35:45 +02:00
|
|
|
|
2016-11-04 12:09:43 +01:00
|
|
|
LOG_DEBUG("vma_free: start = %#lx, end = %#lx\n", start, end);
|
2015-05-23 14:35:45 +02:00
|
|
|
|
|
|
|
if (BUILTIN_EXPECT(start >= end, 0))
|
|
|
|
return -EINVAL;
|
|
|
|
|
2016-08-31 13:28:22 +02:00
|
|
|
spinlock_irqsave_lock(lock);
|
2015-05-23 14:35:45 +02:00
|
|
|
|
|
|
|
// search vma
|
|
|
|
vma = *list;
|
|
|
|
while (vma) {
|
|
|
|
if (start >= vma->start && end <= vma->end) break;
|
|
|
|
vma = vma->next;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (BUILTIN_EXPECT(!vma, 0)) {
|
2016-08-31 13:28:22 +02:00
|
|
|
spinlock_irqsave_unlock(lock);
|
2015-05-23 14:35:45 +02:00
|
|
|
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)) {
|
2016-08-31 13:28:22 +02:00
|
|
|
spinlock_irqsave_unlock(lock);
|
2015-05-23 14:35:45 +02:00
|
|
|
return -ENOMEM;
|
|
|
|
}
|
|
|
|
|
2016-05-26 10:03:13 +02:00
|
|
|
new->flags = vma->flags;
|
|
|
|
|
2015-05-23 14:35:45 +02:00
|
|
|
new->end = vma->end;
|
|
|
|
vma->end = start;
|
|
|
|
new->start = end;
|
|
|
|
|
|
|
|
new->next = vma->next;
|
|
|
|
vma->next = new;
|
|
|
|
new->prev = vma;
|
|
|
|
}
|
|
|
|
|
2016-08-31 13:28:22 +02:00
|
|
|
spinlock_irqsave_unlock(lock);
|
2015-05-23 14:35:45 +02:00
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
int vma_add(size_t start, size_t end, uint32_t flags)
|
|
|
|
{
|
2017-09-23 23:27:12 +02:00
|
|
|
spinlock_irqsave_t* lock = &hermit_mm_lock;
|
2016-03-21 00:18:45 +01:00
|
|
|
vma_t** list = &vma_list;
|
2016-05-31 04:37:38 +02:00
|
|
|
int ret = 0;
|
2015-05-23 14:35:45 +02:00
|
|
|
|
|
|
|
if (BUILTIN_EXPECT(start >= end, 0))
|
|
|
|
return -EINVAL;
|
|
|
|
|
2016-11-04 12:09:43 +01:00
|
|
|
LOG_DEBUG("vma_add: start = %#lx, end = %#lx, flags = %#x\n", start, end, flags);
|
2015-05-23 14:35:45 +02:00
|
|
|
|
2016-08-31 13:28:22 +02:00
|
|
|
spinlock_irqsave_lock(lock);
|
2015-05-23 14:35:45 +02:00
|
|
|
|
|
|
|
// search gap
|
|
|
|
vma_t* pred = NULL;
|
|
|
|
vma_t* succ = *list;
|
|
|
|
|
|
|
|
while (pred || succ) {
|
|
|
|
if ((!pred || pred->end <= start) &&
|
|
|
|
(!succ || succ->start >= end))
|
|
|
|
break;
|
|
|
|
|
|
|
|
pred = succ;
|
|
|
|
succ = (succ) ? succ->next : NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (BUILTIN_EXPECT(*list && !pred && !succ, 0)) {
|
2016-05-31 04:37:38 +02:00
|
|
|
ret = -EINVAL;
|
|
|
|
goto fail;
|
2015-05-23 14:35:45 +02:00
|
|
|
}
|
|
|
|
|
2016-05-31 04:37:38 +02:00
|
|
|
if (pred && (pred->end == start) && (pred->flags == flags)) {
|
|
|
|
pred->end = end; // resize VMA
|
2017-11-05 15:45:21 +01:00
|
|
|
LOG_DEBUG("vma_add: resize vma, start 0x%zx, pred->start 0x%zx, pred->end 0x%zx\n", start, pred->start, pred->end);
|
2016-05-31 04:37:38 +02:00
|
|
|
} else {
|
|
|
|
// insert new VMA
|
|
|
|
vma_t* new = kmalloc(sizeof(vma_t));
|
|
|
|
if (BUILTIN_EXPECT(!new, 0)) {
|
|
|
|
ret = -ENOMEM;
|
|
|
|
goto fail;
|
|
|
|
}
|
2015-05-23 14:35:45 +02:00
|
|
|
|
2016-05-31 04:37:38 +02:00
|
|
|
new->start = start;
|
|
|
|
new->end = end;
|
|
|
|
new->flags = flags;
|
|
|
|
new->next = succ;
|
|
|
|
new->prev = pred;
|
2017-11-05 15:45:21 +01:00
|
|
|
LOG_DEBUG("vma_add: create new vma, new->start 0x%zx, new->end 0x%zx\n", new->start, new->end);
|
2015-05-23 14:35:45 +02:00
|
|
|
|
2016-05-31 04:37:38 +02:00
|
|
|
if (succ)
|
|
|
|
succ->prev = new;
|
2017-11-05 15:45:21 +01:00
|
|
|
|
2016-05-31 04:37:38 +02:00
|
|
|
if (pred)
|
|
|
|
pred->next = new;
|
|
|
|
else
|
|
|
|
*list = new;
|
|
|
|
}
|
2015-05-23 14:35:45 +02:00
|
|
|
|
2016-05-31 04:37:38 +02:00
|
|
|
fail:
|
2016-08-31 13:28:22 +02:00
|
|
|
spinlock_irqsave_unlock(lock);
|
2015-05-23 14:35:45 +02:00
|
|
|
|
2016-05-31 04:37:38 +02:00
|
|
|
return ret;
|
2015-05-23 14:35:45 +02:00
|
|
|
}
|
|
|
|
|
2017-11-05 15:45:21 +01:00
|
|
|
static void print_vma(vma_t *vma)
|
2015-05-23 14:35:45 +02:00
|
|
|
{
|
2017-11-05 15:45:21 +01:00
|
|
|
while (vma) {
|
|
|
|
LOG_INFO("0x%lx - 0x%lx: size=0x%x, flags=%c%c%c%s\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->flags & VMA_CACHEABLE) ? "" : " (uncached)");
|
|
|
|
vma = vma->next;
|
2015-05-23 14:35:45 +02:00
|
|
|
}
|
2017-11-05 15:45:21 +01:00
|
|
|
}
|
2015-05-23 14:35:45 +02:00
|
|
|
|
2017-11-05 15:45:21 +01:00
|
|
|
void vma_dump(void)
|
|
|
|
{
|
2016-11-04 12:09:43 +01:00
|
|
|
LOG_INFO("VMAs:\n");
|
2017-09-23 23:27:12 +02:00
|
|
|
spinlock_irqsave_lock(&hermit_mm_lock);
|
2017-11-05 15:45:21 +01:00
|
|
|
print_vma(vma_list);
|
2017-09-23 23:27:12 +02:00
|
|
|
spinlock_irqsave_unlock(&hermit_mm_lock);
|
2015-05-23 14:35:45 +02:00
|
|
|
}
|