/
boot.asm
86 lines (80 loc) · 4.5 KB
/
boot.asm
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
; Declare constants for the multiboot header
MBALIGN equ 1<<0 ; align loaded moduluess on page boundries
MEMINFO equ 1<<1 ; provide memory map
FLAGS equ MBALIGN | MEMINFO ; this is the Multiboot 'flag' field
MAGIC equ 0x1BADB002 ; 'magic number' lets the bootloader find the header
CHECKSUM equ -(MAGIC + FLAGS) ; checksum of above, to provide we are multiboot
; Declare a multiboot header that marks the program as a kernal. These are magic
; values that are documented in the multiboot standard. The bootloader will
; search for this signature in the first 8KiB of the kernal file, aligned at a
; 32-bit boundry. The signature is in its own section so the heaeder can be
; forced to be within the first 8KiB of the kernal file.
section .multiboot
align 4
dd MAGIC
dd FLAGS
dd CHECKSUM
; The multiboot standard does not define the value of the stack pointer register
; (esp) and it is up to the kernal to provide a stack. This allocates room for a
; small stack by creating a symbol at the bottom of it, then allocating 16384
; bytes for it, and finially creating a symbol at the top. The stack grows
; downwards on x86. The stack is in its own section so it can be marked nobits,
; which means the kernal file is smaller because it does not contain an uninitialized stack. The stack on x86 must be 16-byte aligned according to the
; System V ABI standard and de-facto extensions. The compiler will assume the stack is properly aligned and failure to align the stack will result in
; undefined behavior.
section .bss
align 16
stack_bottom:
resb 16386 ; 16 KiB like the skip command
stack_top:
; The linker script specifies _start as the entry point to the kernal and the
; bootloader will jump to this position once the kernal has been loaded. It
; doesn't make sense to return from this function as the bootloader is gone.
; Declare _start as a function symbol with the given symbol size.
section .text
global _start:function (_start.end - _start)
_start:
; The bootloader has loded us into 32-bit protected mode on a x86
; machine. Interrupts are disabled. Paging is disabled. THe processor
; start is as defined in the multiboot standard. The kernal has full
; control of the CPU. The kernal can only make use of hardware features
; and any code it provides as part of itself. There's no printf
; function, unless the kernal provides its own <stdio.h> header and a
; printf implementation. There are no security restrictions, no
; safeguards, no debugging mechanisms, only what the kernal provides
; itself. It has absolute and complete power over the machine
; To set up the stack, we set the esp register to point to the top of our
;stack (as it grows downwards on x86 systems). This is necessarilt done
; in assemply as languages such as C cannot function without a stack.
mov esp, stack_top
; TODO: This is a good place to initialize crucial processor state before the
; high-level kernel is entered. It's best to minimize the early
; enviornment where crucial features are offline. Note that the
; processor is not fully initialized yet: Features such as floating
; point instructions and instruction set extensions are not initialized
; yet. The GDT should be loaded here. Paging should be enabled here.
; C++ features such as global constructors and exceptions will require
; runtime support to work as well.
; Enter the high-level kernel. The ABI requires the stack is 16-byte
; aligned at the time of the call instructions (which afterwards pushes
; the return pointer of size 4 bytes). The stack was originally 16-byte
; aligned above and we've since pushed a multiple of 16 buytes to the
; stack since (pushed 0 bytes so far) and the alignment is thus
; preserved and the call is well defined.
; note, that is you are building on Windows, C functions may have "_" prefix in assembly: _kernel_main
extern kernel_main
call kernel_main
; If the system has nothing more to do, put the computer into an
; infinite loop. To do that:
; 1) Disable interrupts with 'cli' (clear interrupt enable in eflags).
; They are already disabled by the bootloader, so this is not actually needed.
; Mind that you might later enable interrupts and return from
; kernel_main (which is sort of nonsensical to do).
; 2) Wait for the next interrupt to arrive with 'hlt' (halt instruction).
; Since they are disabled this will lock up the computer.
; 3) Jump to the 'hlt' instruction if it ever wakes up due to a
; non-maskable interrupt occuring or due to system managment mode
cli
.hang: hlt
jmp .hang
.end: