/* * 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 void page_set_mark(size_t i) { size_t index = i >> 3; size_t mod = i & 0x7; 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; bitmap[index] = bitmap[index] & ~(1 << mod); } size_t get_pages(uint32_t npages) { // first page is reserved static size_t start = 1; size_t i, j, l; size_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_BITS; spinlock_lock(&bitmap_lock); for (i=0; iflags & 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_BITS); 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_BITS); 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_BITS); 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_BITS); 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_BITS); 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_BITS); 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_BITS); atomic_int32_inc(&total_allocated_pages); atomic_int32_dec(&total_available_pages); } #if MAX_CORES > 1 page_set_mark(SMP_SETUP_ADDR >> PAGE_BITS); 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(PAGE_CEIL((size_t) &kernel_start), PAGE_FLOOR((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); #ifdef CONFIG_VGA // add VGA to VMA list vma_add(PAGE_CEIL(VIDEO_MEM_ADDR), PAGE_FLOOR(VIDEO_MEM_ADDR) + PAGE_SIZE, VMA_READ|VMA_WRITE); #endif #if MAX_CORES > 1 // reserve page for SMP boot code vma_add(PAGE_CEIL(SMP_SETUP_ADDR), PAGE_FLOOR(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(PAGE_CEIL((size_t) mb_info), PAGE_FLOOR((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(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); for(i=0; imods_count; i++) { vma_add(PAGE_CEIL(mmodule[i].mod_start), PAGE_FLOOR(mmodule[i].mod_end), VMA_READ|VMA_WRITE|VMA_CACHEABLE); for(addr=mmodule[i].mod_start; addr> PAGE_BITS); 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_BITS); 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_BITS); atomic_int32_inc(&total_allocated_pages); atomic_int32_dec(&total_available_pages); } #endif return ret; }