arch/i386: Add multiboot support
This commit is contained in:
		
							
								
								
									
										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
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user