diff --git a/hermit/arch/x86/include/asm/multiboot.h b/hermit/arch/x86/include/asm/multiboot.h new file mode 100644 index 000000000..298091610 --- /dev/null +++ b/hermit/arch/x86/include/asm/multiboot.h @@ -0,0 +1,148 @@ +/* + * Copyright (c) 2010, Stefan Lankes, 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. + */ + +#ifndef __ARCH_MULTIBOOT_H__ +#define __ARCH_MULTIBOOT_H__ + +#include + +/// Does the bootloader provide mem_* fields? +#define MULTIBOOT_INFO_MEM (1 << 0) +/// Does the bootloader provide the command-line? +#define MULTIBOOT_INFO_CMDLINE (1 << 2) +/// Does the bootloader provide a list of modules? +#define MULTIBOOT_INFO_MODS (1 << 3) +/// Does the bootloader provide a full memory map? +#define MULTIBOOT_INFO_MEM_MAP (1 << 6) + +typedef uint16_t multiboot_uint16_t; +typedef uint32_t multiboot_uint32_t; +typedef uint64_t multiboot_uint64_t; + +/* The symbol table for a.out. */ +struct multiboot_aout_symbol_table +{ + multiboot_uint32_t tabsize; + multiboot_uint32_t strsize; + multiboot_uint32_t addr; + multiboot_uint32_t reserved; +}; +typedef struct multiboot_aout_symbol_table multiboot_aout_symbol_table_t; + +/* The section header table for ELF. */ +struct multiboot_elf_section_header_table +{ + multiboot_uint32_t num; + multiboot_uint32_t size; + multiboot_uint32_t addr; + multiboot_uint32_t shndx; +}; +typedef struct multiboot_elf_section_header_table multiboot_elf_section_header_table_t; + +struct multiboot_info +{ + /** Multiboot info version number */ + multiboot_uint32_t flags; + + /** Available memory from BIOS */ + multiboot_uint32_t mem_lower; + multiboot_uint32_t mem_upper; + + /** "root" partition */ + multiboot_uint32_t boot_device; + + /** Kernel command line */ + multiboot_uint32_t cmdline; + + /** Boot-Module list */ + multiboot_uint32_t mods_count; + multiboot_uint32_t mods_addr; + + union + { + multiboot_aout_symbol_table_t aout_sym; + multiboot_elf_section_header_table_t elf_sec; + } u; + + /** Memory Mapping buffer */ + multiboot_uint32_t mmap_length; + multiboot_uint32_t mmap_addr; + + /** Drive Info buffer */ + multiboot_uint32_t drives_length; + multiboot_uint32_t drives_addr; + + /** ROM configuration table */ + multiboot_uint32_t config_table; + + /** Boot Loader Name */ + multiboot_uint32_t boot_loader_name; + + /** APM table */ + multiboot_uint32_t apm_table; + + /** Video */ + multiboot_uint32_t vbe_control_info; + multiboot_uint32_t vbe_mode_info; + multiboot_uint16_t vbe_mode; + multiboot_uint16_t vbe_interface_seg; + multiboot_uint16_t vbe_interface_off; + multiboot_uint16_t vbe_interface_len; +}; + +typedef struct multiboot_info multiboot_info_t; + +struct multiboot_mmap_entry +{ + multiboot_uint32_t size; + multiboot_uint64_t addr; + multiboot_uint64_t len; +#define MULTIBOOT_MEMORY_AVAILABLE 1 +#define MULTIBOOT_MEMORY_RESERVED 2 + multiboot_uint32_t type; +} __attribute__((packed)); +typedef struct multiboot_mmap_entry multiboot_memory_map_t; + +struct multiboot_mod_list +{ + /** the memory used goes from bytes ’mod start’ to ’mod end-1’ inclusive */ + multiboot_uint32_t mod_start; + multiboot_uint32_t mod_end; + + /** Module command line */ + multiboot_uint32_t cmdline; + + /** padding to take it to 16 bytes (must be zero) */ + multiboot_uint32_t pad; +}; +typedef struct multiboot_mod_list multiboot_module_t; + +/// Pointer to multiboot structure +/// This pointer is declared at set by entry.asm +extern multiboot_info_t* mb_info; + +#endif diff --git a/hermit/arch/x86/kernel/entry.asm b/hermit/arch/x86/kernel/entry.asm index 1512de6f6..71af56764 100644 --- a/hermit/arch/x86/kernel/entry.asm +++ b/hermit/arch/x86/kernel/entry.asm @@ -66,6 +66,7 @@ align 4 global heap_size global header_size global disable_x2apic + global mb_info base dq 0 limit dq 0 cpu_freq dd 0 @@ -86,6 +87,7 @@ align 4 header_start_address dq 0 disable_x2apic dd 1 single_kernel dd 1 + mb_info dq 0 ; Bootstrap page tables are used during the initialization. align 4096 @@ -107,6 +109,9 @@ boot_pgt: SECTION .ktext align 4 start64: + ; store pointer to the multiboot information + mov [mb_info], QWORD rdx + ; reset registers to kill any stale realmode selectors xor eax, eax mov ds, eax @@ -173,7 +178,17 @@ start64: or rax, 0x113 ; set present, global, writable and cache disable bits mov QWORD [rdi], rax %endif - + ; map multiboot info + mov rax, QWORD [mb_info] + and rax, ~0xFFF ; page align lower half + cmp rax, 0 + je Lno_mbinfo + mov rdi, rax + shr rdi, 9 ; (edi >> 12) * 8 (index for boot_pgt) + add rdi, boot_pgt + or rax, 0x103 ; set present, global and writable bits + mov QWORD [rdi], rax +Lno_mbinfo: ; remap kernel mov rdi, kernel_start shr rdi, 18 ; (edi >> 21) * 8 (index for boot_pgd) diff --git a/hermit/arch/x86/loader/main.c b/hermit/arch/x86/loader/main.c index 0e899f13b..9f783f485 100644 --- a/hermit/arch/x86/loader/main.c +++ b/hermit/arch/x86/loader/main.c @@ -54,6 +54,7 @@ void main(void) koutput_init(); kputs("HermitCore loader...\n"); kprintf("Loader starts at %p and ends at %p\n", &kernel_start, &kernel_end); + kprintf("Found mb_info at %p\n", mb_info); page_init(); @@ -169,7 +170,7 @@ void main(void) kprintf("Entry point: 0x%zx\n", header->entry); // jump to the HermitCore app - asm volatile ("jmp *%0" :: "r"(header->entry) : "memory"); + asm volatile ("jmp *%0" :: "r"(header->entry), "d"(mb_info) : "memory"); // we should never reach this point while(1) { HALT; } diff --git a/hermit/mm/memory.c b/hermit/mm/memory.c index 62035f96e..510e98728 100644 --- a/hermit/mm/memory.c +++ b/hermit/mm/memory.c @@ -33,6 +33,7 @@ #include #include +#include extern uint64_t base; extern uint64_t limit; @@ -52,7 +53,7 @@ extern const void kernel_end; static spinlock_t list_lock = SPINLOCK_INIT; -static free_list_t init_list; +static free_list_t init_list = {0, 0, NULL, NULL}; static free_list_t* free_start = &init_list; atomic_int64_t total_pages = ATOMIC_INIT(0); @@ -218,25 +219,105 @@ int memory_init(void) return ret; } + kprintf("mb_info: 0x%zx\n", mb_info); kprintf("memory_init: base 0x%zx, image_size 0x%zx, limit 0x%zx\n", base, image_size, limit); - // determine available memory - atomic_int64_add(&total_pages, (limit-base) >> PAGE_BITS); - atomic_int64_add(&total_available_pages, (limit-base) >> PAGE_BITS); + if (mb_info) { + if (mb_info->flags & MULTIBOOT_INFO_MEM_MAP) { + size_t end_addr, start_addr; + 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 first available memory slot as free + for(; mmap < mmap_end; mmap = (multiboot_memory_map_t*) ((size_t) mmap + sizeof(uint32_t) + mmap->size)) { + if (mmap->type == MULTIBOOT_MEMORY_AVAILABLE) { + start_addr = PAGE_FLOOR(mmap->addr); + end_addr = PAGE_CEIL(mmap->addr + mmap->len); + + kprintf("Free region 0x%zx - 0x%zx\n", start_addr, end_addr); + + if ((start_addr <= base) && (end_addr >= PAGE_2M_FLOOR(base+image_size))) { + init_list.start = PAGE_2M_FLOOR(base+image_size); + init_list.end = end_addr; + + kprintf("Add region 0x%zx - 0x%zx\n", init_list.start, init_list.end); + } + + // determine available memory + atomic_int64_add(&total_pages, (end_addr-start_addr) >> PAGE_BITS); + atomic_int64_add(&total_available_pages, (end_addr-start_addr) >> PAGE_BITS); + } + } + + if (!init_list.end) + goto oom; + } else { + goto oom; + } + } else { + // determine available memory + atomic_int64_add(&total_pages, (limit-base) >> PAGE_BITS); + atomic_int64_add(&total_available_pages, (limit-base) >> PAGE_BITS); + + //initialize free list + init_list.start = PAGE_2M_FLOOR(base + image_size); + init_list.end = limit; + } // determine allocated memory, we use 2MB pages to map the kernel atomic_int64_add(&total_allocated_pages, PAGE_2M_FLOOR(image_size) >> PAGE_BITS); atomic_int64_sub(&total_available_pages, PAGE_2M_FLOOR(image_size) >> PAGE_BITS); - //initialize free list - init_list.start = PAGE_2M_FLOOR(base + image_size); - init_list.end = limit; - init_list.prev = init_list.next = NULL; kprintf("free list starts at 0x%zx, limit 0x%zx\n", init_list.start, init_list.end); ret = vma_init(); if (BUILTIN_EXPECT(ret, 0)) kprintf("Failed to initialize VMA regions: %d\n", ret); + // add missing free regions + if (mb_info) { + if (mb_info->flags & MULTIBOOT_INFO_MEM_MAP) { + free_list_t* last = &init_list; + size_t end_addr, start_addr; + 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 + for(; mmap < mmap_end; mmap = (multiboot_memory_map_t*) ((size_t) mmap + sizeof(uint32_t) + mmap->size)) + { + if (mmap->type == MULTIBOOT_MEMORY_AVAILABLE) { + start_addr = PAGE_FLOOR(mmap->addr); + end_addr = PAGE_CEIL(mmap->addr + mmap->len); + + if ((start_addr <= base) && (end_addr >= PAGE_2M_FLOOR(base+image_size))) + end_addr = base; + + // ignore everything below 1M => reserve for I/O devices + if ((start_addr < (size_t) 0x100000)) + start_addr = 0x100000; + + if (start_addr >= end_addr) + continue; + + last->next = kmalloc(sizeof(free_list_t)); + if (BUILTIN_EXPECT(!last->next, 0)) + goto oom; + + kprintf("Add region 0x%zx - 0x%zx\n", start_addr, end_addr); + + last->next->prev = last; + last = last->next; + last->next = NULL; + last->start = start_addr; + last->end = end_addr; + } + } + } + } + return ret; + +oom: + kprintf("BUG: Failed to init mm!\n"); + while(1) {HALT; } } diff --git a/hermit/mm/vma.c b/hermit/mm/vma.c index 332f29b76..c22af9445 100644 --- a/hermit/mm/vma.c +++ b/hermit/mm/vma.c @@ -31,6 +31,7 @@ #include #include #include +#include /* * Note that linker symbols are not variables, they have no memory allocated for @@ -73,6 +74,12 @@ int vma_init(void) goto out; #endif + if (mb_info) { + ret = vma_add((size_t)mb_info, (size_t)mb_info + PAGE_SIZE, VMA_READ|VMA_WRITE); + if (BUILTIN_EXPECT(ret, 0)) + goto out; + } + out: return ret; }