summaryrefslogtreecommitdiff
path: root/src/boot/boot.asm
blob: 04016d6c4d939c4e0d7446d6991ea16441ed534e (plain)
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
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
; legacy BIOS PC-boot code, starts in 16-bit real mode
	
; BIOS always loads us to this location
	[org 0x7c00]

; 16-bit real-mode
	[bits 16]

; export a map of boot loader symbols for debugging	
	[map symbols boot.map]
		 
; initialize segment registers
	mov ax, 0
	mov ds, ax
	mov es, ax
	mov ss, ax

; dl contains the boot drive, primarily because that's the last function
; called by the BIOS MBR loader, we remember that for loading additional
; blocks from the boot medium
	mov [BOOT_DRIVE], dl
	
; make sure we know the location of the stack by setting it on our own
; this stack is only used in real mode in stage 1, so it's growing from
; 0x2000 down, we should really not set it above 0x7c00, as this is
; exacly the place, where we load stage 2 and the kernel to! (was 0xffff,
; which was a really bad idea after the kernel got bigger than roughly
; 32k!)
	mov bp, 0x2000
	mov sp, bp
	
; print out some information about CPU mode and BIOS boot drive
	mov si, MESSAGE_REAL_MODE
	call print_string

	mov si, MESSAGE_BOOT_DRIVE
	call print_string
	mov ax, dx
	call print_hex	
	call print_newline

; print we are going to load stage 2 of the boot blocks
	mov si, MESSAGE_LOADING_STAGE_2
	call print_string

; load stage 2 with simple load method to 0x7e00 (directly
; after the boot sector)	
	mov dl, [BOOT_DRIVE]
	call read_stage2_from_disk
	call print_newline
	
; jump over variables and subroutines of stage 1 and execute stage 2
	jmp stage2
	
%include "boot/stage1_functions.asm"
		
DISK_ERROR:
	db "DISK ERROR ", 0

SHORT_READ:
	db "DISK SHORT READ", 0

MESSAGE_REAL_MODE:
	db "Started in 16-bit Real Mode", 13, 10, 0

MESSAGE_BOOT_DRIVE:
	db "Booting from drive ", 0
	
MESSAGE_LOADING_STAGE_2:
	db "Loading stage 2 boot loader", 0

MESSAGE_DETECTING_DISK_GEOMETRY:
	db "Detecting disk geometry ", 0

MESSAGE_LOADING_KERNEL:
	db "Loading kernel", 0

BOOT_DRIVE:
	db 0
	
; pad rest of sector with zeroes so we get 512 bytes in the end
times 510-($-$$) db 0

; magic number of a boot sector
dw 0xaa55

stage2:

; print a message that we are indeed in stage 2 now
	mov si, MESSAGE_STAGE_2_LOADED
	call print_string

; detect disk geometry (works for real floppies only?)
	mov cl, [BOOT_DRIVE]
	and cl, 0x80
	jnz .not_a_floppy

	mov si, MESSAGE_DETECTING_DISK_GEOMETRY
	call print_string
	mov dl, [BOOT_DRIVE]
	call detect_disk_geometry
	mov dl, [BOOT_DRIVE]
	call probe_and_fix_disk_geometry
	call print_newline
.not_a_floppy:

; print a message we are now loading the kernel
	mov si, MESSAGE_LOADING_KERNEL
	call print_string


; load kernel to 0x8800 (directly  after stage 2
; of the boot loader)	
	mov dl, [BOOT_DRIVE]
	call read_from_disk
	call print_newline

; turn off disk motor
	call kill_motor

; check A20 gate
	mov si, MESSAGE_STAGE_2_CHECKING_A20
	call print_string
	call check_and_enable_A20
	cmp ax, 1
	je .A20_enabled
	mov si, MESSAGE_STAGE_2_A20_DISABLED
	call print_string
;	jmp .halt_os_realmode

.A20_enabled:
	mov si, MESSAGE_STAGE_2_A20_ENABLED
	call print_string
	
; remember current position on screen, otherwise we loose this
; information after switching to protected mode 
	call current_row
	mov [CURSOR_Y], dh

; enter protected mode
	call switch_to_protected_mode
	
; we should never get here, but just in case
	jmp $

; stop loading in real mode
.halt_os_realmode:
	mov si, MESSAGE_HALTED_REAL
	call print_string
.loop:	cli
	hlt
	jmp .loop

MESSAGE_STAGE_2_LOADED:
	db "Stage 2 boot sectors loaded", 13, 10, 0

MESSAGE_STAGE_2_CHECKING_A20:
	db "Checking A20 address gate... ", 0
	
MESSAGE_STAGE_2_A20_ENABLED:
	db " ...enabled", 13, 10, 0
	
MESSAGE_STAGE_2_A20_DISABLED:
	db " ...disabled", 13, 10, 0

MESSAGE_HALTED_REAL:
	db "Operating system halted in real mode", 13, 10, 0

%include "boot/stage2_real_functions.asm"	
%include "boot/stage2_switch_mode.asm"
%include "boot/stage2_a20.asm"

[bits 32]

BEGIN_PROTECTED_MODE:
; make sure registers have known values
	xor eax, eax
	xor ebx, ebx
	xor ecx, ecx
	xor edx, edx
	xor esi, esi
	xor edi, edi

; we switched to protected mode
	mov si, MESSAGE_PROTECTED_MODE
	call pm_print_string
	call pm_print_newline

; check sanity of kernel by searching for MAGIC string at a given
; position
	call check_magic
	cmp ax, 1
	jnz .halt_os_pm

; print a message before we call the C level kernel	
	mov si, MESSAGE_CALL_C_ENTRY
	call pm_print_string
	call pm_print_newline
	
; call our kernel
	call kernel_entry

; restore cursor variables from VGA hardware
	call read_hardware_vga_cursor

; "kernel halted" message, when we terminate the C kernel
.halt_os_pm:
	mov si, MESSAGE_HALTED
	call pm_print_string
	call pm_print_newline

; end of C, disable interupts again, NMIs can still happen
; make sure we loop endlessly here but without burning too
; much CPU
	cli
.loop:
	hlt
	jmp .loop
	
MESSAGE_PROTECTED_MODE:
	db "Switched to 32-bit Protected Mode", 0

MESSAGE_CALL_C_ENTRY:
	db "Calling C entry function", 0
	
MESSAGE_HALTED:
	db "Operating system halted", 0

%include "boot/stage2_pm_functions.asm"
%include "boot/stage2_check_magic.asm"

; make sure we have full sectors, stage 1 is 512 bytes, so we
; have to will up 5 sectors (gives 6 in total for stage 1 and 2)
times 3070-($-$$) db 0

; magic number of stage 2
dw 0xAABB

; position is 0x8800 now for the C entry
kernel_entry: