Browse Source

arch/i386: Boot to kernel main() with proper initial segmentation/paging setup

Aaron Lindsay 7 years ago
parent
commit
c51062ce87
5 changed files with 153 additions and 60 deletions
  1. 0 30
      arch/i386/i386_main.c
  2. 26 0
      arch/i386/include/arch/segmentation.h
  3. 14 5
      arch/i386/kernel.ld
  4. 1 2
      arch/i386/kernel.mk
  5. 112 23
      arch/i386/start.S

+ 0 - 30
arch/i386/i386_main.c

@@ -1,30 +0,0 @@
1
-#include <stdint.h>
2
-
3
-void main();
4
- 
5
-void i386_main(void)
6
-{
7
-   extern uint32_t magic;
8
- 
9
-   /* Uncomment the following if you want to be able to access the multiboot header */
10
-   /* extern void *mbd; */
11
- 
12
-   if ( magic != 0x2BADB002 )
13
-   {
14
-      /* Something went not according to specs. Print an error */
15
-      /* message and halt, but do *not* rely on the multiboot */
16
-      /* data structure. */
17
-   }
18
- 
19
-   /* You could either use multiboot.h */
20
-   /* (http://www.gnu.org/software/grub/manual/multiboot/multiboot.html#multiboot_002eh) */
21
-   /* or do your offsets yourself. The following is merely an example. */ 
22
-   //char * boot_loader_name =(char*) ((long*)mbd)[16];
23
- 
24
-   /* Print a letter to screen to see everything is working: */
25
-   unsigned char *videoram = (unsigned char *)0xB8000;
26
-   videoram[0] = 65; /* character 'A' */
27
-   videoram[1] = 0x07; /* light grey (7) on black (0). */
28
-
29
-   main();
30
-}

+ 26 - 0
arch/i386/include/arch/segmentation.h

@@ -0,0 +1,26 @@
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
+#define SEGMENT_SELECTOR_KERNEL_PRIVILEGE  0x0
22
+#define SEGMENT_SELECTOR_KERNEL_CS         (0x10 | SEGMENT_SELECTOR_KERNEL_PRIVILEGE)
23
+#define SEGMENT_SELECTOR_KERNEL_DS         (0x18 | SEGMENT_SELECTOR_KERNEL_PRIVILEGE)
24
+#define SEGMENT_SELECTOR_USER_PRIVILEGE    0x3
25
+#define SEGMENT_SELECTOR_USER_CS           (0x20 | SEGMENT_SELECTOR_USER_PRIVILEGE)
26
+#define SEGMENT_SELECTOR_USER_DS           (0x28 | SEGMENT_SELECTOR_USER_PRIVILEGE)

+ 14 - 5
arch/i386/kernel.ld

@@ -2,9 +2,12 @@ ENTRY (start)
2 2
 
3 3
 SECTIONS
4 4
 {
5
-	. = 0x00100000;
6
-	.text ALIGN (0x1000) : { *(.text*) *(.rodata*) }
7
-	.init : {
5
+	. = 0xc0100000;
6
+	kernel_start = .;
7
+	.text ALIGN(0x1000) : AT(ADDR(.text) - 0xc0000000) {
8
+		*(.text*) *(.rodata*)
9
+	}
10
+	.init : AT(ADDR(.init) - 0xc0000000) {
8 11
 		early_initcalls_start = .;
9 12
 		*(.earlyinitcalls*)
10 13
 		early_initcalls_end = .;
@@ -12,7 +15,13 @@ SECTIONS
12 15
 		*(.driversubsysinitcalls*)
13 16
 		*(.deviceinitcalls*)
14 17
 		initcalls_end = .;
18
+		*(.init*)
19
+	}
20
+	.data ALIGN(0x1000) : AT(ADDR(.data) - 0xc0000000) {
21
+		*(.data*)
22
+	}
23
+	.bss : AT(ADDR(.bss) - 0xc0000000) {
24
+		*(.bss*) *(COMMON*)
15 25
 	}
16
-	.data ALIGN (0x1000) : { *(.data*) }
17
-	.bss : { *(.bss*) *(COMMON*) }
26
+	kernel_end = .;
18 27
 }

+ 1 - 2
arch/i386/kernel.mk

@@ -22,8 +22,7 @@ aedrix-boot.img: aedrix-kernel.elf
22 22
 	$(V)mcopy -i "$@" aedrix-kernel.elf ::kernel.bin
23 23
 	$(V)mcopy -i "$@" arch/i386/syslinux.cfg ::syslinux.cfg
24 24
 
25
-OBJS_$(d) := $(d)/start.o \
26
-	$(d)/i386_main.o
25
+OBJS_$(d) := $(d)/start.o
27 26
 
28 27
 KOBJS += $(OBJS_$(d))
29 28
 

+ 112 - 23
arch/i386/start.S

@@ -18,39 +18,128 @@
18 18
     51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
19 19
  */
20 20
 
21
-.global start /* making entry point visible to linker */
21
+#include <arch/segmentation.h>
22 22
 
23
-/* setting up the Multiboot header - see GRUB docs for details */
24
-.set ALIGN,    1<<0                     /* align loaded modules on page boundaries */
25
-.set MEMINFO,  1<<1                     /* provide memory map */
26
-.set FLAGS,    ALIGN | MEMINFO          /* this is the Multiboot 'flag' field */
27
-.set MAGIC,    0x1BADB002               /* 'magic number' lets bootloader find the header */
28
-.set CHECKSUM, -(MAGIC + FLAGS)         /* checksum required */
23
+#define KERNEL_START_VIRTUAL 0xc0000000
24
+#define KERNEL_START_PAGE (KERNEL_START_VIRTUAL >> 22)
25
+#define virt_to_phys(addr) (addr - KERNEL_START_VIRTUAL)
26
+
27
+#define STACK_SIZE 0x4000 /* 16k */
28
+
29
+/* setup multiboot header */
30
+#define ALIGN     (1<<0)                   /* align loaded modules on page boundaries */
31
+#define MEMINFO   (1<<1)                   /* provide memory map */
32
+#define FLAGS     (ALIGN | MEMINFO)        /* this is the Multiboot 'flag' field */
33
+#define MAGIC     0x1BADB002               /* 'magic number' lets bootloader find the header */
34
+#define CHECKSUM  (-(MAGIC + FLAGS))       /* checksum required */
29 35
 
30 36
 .align 4
31 37
 .long MAGIC
32 38
 .long FLAGS
33 39
 .long CHECKSUM
34 40
 
35
-/* reserve initial kernel stack space */
36
-.set STACKSIZE, 0x4000                  /* that is, 16k. */
37
-.align 32
38
-.lcomm stack, STACKSIZE                 /* reserve 16k stack on a doubleword boundary */
39
-.comm  mbd, 4                           /* we will use this in i386_main */
40
-.comm  magic, 4                         /* we will use this in i386_main */
41
-
41
+/*
42
+ * Kernel entry in assembly for i386 architecture. This handles saving off
43
+ * multiboot information, setting up initial page tables to map the kernel to
44
+ * where it is linked against, ensures segmentation is setup right (large segments
45
+ * over entire address range), and finally jumps to the kernel's main() function
46
+ * in C.
47
+ */
48
+.global start
42 49
 start:
43
-    movl  $(stack + STACKSIZE), %esp    /* set up the stack */
44
-    movl  %eax, magic                   /* Multiboot magic number */
45
-    movl  %ebx, mbd                     /* Multiboot data structure */
50
+	movl  %eax, virt_to_phys(magic)		/* Save off multiboot magic number */
51
+	movl  %ebx, virt_to_phys(mbd)		/* Save off multiboot data structure */
52
+
53
+	movl $virt_to_phys(init_page_dir), %eax	/* load page directory base register */
54
+	movl %eax, %cr3
55
+
56
+	movl %cr4, %eax
57
+	orl $0x00000010, %eax			/* set PSE bit for 4mb pages */
58
+	movl %eax, %cr4
59
+
60
+	movl %cr0, %eax
61
+	orl $0x80000000, %eax			/* set PG bit to enable paging */
62
+	movl %eax, %cr0
63
+
64
+	lea paging_enabled, %eax
65
+	jmp *%eax
66
+paging_enabled:
67
+	movl $0, init_page_dir			/* unmap identity mapping and invalidate */
68
+	invlpg 0
46 69
 
47
-    call  i386_main                     /* call kernel proper */
70
+	movl $(stack + STACK_SIZE), %esp	/* set up the stack */
48 71
 
49
-    cli
72
+	/* setup two large kernel segments over entire 4gb address range */
73
+	pushl $initial_gdt			/* setup the pointer to the GDT */
74
+	pushw $(INITIAL_GDT_SIZE-1)
75
+	lgdt (%esp)
76
+	addl $6, %esp
77
+	ljmp $SEGMENT_SELECTOR_KERNEL_CS, $gdt_reload	/* long jump to set %cs */
78
+gdt_reload:
79
+
80
+	/* reset remaining segment registers */
81
+	movl $SEGMENT_SELECTOR_KERNEL_DS, %eax
82
+	movl %eax, %ds
83
+	movl %eax, %es
84
+	movl %eax, %fs
85
+	movl %eax, %gs
86
+	movl %eax, %ss
87
+
88
+	call main				/* call kernel proper */
89
+
90
+	cli
50 91
 hang:
51
-    hlt                                 /* halt machine should kernel return */
52
-    jmp   hang
92
+	hlt                                 /* halt machine should kernel return */
93
+	jmp   hang
94
+
95
+.section .init
96
+
97
+/* Note: if the offsets into the GDT change, they must be updated in arch/segmentation.h */
98
+.set INITIAL_GDT_SIZE, 8*6
99
+initial_gdt:
100
+	.long 0x00000000 /* 0x00 intentionally empty */
101
+	.long 0x00000000
102
+	.long 0x00000000 /* 0x08 TODO TSS */
103
+	.long 0x00000000
104
+	.long 0x0000ffff /* 0x10 kernel code segment (%cs) */
105
+	.long 0x00cf9a00
106
+	.long 0x0000ffff /* 0x18 kernel data segment (%ds) */
107
+	.long 0x00cf9200
108
+	.long 0x0000ffff /* 0x20 userspace code segment (%cs) */
109
+	.long 0x00cffa00
110
+	.long 0x0000ffff /* 0x28 userspace data segment (%ds) */
111
+	.long 0x00cff200
112
+
113
+.align 0x1000
114
+init_page_dir:
115
+	/* This page directory entry identity-maps the first 4MB of the 32-bit physical address space.
116
+	All bits are clear except the following:
117
+	bit 7: PS The kernel page is 4MB.
118
+	bit 1: RW The kernel page is read/write.
119
+	bit 0: P  The kernel page is present.
120
+	This entry must be here -- otherwise the kernel will crash immediately after paging is
121
+	enabled because it can't fetch the next instruction! It's ok to unmap this page later. */
122
+	.long 0x00000083
123
+	.rept (KERNEL_START_PAGE - 1)
124
+		.long 0                 /* Pages before kernel space. */
125
+	.endr
126
+	/* This page directory entry defines a 4MB page containing the kernel. */
127
+	.long 0x00000083
128
+	.rept (1024 - KERNEL_START_PAGE - 1)
129
+		.long 0  /* Pages after the kernel image. */
130
+	.endr
131
+
132
+/* reserve initial kernel stack space */
133
+.align 32
134
+.lcomm stack, STACK_SIZE		/* reserve stack space on a doubleword boundary */
135
+
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 */
53 141
 
54
-.globl atags_ptr
142
+/* TODO FIXME remove this - needs fix to dependencies first */
143
+.global atags_ptr
55 144
 atags_ptr:
56
-	.word 0
145
+	.long 0