arch/i386: Add multiboot support
This commit is contained in:
parent
62755b8f38
commit
b98aa76334
119
arch/i386/include/arch/multiboot.h
Normal file
119
arch/i386/include/arch/multiboot.h
Normal file
@ -0,0 +1,119 @@
|
||||
/* multiboot.h - the header for Multiboot */
|
||||
/* Copyright (C) 1999, 2001 Free Software Foundation, Inc.
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation; either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program; if not, write to the Free Software
|
||||
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
|
||||
|
||||
/* Macros. */
|
||||
|
||||
/* The magic number for the Multiboot header. */
|
||||
#define MULTIBOOT_HEADER_MAGIC 0x1BADB002
|
||||
|
||||
/* The flags for the Multiboot header. */
|
||||
#ifdef __ELF__
|
||||
# define MULTIBOOT_HEADER_FLAGS 0x00000003
|
||||
#else
|
||||
# define MULTIBOOT_HEADER_FLAGS 0x00010003
|
||||
#endif
|
||||
|
||||
/* The magic number passed by a Multiboot-compliant boot loader. */
|
||||
#define MULTIBOOT_BOOTLOADER_MAGIC 0x2BADB002
|
||||
|
||||
/* The size of our stack (16KB). */
|
||||
#define STACK_SIZE 0x4000
|
||||
|
||||
/* C symbol format. HAVE_ASM_USCORE is defined by configure. */
|
||||
#ifdef HAVE_ASM_USCORE
|
||||
# define EXT_C(sym) _ ## sym
|
||||
#else
|
||||
# define EXT_C(sym) sym
|
||||
#endif
|
||||
|
||||
#ifndef ASM
|
||||
/* Do not include here in boot.S. */
|
||||
|
||||
/* Types. */
|
||||
|
||||
/* The Multiboot header. */
|
||||
typedef struct multiboot_header
|
||||
{
|
||||
unsigned long magic;
|
||||
unsigned long flags;
|
||||
unsigned long checksum;
|
||||
unsigned long header_addr;
|
||||
unsigned long load_addr;
|
||||
unsigned long load_end_addr;
|
||||
unsigned long bss_end_addr;
|
||||
unsigned long entry_addr;
|
||||
} multiboot_header_t;
|
||||
|
||||
/* The symbol table for a.out. */
|
||||
typedef struct aout_symbol_table
|
||||
{
|
||||
unsigned long tabsize;
|
||||
unsigned long strsize;
|
||||
unsigned long addr;
|
||||
unsigned long reserved;
|
||||
} aout_symbol_table_t;
|
||||
|
||||
/* The section header table for ELF. */
|
||||
typedef struct elf_section_header_table
|
||||
{
|
||||
unsigned long num;
|
||||
unsigned long size;
|
||||
unsigned long addr;
|
||||
unsigned long shndx;
|
||||
} elf_section_header_table_t;
|
||||
|
||||
/* The Multiboot information. */
|
||||
typedef struct multiboot_info
|
||||
{
|
||||
unsigned long flags;
|
||||
unsigned long mem_lower;
|
||||
unsigned long mem_upper;
|
||||
unsigned long boot_device;
|
||||
unsigned long cmdline;
|
||||
unsigned long mods_count;
|
||||
unsigned long mods_addr;
|
||||
union
|
||||
{
|
||||
aout_symbol_table_t aout_sym;
|
||||
elf_section_header_table_t elf_sec;
|
||||
} u;
|
||||
unsigned long mmap_length;
|
||||
unsigned long mmap_addr;
|
||||
} multiboot_info_t;
|
||||
|
||||
/* The module structure. */
|
||||
typedef struct module
|
||||
{
|
||||
unsigned long mod_start;
|
||||
unsigned long mod_end;
|
||||
unsigned long string;
|
||||
unsigned long reserved;
|
||||
} module_t;
|
||||
|
||||
/* The memory map. Be careful that the offset 0 is base_addr_low
|
||||
but no size. */
|
||||
typedef struct memory_map
|
||||
{
|
||||
unsigned long size;
|
||||
unsigned long base_addr_low;
|
||||
unsigned long base_addr_high;
|
||||
unsigned long length_low;
|
||||
unsigned long length_high;
|
||||
unsigned long type;
|
||||
} memory_map_t;
|
||||
|
||||
#endif /* ! ASM */
|
@ -4,6 +4,7 @@ SECTIONS
|
||||
{
|
||||
. = 0xc0100000;
|
||||
kernel_start = .;
|
||||
kernel_start_phys = . - 0xc0000000;
|
||||
.text ALIGN(0x1000) : AT(ADDR(.text) - 0xc0000000) {
|
||||
*(.text*) *(.rodata*)
|
||||
}
|
||||
@ -24,4 +25,5 @@ SECTIONS
|
||||
*(.bss*) *(COMMON*)
|
||||
}
|
||||
kernel_end = .;
|
||||
kernel_end_phys = . - 0xc0000000;
|
||||
}
|
||||
|
@ -3,7 +3,8 @@ SUBDIRS :=
|
||||
|
||||
include $(BASEDIR)/header.mk
|
||||
|
||||
OBJS_$(d) := $(d)/mmu.o
|
||||
OBJS_$(d) := $(d)/mmu.o \
|
||||
$(d)/multiboot.o
|
||||
|
||||
KOBJS += $(OBJS_$(d))
|
||||
|
||||
|
@ -22,9 +22,101 @@
|
||||
#include <types.h>
|
||||
#include <mm.h>
|
||||
|
||||
extern uint32 kernel_start_phys, kernel_end_phys;
|
||||
extern uint32 kernel_start, kernel_end;
|
||||
|
||||
#define PAGE_SIZE 0x00400000
|
||||
#define ROUND_DOWN_PAGE_SIZE(addr) ((typeof(addr))((uint32)(addr) & 0xff800000))
|
||||
|
||||
static inline int page_intersects(uint32 *page_start, uint32 *lower, uint32 *upper) {
|
||||
return (lower >= page_start && lower < (page_start + PAGE_SIZE)) ||
|
||||
(upper >= page_start && upper < (page_start + PAGE_SIZE)) ||
|
||||
(lower < page_start && upper >= (page_start + PAGE_SIZE));
|
||||
}
|
||||
|
||||
void mmu_reinit() {
|
||||
int virt_phys_offset; /* CAUTION: 1 = 4 bytes */
|
||||
uint32 *curr_tbl_entry;
|
||||
uint32 *curr_addr;
|
||||
uint32 *page_dir;
|
||||
asm("movl %%cr3, %0" : "=r"(page_dir) : : );
|
||||
|
||||
virt_phys_offset = &kernel_start - &kernel_start_phys;
|
||||
curr_tbl_entry = page_dir + virt_phys_offset;
|
||||
|
||||
//do first loop iteration outside th eloop, because we have to check against wrapping back around to know we're done
|
||||
*curr_tbl_entry = 0x83;
|
||||
curr_tbl_entry++;
|
||||
|
||||
//create identity mapping for entire address space using sections.
|
||||
//BUT, if we've relocated the kernel from where it is in physical
|
||||
//memory, make sure we keep those mappings correct, and we'll actually
|
||||
//swap the two mappings so all of memory is addressable.
|
||||
for (curr_addr = (uint32 *)PAGE_SIZE; curr_addr != 0; curr_addr += (PAGE_SIZE>>2)) {
|
||||
if (page_intersects(curr_addr, &kernel_start_phys, &kernel_end_phys)) {
|
||||
*curr_tbl_entry = (uint32)ROUND_DOWN_PAGE_SIZE(curr_addr + virt_phys_offset) | 0x83;
|
||||
} else if (page_intersects(curr_addr, &kernel_start, &kernel_end)) {
|
||||
*curr_tbl_entry = (uint32)ROUND_DOWN_PAGE_SIZE(curr_addr - virt_phys_offset) | 0x83;
|
||||
} else {
|
||||
*curr_tbl_entry = (uint32)curr_addr | 0x83;
|
||||
}
|
||||
/* Force the entries to reload */
|
||||
asm("invlpg (%0)" : : "r"(curr_addr) : );
|
||||
curr_tbl_entry++;
|
||||
}
|
||||
}
|
||||
|
||||
//TODO merge the rest of this file with the similar section in arch/arm. This is clearly mostly duplicated code.
|
||||
|
||||
int mmu_region_contains(void *lower_a, void *upper_a, void *lower_b, void *upper_b) {
|
||||
return lower_b >= lower_a && upper_b <= upper_a;
|
||||
}
|
||||
int mmu_region_contains_single(void *lower_a, void *upper_a, void *ptr) {
|
||||
return lower_a <= ptr && ptr <= upper_a;
|
||||
}
|
||||
|
||||
#define section_round_down(ptr) (((uint32)ptr) & ~(PAGE_SIZE-1))
|
||||
#define section_round_up(ptr) (((((uint32)ptr) & ~1) + (PAGE_SIZE-1) ) & ~(PAGE_SIZE-1))
|
||||
|
||||
/* Called once per physical memory region by bootup code. This function is
|
||||
* responsible for only adding (via mm_add_free_region()) those parts of the
|
||||
* memory region which are still available (i.e. aren't in the kernel and
|
||||
* haven't been remapped anywhere else. */
|
||||
void declare_memory_region(void *lower, void *upper) {
|
||||
(void)lower;
|
||||
(void)upper;
|
||||
void *k_section_start_phys = (void *)section_round_down(&kernel_start_phys);
|
||||
void *k_section_end_phys = (void *)(section_round_up(&kernel_end_phys) - 1);
|
||||
void *k_section_start_virt = (void *)section_round_down(&kernel_start);
|
||||
void *k_section_end_virt = (void *)(section_round_up(&kernel_end) - 1);
|
||||
|
||||
if (upper - lower < 1) {
|
||||
print("Warning: declare_memory_region() called with lower=%x, upper=%x. Ignoring.\n", lower, upper);
|
||||
return;
|
||||
}
|
||||
|
||||
//TODO It's possible (though highly unlikely) that the kernel (virtual)
|
||||
//is split across two different memory regions. We should probably
|
||||
//handle this.
|
||||
if (mmu_region_contains(lower, upper, k_section_start_phys, k_section_end_phys)) {
|
||||
//Don't map any of the physical kernel's memory
|
||||
declare_memory_region(lower, (void *) ((char *)k_section_start_phys - 1));
|
||||
declare_memory_region((void *) ((char *)k_section_end_phys + 1), upper);
|
||||
mm_add_free_region(&kernel_end, k_section_end_virt);
|
||||
} else if (mmu_region_contains(lower, upper, k_section_start_virt, k_section_end_virt)) {
|
||||
declare_memory_region(lower, (void *) ((char *)k_section_start_virt - 1));
|
||||
declare_memory_region((void *) ((char *)k_section_end_virt + 1), upper);
|
||||
} else if (mmu_region_contains_single(lower, upper, k_section_start_phys)) {
|
||||
if ((void*)((char*)lower + 1) < k_section_start_phys)
|
||||
declare_memory_region(lower, (void *) ((char *)k_section_start_phys - 1));
|
||||
} else if (mmu_region_contains_single(lower, upper, k_section_end_phys)) {
|
||||
if (k_section_end_phys < (void*)((char*)upper - 1))
|
||||
declare_memory_region((void *) ((char *)k_section_end_phys + 1), upper);
|
||||
} else if (mmu_region_contains_single(lower, upper, k_section_start_virt)) {
|
||||
if ((void*)((char*)lower + 1) < k_section_start_virt)
|
||||
declare_memory_region(lower, (void *) ((char *)k_section_start_virt - 1));
|
||||
} else if (mmu_region_contains_single(lower, upper, k_section_end_virt)) {
|
||||
if (k_section_end_virt < (void*)((char*)upper - 1))
|
||||
declare_memory_region((void *) ((char *)k_section_end_virt + 1), upper);
|
||||
} else {
|
||||
mm_add_free_region(lower, upper);
|
||||
}
|
||||
}
|
||||
|
64
arch/i386/kernel/multiboot.c
Normal file
64
arch/i386/kernel/multiboot.c
Normal file
@ -0,0 +1,64 @@
|
||||
/*
|
||||
Copyright (C) 2012, Aaron Lindsay <aaron@aclindsay.com>
|
||||
|
||||
This file is part of Aedrix.
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation; either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License along
|
||||
with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*/
|
||||
|
||||
#include <arch/multiboot.h>
|
||||
#include <mmu.h>
|
||||
|
||||
struct multiboot_mmap {
|
||||
unsigned int size;
|
||||
unsigned int base_addr_low, base_addr_high;
|
||||
unsigned int length_low, length_high;
|
||||
unsigned int type;
|
||||
};
|
||||
|
||||
extern multiboot_info_t* multiboot_mbd;
|
||||
extern uint32 multiboot_magic;
|
||||
|
||||
int detect_memory_mmap() {
|
||||
struct multiboot_mmap* mmap = (struct multiboot_mmap*)multiboot_mbd->mmap_addr;
|
||||
int found_region = 0;
|
||||
|
||||
while ((char*)mmap < (char*)multiboot_mbd->mmap_addr + multiboot_mbd->mmap_length) {
|
||||
if (mmap->base_addr_high || mmap->length_high)
|
||||
return -1;
|
||||
if (mmap->type == 0x1) { /* ignore non-usable memory */
|
||||
declare_memory_region((void*)mmap->base_addr_low,
|
||||
(void*)(mmap->base_addr_low + mmap->length_low));
|
||||
found_region = 1;
|
||||
}
|
||||
mmap = (struct multiboot_mmap*) ((unsigned int)mmap + mmap->size + sizeof(unsigned int));
|
||||
}
|
||||
|
||||
return !found_region;
|
||||
}
|
||||
|
||||
int detect_memory_contiguous() {
|
||||
declare_memory_region((void*)0x0, (void*)((uint32)multiboot_mbd->mem_lower * 1024));
|
||||
declare_memory_region((void*)0x1000000, (void*)((uint32)multiboot_mbd->mem_upper*1024 + 0x100000));
|
||||
return 0;
|
||||
}
|
||||
|
||||
int detect_memory() {
|
||||
if (multiboot_mbd->flags & (1<<6))
|
||||
return detect_memory_mmap();
|
||||
else if (multiboot_mbd->flags & (1<<0))
|
||||
return detect_memory_contiguous();
|
||||
return -1;
|
||||
}
|
@ -47,8 +47,8 @@
|
||||
*/
|
||||
.global start
|
||||
start:
|
||||
movl %eax, virt_to_phys(magic) /* Save off multiboot magic number */
|
||||
movl %ebx, virt_to_phys(mbd) /* Save off multiboot data structure */
|
||||
movl %eax, virt_to_phys(multiboot_magic)/* Save off multiboot magic number */
|
||||
movl %ebx, virt_to_phys(multiboot_mbd) /* Save off multiboot data structure */
|
||||
|
||||
movl $virt_to_phys(init_page_dir), %eax /* load page directory base register */
|
||||
movl %eax, %cr3
|
||||
@ -134,10 +134,10 @@ init_page_dir:
|
||||
.lcomm stack, STACK_SIZE /* reserve stack space on a doubleword boundary */
|
||||
|
||||
/* place to store multiboot header information */
|
||||
.global mbd
|
||||
.comm mbd, 4 /* we will use this in i386_main */
|
||||
.global magic
|
||||
.comm magic, 4 /* we will use this in i386_main */
|
||||
.global multiboot_mbd
|
||||
.comm multiboot_mbd, 4 /* we will use this in i386_main */
|
||||
.global multiboot_magic
|
||||
.comm multiboot_magic, 4 /* we will use this in i386_main */
|
||||
|
||||
/* TODO FIXME remove this - needs fix to dependencies first */
|
||||
.global atags_ptr
|
||||
|
Loading…
Reference in New Issue
Block a user