/* * 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 #include #include #include #include #include #include #include #include #ifdef CONFIG_MULTIBOOT #include #endif #ifdef CONFIG_ROCKCREEK #include #include #include #include #endif /* * Set whole address space as occupied: * 0 => free, 1 => occupied */ 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; 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)) kprintf("page_clear_mark(%u): already unmarked\n", i); bitmap[index] = bitmap[index] & ~(1 << mod); } 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= 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> PAGE_SHIFT; spinlock_lock(&bitmap_lock); for (index=0; indexflags & 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> 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; addrmods_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) // 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); 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); } // 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; } // 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) { 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); 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; imods_count; i++) { 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> PAGE_SHIFT); atomic_int32_inc(&total_allocated_pages); atomic_int32_dec(&total_available_pages); } } } } #elif defined(CONFIG_ROCKCREEK) /* * 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); // define the residual private slots as free for(addr=1*0x1000000; 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; addraddr+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++; phyaddr = get_pages(npages); if (BUILTIN_EXPECT(!phyaddr, 0)) return 0; viraddr = map_region(0, phyaddr, npages, flags); return (void*) viraddr; } void* kmalloc(size_t sz) { return mem_allocation(sz, MAP_KERNEL_SPACE); } 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> PAGE_SHIFT; page_clear_mark(index); } spinlock_unlock(&bitmap_lock); 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; }