Browse Source

arch/i386: Add multiboot support

Aaron Lindsay 7 years ago
parent
commit
b98aa76334

+ 119 - 0
arch/i386/include/arch/multiboot.h

@@ -0,0 +1,119 @@
1
+/* multiboot.h - the header for Multiboot */
2
+/* Copyright (C) 1999, 2001  Free Software Foundation, Inc.
3
+
4
+   This program is free software; you can redistribute it and/or modify
5
+   it under the terms of the GNU General Public License as published by
6
+   the Free Software Foundation; either version 2 of the License, or
7
+   (at your option) any later version.
8
+
9
+   This program is distributed in the hope that it will be useful,
10
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
11
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12
+   GNU General Public License for more details.
13
+
14
+   You should have received a copy of the GNU General Public License
15
+   along with this program; if not, write to the Free Software
16
+   Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
17
+
18
+/* Macros. */
19
+
20
+/* The magic number for the Multiboot header. */
21
+#define MULTIBOOT_HEADER_MAGIC          0x1BADB002
22
+
23
+/* The flags for the Multiboot header. */
24
+#ifdef __ELF__
25
+# define MULTIBOOT_HEADER_FLAGS         0x00000003
26
+#else
27
+# define MULTIBOOT_HEADER_FLAGS         0x00010003
28
+#endif
29
+
30
+/* The magic number passed by a Multiboot-compliant boot loader. */
31
+#define MULTIBOOT_BOOTLOADER_MAGIC      0x2BADB002
32
+
33
+/* The size of our stack (16KB). */
34
+#define STACK_SIZE                      0x4000
35
+
36
+/* C symbol format. HAVE_ASM_USCORE is defined by configure. */
37
+#ifdef HAVE_ASM_USCORE
38
+# define EXT_C(sym)                     _ ## sym
39
+#else
40
+# define EXT_C(sym)                     sym
41
+#endif
42
+
43
+#ifndef ASM
44
+/* Do not include here in boot.S. */
45
+
46
+/* Types. */
47
+
48
+/* The Multiboot header. */
49
+typedef struct multiboot_header
50
+{
51
+  unsigned long magic;
52
+  unsigned long flags;
53
+  unsigned long checksum;
54
+  unsigned long header_addr;
55
+  unsigned long load_addr;
56
+  unsigned long load_end_addr;
57
+  unsigned long bss_end_addr;
58
+  unsigned long entry_addr;
59
+} multiboot_header_t;
60
+
61
+/* The symbol table for a.out. */
62
+typedef struct aout_symbol_table
63
+{
64
+  unsigned long tabsize;
65
+  unsigned long strsize;
66
+  unsigned long addr;
67
+  unsigned long reserved;
68
+} aout_symbol_table_t;
69
+
70
+/* The section header table for ELF. */
71
+typedef struct elf_section_header_table
72
+{
73
+  unsigned long num;
74
+  unsigned long size;
75
+  unsigned long addr;
76
+  unsigned long shndx;
77
+} elf_section_header_table_t;
78
+
79
+/* The Multiboot information. */
80
+typedef struct multiboot_info
81
+{
82
+  unsigned long flags;
83
+  unsigned long mem_lower;
84
+  unsigned long mem_upper;
85
+  unsigned long boot_device;
86
+  unsigned long cmdline;
87
+  unsigned long mods_count;
88
+  unsigned long mods_addr;
89
+  union
90
+  {
91
+    aout_symbol_table_t aout_sym;
92
+    elf_section_header_table_t elf_sec;
93
+  } u;
94
+  unsigned long mmap_length;
95
+  unsigned long mmap_addr;
96
+} multiboot_info_t;
97
+
98
+/* The module structure. */
99
+typedef struct module
100
+{
101
+  unsigned long mod_start;
102
+  unsigned long mod_end;
103
+  unsigned long string;
104
+  unsigned long reserved;
105
+} module_t;
106
+
107
+/* The memory map. Be careful that the offset 0 is base_addr_low
108
+   but no size. */
109
+typedef struct memory_map
110
+{
111
+  unsigned long size;
112
+  unsigned long base_addr_low;
113
+  unsigned long base_addr_high;
114
+  unsigned long length_low;
115
+  unsigned long length_high;
116
+  unsigned long type;
117
+} memory_map_t;
118
+
119
+#endif /* ! ASM */

+ 2 - 0
arch/i386/kernel.ld

@@ -4,6 +4,7 @@ SECTIONS
4 4
 {
5 5
 	. = 0xc0100000;
6 6
 	kernel_start = .;
7
+	kernel_start_phys = . - 0xc0000000;
7 8
 	.text ALIGN(0x1000) : AT(ADDR(.text) - 0xc0000000) {
8 9
 		*(.text*) *(.rodata*)
9 10
 	}
@@ -24,4 +25,5 @@ SECTIONS
24 25
 		*(.bss*) *(COMMON*)
25 26
 	}
26 27
 	kernel_end = .;
28
+	kernel_end_phys = . - 0xc0000000;
27 29
 }

+ 2 - 1
arch/i386/kernel/kernel.mk

@@ -3,7 +3,8 @@ SUBDIRS :=
3 3
 
4 4
 include $(BASEDIR)/header.mk
5 5
 
6
-OBJS_$(d) := $(d)/mmu.o
6
+OBJS_$(d) := $(d)/mmu.o \
7
+	$(d)/multiboot.o
7 8
 
8 9
 KOBJS += $(OBJS_$(d))
9 10
 

+ 94 - 2
arch/i386/kernel/mmu.c

@@ -22,9 +22,101 @@
22 22
 #include <types.h>
23 23
 #include <mm.h>
24 24
 
25
+extern uint32 kernel_start_phys, kernel_end_phys;
26
+extern uint32 kernel_start, kernel_end;
27
+
28
+#define PAGE_SIZE 0x00400000
29
+#define ROUND_DOWN_PAGE_SIZE(addr) ((typeof(addr))((uint32)(addr) & 0xff800000))
30
+
31
+static inline int page_intersects(uint32 *page_start, uint32 *lower, uint32 *upper) {
32
+	return (lower >= page_start && lower < (page_start + PAGE_SIZE)) ||
33
+		(upper >= page_start && upper < (page_start + PAGE_SIZE)) ||
34
+		(lower < page_start && upper >= (page_start + PAGE_SIZE));
35
+}
36
+
25 37
 void mmu_reinit() {
38
+	int virt_phys_offset; /* CAUTION: 1 = 4 bytes */
39
+	uint32 *curr_tbl_entry;
40
+	uint32 *curr_addr;
41
+	uint32 *page_dir;
42
+	asm("movl %%cr3, %0" : "=r"(page_dir) : : );
43
+
44
+	virt_phys_offset = &kernel_start - &kernel_start_phys;
45
+	curr_tbl_entry = page_dir + virt_phys_offset;
46
+
47
+	//do first loop iteration outside th eloop, because we have to check against wrapping back around to know we're done
48
+	*curr_tbl_entry = 0x83;
49
+	curr_tbl_entry++;
50
+
51
+	//create identity mapping for entire address space using sections.
52
+	//BUT, if we've relocated the kernel from where it is in physical
53
+	//memory, make sure we keep those mappings correct, and we'll actually
54
+	//swap the two mappings so all of memory is addressable.
55
+	for (curr_addr = (uint32 *)PAGE_SIZE; curr_addr != 0; curr_addr += (PAGE_SIZE>>2)) {
56
+		if (page_intersects(curr_addr, &kernel_start_phys, &kernel_end_phys)) {
57
+			*curr_tbl_entry = (uint32)ROUND_DOWN_PAGE_SIZE(curr_addr + virt_phys_offset) | 0x83;
58
+		} else if (page_intersects(curr_addr, &kernel_start, &kernel_end)) {
59
+			*curr_tbl_entry = (uint32)ROUND_DOWN_PAGE_SIZE(curr_addr - virt_phys_offset) | 0x83;
60
+		} else {
61
+			*curr_tbl_entry = (uint32)curr_addr | 0x83;
62
+		}
63
+		/* Force the entries to reload */
64
+		asm("invlpg (%0)" : : "r"(curr_addr) : );
65
+		curr_tbl_entry++;
66
+	}
26 67
 }
68
+
69
+//TODO merge the rest of this file with the similar section in arch/arm. This is clearly mostly duplicated code.
70
+
71
+int mmu_region_contains(void *lower_a, void *upper_a, void *lower_b, void *upper_b) {
72
+	return lower_b >= lower_a && upper_b <= upper_a;
73
+}
74
+int mmu_region_contains_single(void *lower_a, void *upper_a, void *ptr) {
75
+	return lower_a <= ptr && ptr <= upper_a;
76
+}
77
+
78
+#define section_round_down(ptr) (((uint32)ptr) & ~(PAGE_SIZE-1))
79
+#define section_round_up(ptr) (((((uint32)ptr) & ~1) + (PAGE_SIZE-1) ) & ~(PAGE_SIZE-1))
80
+
81
+/* Called once per physical memory region by bootup code. This function is
82
+ * responsible for only adding (via mm_add_free_region()) those parts of the
83
+ * memory region which are still available (i.e. aren't in the kernel and
84
+ * haven't been remapped anywhere else. */
27 85
 void declare_memory_region(void *lower, void *upper) {
28
-	(void)lower;
29
-	(void)upper;
86
+	void *k_section_start_phys = (void *)section_round_down(&kernel_start_phys);
87
+	void *k_section_end_phys = (void *)(section_round_up(&kernel_end_phys) - 1);
88
+	void *k_section_start_virt = (void *)section_round_down(&kernel_start);
89
+	void *k_section_end_virt = (void *)(section_round_up(&kernel_end) - 1);
90
+
91
+	if (upper - lower < 1) {
92
+		print("Warning: declare_memory_region() called with lower=%x, upper=%x. Ignoring.\n", lower, upper);
93
+		return;
94
+	}
95
+
96
+	//TODO It's possible (though highly unlikely) that the kernel (virtual)
97
+	//is split across two different memory regions. We should probably
98
+	//handle this.
99
+	if (mmu_region_contains(lower, upper, k_section_start_phys, k_section_end_phys)) {
100
+		//Don't map any of the physical kernel's memory
101
+		declare_memory_region(lower, (void *) ((char *)k_section_start_phys - 1));
102
+		declare_memory_region((void *) ((char *)k_section_end_phys + 1), upper);
103
+		mm_add_free_region(&kernel_end, k_section_end_virt);
104
+	} else if (mmu_region_contains(lower, upper, k_section_start_virt, k_section_end_virt)) {
105
+		declare_memory_region(lower, (void *) ((char *)k_section_start_virt - 1));
106
+		declare_memory_region((void *) ((char *)k_section_end_virt + 1), upper);
107
+	} else if (mmu_region_contains_single(lower, upper, k_section_start_phys)) {
108
+		if ((void*)((char*)lower + 1) < k_section_start_phys)
109
+			declare_memory_region(lower, (void *) ((char *)k_section_start_phys - 1));
110
+	} else if (mmu_region_contains_single(lower, upper, k_section_end_phys)) {
111
+		if (k_section_end_phys < (void*)((char*)upper - 1))
112
+			declare_memory_region((void *) ((char *)k_section_end_phys + 1), upper);
113
+	} else if (mmu_region_contains_single(lower, upper, k_section_start_virt)) {
114
+		if ((void*)((char*)lower + 1) < k_section_start_virt)
115
+			declare_memory_region(lower, (void *) ((char *)k_section_start_virt - 1));
116
+	} else if (mmu_region_contains_single(lower, upper, k_section_end_virt)) {
117
+		if (k_section_end_virt < (void*)((char*)upper - 1))
118
+			declare_memory_region((void *) ((char *)k_section_end_virt + 1), upper);
119
+	} else {
120
+		mm_add_free_region(lower, upper);
121
+	}
30 122
 }

+ 64 - 0
arch/i386/kernel/multiboot.c

@@ -0,0 +1,64 @@
1
+/*
2
+    Copyright (C) 2012, Aaron Lindsay <aaron@aclindsay.com>
3
+
4
+    This file is part of Aedrix.
5
+
6
+    This program is free software; you can redistribute it and/or modify
7
+    it under the terms of the GNU General Public License as published by
8
+    the Free Software Foundation; either version 2 of the License, or
9
+    (at your option) any later version.
10
+
11
+    This program is distributed in the hope that it will be useful,
12
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
13
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14
+    GNU General Public License for more details.
15
+
16
+    You should have received a copy of the GNU General Public License along
17
+    with this program; if not, write to the Free Software Foundation, Inc.,
18
+    51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
19
+ */
20
+
21
+#include <arch/multiboot.h>
22
+#include <mmu.h>
23
+
24
+struct multiboot_mmap {
25
+	unsigned int size;
26
+	unsigned int base_addr_low, base_addr_high;
27
+	unsigned int length_low, length_high;
28
+	unsigned int type;
29
+};
30
+
31
+extern multiboot_info_t* multiboot_mbd;
32
+extern uint32 multiboot_magic;
33
+
34
+int detect_memory_mmap() {
35
+	struct multiboot_mmap* mmap = (struct multiboot_mmap*)multiboot_mbd->mmap_addr;
36
+	int found_region = 0;
37
+
38
+	while ((char*)mmap < (char*)multiboot_mbd->mmap_addr + multiboot_mbd->mmap_length) {
39
+		if (mmap->base_addr_high || mmap->length_high)
40
+			return -1;
41
+		if (mmap->type == 0x1) { /* ignore non-usable memory */
42
+			declare_memory_region((void*)mmap->base_addr_low,
43
+				(void*)(mmap->base_addr_low + mmap->length_low));
44
+			found_region = 1;
45
+		}
46
+		mmap = (struct multiboot_mmap*) ((unsigned int)mmap + mmap->size + sizeof(unsigned int));
47
+	}
48
+
49
+	return !found_region;
50
+}
51
+
52
+int detect_memory_contiguous() {
53
+	declare_memory_region((void*)0x0, (void*)((uint32)multiboot_mbd->mem_lower * 1024));
54
+	declare_memory_region((void*)0x1000000, (void*)((uint32)multiboot_mbd->mem_upper*1024 + 0x100000));
55
+	return 0;
56
+}
57
+
58
+int detect_memory() {
59
+	if (multiboot_mbd->flags & (1<<6))
60
+		return detect_memory_mmap();
61
+	else if (multiboot_mbd->flags & (1<<0))
62
+		return detect_memory_contiguous();
63
+	return -1;
64
+}

+ 6 - 6
arch/i386/start.S

@@ -47,8 +47,8 @@
47 47
  */
48 48
 .global start
49 49
 start:
50
-	movl  %eax, virt_to_phys(magic)		/* Save off multiboot magic number */
51
-	movl  %ebx, virt_to_phys(mbd)		/* Save off multiboot data structure */
50
+	movl  %eax, virt_to_phys(multiboot_magic)/* Save off multiboot magic number */
51
+	movl  %ebx, virt_to_phys(multiboot_mbd)	/* Save off multiboot data structure */
52 52
 
53 53
 	movl $virt_to_phys(init_page_dir), %eax	/* load page directory base register */
54 54
 	movl %eax, %cr3
@@ -134,10 +134,10 @@ init_page_dir:
134 134
 .lcomm stack, STACK_SIZE		/* reserve stack space on a doubleword boundary */
135 135
 
136 136
 /* place to store multiboot header information */
137
-.global mbd
138
-.comm  mbd, 4				/* we will use this in i386_main */
139
-.global magic
140
-.comm  magic, 4				/* we will use this in i386_main */
137
+.global multiboot_mbd
138
+.comm  multiboot_mbd, 4			/* we will use this in i386_main */
139
+.global multiboot_magic
140
+.comm  multiboot_magic, 4		/* we will use this in i386_main */
141 141
 
142 142
 /* TODO FIXME remove this - needs fix to dependencies first */
143 143
 .global atags_ptr