1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
|
/* multiboot header constants */
.set ALIGN, 1<<0 /* Align loaded modules on page boundries */
.set MEMINFO, 1<<1 /* Provide memory map*/
.set FLAGS, ALIGN | MEMINFO /* Multiboot flag field*/
.set MAGIC, 0x1BADB002 /*Lets bootloader find header*/
.set CHECKSUM, -(MAGIC + FLAGS) /* Proves we are multiboot via checksum*/
/*
* Declare a multiboot header that marks the program as a kernel.
* These are magic values that are docymented in the multiboot standard.
* The bootloader will search fot this signature in the first 8KiB of
* the kernel file, aligned at a 32-bit boundry.
* The signature is in its own section so the header can be forced to be
* within the first 8KiB of the kernel file.
*/
.section .multiboot
.align 4
.long MAGIC
.long FLAGS
.long CHECKSUM
/*
* The multiboot standard doesn't define the value of the "stack pointer register,"
* or esp, as it is up to the kernel to provide a stack.
* This allocates room for a small stack through a few steps:
* 1. Create a symbol at the bottom of the stack
* 2. Allocate 16 KiB for the stack
* 3. Create a symbol at the top of the stack.
* The stack grows DOWN on x86.
* Since the stack is in its own section*, it can be marked "nobits"
* which means the kernel file is smaller since it doesn't contain
* an uninitialized stack.
* On x86, the stack must be 16-byte aligned** according to System V ABI standard
* and de-facto extentions.
* The compiler assumes the stack is properly aligned, so failure to align will
* result in UB.
*/
.section bss
.align 16
.stack_bottom:
.skip 16384
stack_top:
/*
* Our linker script specifies _start as te entry point to the kernel.
* The bootloader will jump here once the kernel is loaded.
* We won't return, since the bootloader would be gone at that point
*/
.section .text
.global _start
.type _start, @function
_start:
/*
* The bootloader loads us into 32-bit mode on x86 machines.
* Interrupts and paging are disabled.
* The multiboot standard defines our current processor state.
* The kernel has full control over the CPU
* The kernel can only use two things
* 1. hardware features
* 2. its own code
* No printing is available (Unless we make it).
* No security restrictions, safeguards, or debugging mechanisms.
* We only have what the kernel provides.
* ABSOLUTE POWER OVER THE MACHINE!!
*/
/*
* To setup a stack, make the esp register point to the top of the stack.
* We have to do this in assembly since C wont even function without the stack.
*/
mov $stack_top, %esp
/*
* This is a good point to initialize crucial processor state before our
* high level kernel is entered.
* It's best to minize early environment where crucial features are offline.
* The processor is not fully initialized yet, therefore
* features such as floating point instructions and instruction set extentions
* are ALSO not initialized.
* The GDT (global descriptor table) should be loaded here.
* Paging should be enabled here.
*/
/*
* Enter our high level kernel.
* the ABI (?) requres the stack is 16-byte aligned at the time of
* the call instruction (which afterward will push the return pointer
* of size 4 bytes).
* The stack was 16-byte aligned above, and we have pushed a multiple of
* 16 bytes to the stack (we have pushed zero bytes so far), so
* the alignment is preserved, and the call is well defined.
*/
call kernel_main
/*
* If the system has nothing else to do, we will infinitely loop.
* To do this, we must:
* 1. Disable interrupts with `cli`
* (clear interrupt enable in eflags.)
* They are already disabled by the bootloader, so we can skip this.
* Keep in mind, we may have to do this later AND return from main
* (for some nonsensical reason)
* 2. wait for the next interrupt to arrive with `hlt` (halt instruction)
* Since interrupts are disabled, this locks up the computer (yay!)
* 3. jump to the `hlt` instruction if it ever wakes up due to a
* non-maskable interrupt occuring or due to a system management mode.
*/
cli
1: hlt
jmp 1
/*
* Set the size of _start to { current_location - (start of _start) }
* This is useful for debugging or for call tracing later on.
*/
.size _start, . - _start
|