metalsvm/mm/memory.c

424 lines
10 KiB
C
Raw Permalink Normal View History

/*
* Copyright 2010 Stefan Lankes, Chair for Operating Systems,
* 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.
*/
#include <metalsvm/stdio.h>
#include <metalsvm/string.h>
#include <metalsvm/stdlib.h>
#include <metalsvm/mmu.h>
#include <metalsvm/spinlock.h>
#include <metalsvm/time.h>
#include <metalsvm/processor.h>
#include <metalsvm/page.h>
#include <metalsvm/errno.h>
#ifdef CONFIG_MULTIBOOT
#include <asm/multiboot.h>
#endif
#ifdef CONFIG_ROCKCREEK
#include <asm/RCCE.h>
#include <asm/RCCE_lib.h>
#include <asm/SCC_API.h>
#include <asm/icc.h>
#endif
/*
2013-11-18 15:47:26 +01:00
* Set whole address space as occupied:
* 0 => free, 1 => occupied
*/
2013-11-18 15:47:26 +01:00
static uint8_t bitmap[BITMAP_SIZE] = {[0 ... BITMAP_SIZE-1] = 0xFF};
static spinlock_t bitmap_lock = SPINLOCK_INIT;
atomic_int32_t total_pages = ATOMIC_INIT(0);
atomic_int32_t total_allocated_pages = ATOMIC_INIT(0);
atomic_int32_t total_available_pages = ATOMIC_INIT(0);
/*
* 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;
inline static int page_marked(size_t i)
{
size_t index = i >> 3;
size_t mod = i & 0x7;
return (bitmap[index] & (1 << mod));
}
inline static int page_unmarked(size_t i)
{
return !page_marked(i);
}
inline static void page_set_mark(size_t i)
{
size_t index = i >> 3;
size_t mod = i & 0x7;
2013-11-18 15:47:26 +01:00
if (page_marked(i))
kprintf("page_set_mark(%u): already marked\n", i);
bitmap[index] = bitmap[index] | (1 << mod);
}
inline static void page_clear_mark(size_t i)
{
size_t index = i / 8;
size_t mod = i % 8;
if (page_unmarked(i))
2013-11-18 15:47:26 +01:00
kprintf("page_clear_mark(%u): already unmarked\n", i);
bitmap[index] = bitmap[index] & ~(1 << mod);
}
2013-11-18 15:47:26 +01:00
size_t get_pages(uint32_t npages)
{
// skip first page
static size_t start = 1;
uint32_t i, j, l;
uint32_t k = 0;
size_t ret = 0;
if (BUILTIN_EXPECT(!npages, 0))
return ret;
if (BUILTIN_EXPECT(npages > atomic_int32_read(&total_available_pages), 0))
return ret;
spinlock_lock(&bitmap_lock);
i = start;
next_try:
while((k < BITMAP_SIZE) && page_marked(i)) {
k++;
i = (i+1) & (BITMAP_SIZE-1);
}
if (k >= BITMAP_SIZE)
goto oom;
for(j=1; (j<npages) && (i+j < BITMAP_SIZE) && (k < BITMAP_SIZE); j++, k++) {
if (page_marked(i+j)) {
i = (i+j) & (BITMAP_SIZE-1);
goto next_try;
}
}
if (i+j >= BITMAP_SIZE) {
i = 1;
goto next_try;
}
if (k >= BITMAP_SIZE)
goto oom;
ret = i*PAGE_SIZE;
kprintf("get_pages: ret 0x%x, i = %d, j = %d, npages = %d\n", ret, i, j, npages); // TODO: remove
for(l=i; l<i+j; l++)
page_set_mark(l);
start = i+j;
spinlock_unlock(&bitmap_lock);
atomic_int32_add(&total_allocated_pages, npages);
atomic_int32_sub(&total_available_pages, npages);
return ret;
oom:
spinlock_unlock(&bitmap_lock);
return ret;
}
int put_pages(size_t phyaddr, size_t npages)
{
if (BUILTIN_EXPECT(!phyaddr || !npages, 0))
return -EINVAL;
uint32_t index;
uint32_t base = phyaddr >> PAGE_SHIFT;
spinlock_lock(&bitmap_lock);
for (index=0; index<npages; index++)
page_clear_mark(base+index);
spinlock_unlock(&bitmap_lock);
atomic_int32_sub(&total_allocated_pages, npages);
atomic_int32_add(&total_available_pages, npages);
return 0;
}
int mmu_init(void)
{
unsigned int i;
2011-03-25 20:28:43 +01:00
size_t addr;
int ret = 0;
#ifdef CONFIG_MULTIBOOT
if (mb_info) {
if (mb_info->flags & MULTIBOOT_INFO_MEM_MAP) {
multiboot_memory_map_t* mmap = (multiboot_memory_map_t*) ((size_t) mb_info->mmap_addr);
multiboot_memory_map_t* mmap_end = (void*) ((size_t) mb_info->mmap_addr + mb_info->mmap_length);
// mark available memory as free
while (mmap < mmap_end) {
if (mmap->type == MULTIBOOT_MEMORY_AVAILABLE) {
for (addr=mmap->addr; addr < mmap->addr + mmap->len; addr += PAGE_SIZE) {
page_clear_mark(addr >> PAGE_SHIFT);
atomic_int32_inc(&total_pages);
atomic_int32_inc(&total_available_pages);
}
}
mmap++;
}
}
else if (mb_info->flags & MULTIBOOT_INFO_MEM) {
size_t page;
size_t pages_lower = mb_info->mem_lower >> 2;
size_t pages_upper = mb_info->mem_upper >> 2;
for (page=0; page<pages_lower; page++)
page_clear_mark(page);
for (page=0x100000; page<pages_upper+0x100000; page++)
page_clear_mark(page);
atomic_int32_add(&total_pages, pages_lower + pages_upper);
atomic_int32_add(&total_available_pages, pages_lower + pages_upper);
}
else {
kputs("Unable to initialize the memory management subsystem\n");
while (1) HALT;
}
// mark mb_info as used
page_set_mark((size_t) mb_info >> PAGE_SHIFT);
atomic_int32_inc(&total_allocated_pages);
atomic_int32_dec(&total_available_pages);
// mark modules list as used
if (mb_info->flags & MULTIBOOT_INFO_MODS) {
for(addr=mb_info->mods_addr; addr<mb_info->mods_addr+mb_info->mods_count*sizeof(multiboot_module_t); addr+=PAGE_SIZE) {
page_set_mark(addr >> PAGE_SHIFT);
atomic_int32_inc(&total_allocated_pages);
atomic_int32_dec(&total_available_pages);
}
}
}
#elif defined(CONFIG_ROCKCREEK)
2013-10-16 13:42:58 +02:00
// of course, the first slots belong to the private memory
for(addr=0x00; addr<1*0x1000000; addr+=PAGE_SIZE) {
page_clear_mark(addr >> PAGE_SHIFT);
2011-03-25 20:28:43 +01:00
if (addr > addr + PAGE_SIZE)
break;
atomic_int32_inc(&total_pages);
atomic_int32_inc(&total_available_pages);
}
// Note: The last slot belongs always to the private memory.
for(addr=0xFF000000; addr<0xFFFFFFFF; addr+=PAGE_SIZE) {
page_clear_mark(addr >> PAGE_SHIFT);
if (addr > addr + PAGE_SIZE)
break;
atomic_int32_inc(&total_pages);
atomic_int32_inc(&total_available_pages);
}
2013-10-16 13:42:58 +02:00
// mark the bootinfo as used.
page_set_mark((size_t)bootinfo >> PAGE_SHIFT);
atomic_int32_inc(&total_allocated_pages);
atomic_int32_dec(&total_available_pages);
#else
#error Currently, MetalSVM supports only the Multiboot specification or the RockCreek processor!
#endif
// mark kernel as used
for(addr=(size_t) &kernel_start; addr<(size_t) &kernel_end; addr+=PAGE_SIZE) {
page_set_mark(addr >> PAGE_SHIFT);
atomic_int32_inc(&total_allocated_pages);
atomic_int32_dec(&total_available_pages);
}
#if MAX_CORES > 1
page_set_mark(SMP_SETUP_ADDR >> PAGE_SHIFT);
atomic_int32_inc(&total_allocated_pages);
atomic_int32_dec(&total_available_pages);
#endif
// enable paging and map SMP, VGA, Multiboot modules etc.
ret = paging_init();
if (ret) {
kprintf("Failed to initialize paging: %d\n", ret);
return ret;
}
2013-11-20 11:27:49 +01:00
// add kernel to VMA list
vma_add((size_t) &kernel_start & PAGE_MASK,
PAGE_ALIGN((size_t) &kernel_end),
VMA_READ|VMA_WRITE|VMA_EXECUTE|VMA_CACHEABLE);
// add LAPIC tp VMA list
vma_add((size_t) &kernel_start - PAGE_SIZE,
(size_t) &kernel_start,
VMA_READ|VMA_WRITE);
#if MAX_CORES > 1
// reserve page for SMP boot code
vma_add(SMP_SETUP_ADDR & PAGE_MASK,
PAGE_ALIGN(SMP_SETUP_ADDR + PAGE_SIZE),
VMA_READ|VMA_WRITE|VMA_EXECUTE|VMA_CACHEABLE);
#endif
#ifdef CONFIG_MULTIBOOT
/*
* Modules like the init ram disk are already loaded.
* Therefore, we set these pages as used.
*/
if (mb_info) {
2013-11-20 11:27:49 +01:00
vma_add((size_t) mb_info & PAGE_MASK,
PAGE_ALIGN((size_t) mb_info + sizeof(multiboot_info_t)),
VMA_READ|VMA_CACHEABLE);
if (mb_info->flags & MULTIBOOT_INFO_MODS) {
multiboot_module_t* mmodule = (multiboot_module_t*) ((size_t) mb_info->mods_addr);
2013-11-20 11:27:49 +01:00
vma_add((size_t) mb_info->mods_addr & PAGE_MASK,
PAGE_ALIGN((size_t) mb_info->mods_addr + mb_info->mods_count*sizeof(multiboot_module_t)),
VMA_READ|VMA_CACHEABLE);
for(i=0; i<mb_info->mods_count; i++) {
2013-11-20 11:27:49 +01:00
vma_add(PAGE_ALIGN(mmodule[i].mod_start),
PAGE_ALIGN(mmodule[i].mod_end),
VMA_READ|VMA_WRITE|VMA_CACHEABLE);
for(addr=mmodule[i].mod_start; addr<mmodule[i].mod_end; addr+=PAGE_SIZE) {
page_set_mark(addr >> PAGE_SHIFT);
atomic_int32_inc(&total_allocated_pages);
atomic_int32_dec(&total_available_pages);
}
}
}
}
#elif defined(CONFIG_ROCKCREEK)
/*
2011-06-28 13:45:12 -07:00
* Now, we are able to read the FPGA registers and to
* determine the number of slots for private memory.
*/
uint32_t slots = *((volatile uint8_t*) (FPGA_BASE + 0x8244));
if (slots == 0)
slots = 1;
kprintf("MetalSVM use %d slots for private memory\n", slots);
2011-06-28 13:45:12 -07:00
// define the residual private slots as free
for(addr=1*0x1000000; addr<slots*0x1000000; addr+=PAGE_SIZE) {
page_clear_mark(addr >> PAGE_SHIFT);
if (addr > addr + PAGE_SIZE)
break;
atomic_int32_inc(&total_pages);
atomic_int32_inc(&total_available_pages);
}
/*
* The init ram disk are already loaded.
* Therefore, we set these pages as used.
*/
for(addr=bootinfo->addr; addr<bootinfo->addr+bootinfo->size; addr+=PAGE_SIZE) {
// this area is already mapped, so we need to virt_to_phys() these addresses.
page_set_mark(virt_to_phys(addr) >> PAGE_SHIFT);
atomic_int32_inc(&total_allocated_pages);
atomic_int32_dec(&total_available_pages);
}
#endif
return ret;
}
void* mem_allocation(size_t sz, uint32_t flags)
{
size_t phyaddr, viraddr;
uint32_t npages = sz >> PAGE_SHIFT;
if (sz & (PAGE_SIZE-1))
npages++;
2011-02-24 10:15:58 +01:00
phyaddr = get_pages(npages);
if (BUILTIN_EXPECT(!phyaddr, 0))
return 0;
2011-02-24 10:15:58 +01:00
viraddr = map_region(0, phyaddr, npages, flags);
return (void*) viraddr;
}
void* kmalloc(size_t sz)
{
2011-04-22 09:31:33 +02:00
return mem_allocation(sz, MAP_KERNEL_SPACE);
}
2011-02-24 10:15:58 +01:00
void kfree(void* addr, size_t sz)
{
uint32_t index, npages, i;
size_t phyaddr;
if (BUILTIN_EXPECT(!addr && !sz, 0))
return;
npages = sz >> PAGE_SHIFT;
if (sz & (PAGE_SIZE-1))
npages++;
spinlock_lock(&bitmap_lock);
for(i=0; i<npages; i++) {
unmap_region((size_t) addr+i*PAGE_SIZE, 1);
2011-02-24 10:15:58 +01:00
phyaddr = virt_to_phys((size_t) addr+i*PAGE_SIZE);
if (!phyaddr)
continue;
index = phyaddr >> PAGE_SHIFT;
page_clear_mark(index);
}
spinlock_unlock(&bitmap_lock);
2011-02-24 10:15:58 +01:00
vm_free((size_t) addr, npages);
atomic_int32_sub(&total_allocated_pages, npages);
atomic_int32_add(&total_available_pages, npages);
}
void* create_stack(void)
{
return kmalloc(KERNEL_STACK_SIZE);
}
int destroy_stack(task_t* task)
{
if (BUILTIN_EXPECT(!task || !task->stack, 0))
return -EINVAL;
kfree(task->stack, KERNEL_STACK_SIZE);
return 0;
}