-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathboot.asm
230 lines (188 loc) · 4.61 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
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
; boot.asm
[BITS 16]
[ORG 0x7c00]
; 00000 - 003ff IVT
; 00400 - 004ff BDA
; 00500 - 0052f The Petch Zone :)
; 00530 - 07bff Stack
; 07c00 - 07dff IPL - initial program loader
; 07e00 - 0bbff Kernel - loaded initially
; 0bc00 - 0ffff Free
start:
xor ax, ax
mov ds, ax ; data segment starts from 0
mov es, ax
mov bx, 0x7c00
mov ss, ax
mov sp, bx
mov al, dl
mov [drive_number], al
cld ; set positive direction
mov si, msg_loading_kernel
call bios_print
load_kernel:
mov al, [drive_number]
mov dl, al ; set drive number
xor ax, ax
mov es, ax
mov bx, 0x7e00 ; read destination ES:BX = 0:7e00h
mov ah, 2 ; read operation
mov al, 0x1F ; read 31 sectors
mov ch, 0 ; cylinder 0
mov dh, 0 ; head 0
mov cl, 2 ; sector 2
int 13h ; ask BIOS to read the sector
jnc done_load_kernel ; carry bit is set on error
mov si, msg_failed
call bios_print
hlt
done_load_kernel:
enable_pmode:
cli ; disable interrupts
call check_a20
test ax, 1
jz enable_a20
mov si, msg_a20_already_enabled
call bios_print
jmp copy_kernel
enable_a20:
mov si, msg_enabling_a20
call bios_print
call wait_kbd_w
mov al, 0xAD ; disable keyboard
out 0x64, al
call wait_kbd_w
mov al, 0xD0 ; request read from input
out 0x64, al
call wait_kbd_r
in al, 0x60 ; read from input
push ax
call wait_kbd_w
mov al, 0xD1 ; request write to output
out 0x64, al
call wait_kbd_w
pop ax
or al, 2 ; set second bit
out 0x60, al ; write to output
call wait_kbd_w
mov al, 0xAE ; enable keyboard
out 0x64, al
call wait_kbd_w
call check_a20
test ax, 1
jnz copy_kernel
mov si, msg_failed
call bios_print
hlt
copy_kernel:
xor ax, ax
mov ds, ax
mov si, 0x7e00
mov ax, 0xffff
mov es, ax
mov di, 0x10 ; es:di = 0xffff:0x10 = 0xffff*0x10 + 0x10 = 1MB
mov cx, 0x1e00 ; 15 * 512 - 15 sectors of kernel (7680 bytes)
rep movsw
xor ax, ax
mov es, ax
; now let us load GDT
load_gdt:
mov si, msg_final
call bios_print
cli
mov ax, cs ; all addresses generated by assembler are relative to CS
mov ds, ax
lgdt [gdtr] ; load Global Descriptor Table
xor ax, ax
mov ds, ax
mov eax, cr0
or al, 1
mov cr0, eax ; set PE bit to control register 0 to enable Pmode
; data segments should point to data selector inside GDT (offset inside GDT is 0x10)
mov eax, 0x10
mov ds, ax
mov ss, ax
mov es, ax
; To far jump to kernel code to clear pipe line.
; To do it, we need to set offset explicitly.
; Offset in 32 bit mode is offset of code_descriptor inside GDT - 0x8 (it is value of CS)
; First let's jump to intermediate 32 bit code of bootloader
jmp 0x8:PM32
hlt
bios_print:
lodsb
or al, al ; if zero then end of string reached
jz done_print
mov ah, 0x0E
int 0x10
jmp bios_print
done_print:
ret
wait_kbd_w:
nop
in al, 0x64
test al, 2 ; if output buffer full
jnz wait_kbd_w ; wait more
ret
wait_kbd_r:
nop
in al, 0x64
test al, 1 ; if input buffer empty
jz wait_kbd_r ; wait more
ret
check_a20:
mov ax, 0xFFFF
mov ds, ax
mov di, 0x7DFE ; address of boot sector identifier 0xAA55
mov si, 0x7E0E ; DS is 1 x 10h below 1 MiB, so 0xFFFF x 0x10 + 0x7DFE == 0x10000 + 0x7E0E
mov byte [es:di], 0x00
mov byte [ds:si], 0xFF
cmp byte [es:di], 0xFF
mov word [es:di], 0xAA55 ; restore original value
mov ax, 0
je check_a20__exit
mov ax, 1
check_a20__exit:
xor bx, bx
mov ds, bx
ret
msg_loading_kernel db 'Loading kernel...', 13, 10, 0
msg_failed db 'FAILED!', 0
msg_enabling_a20 db 'Enabling A20...', 13, 10, 0
msg_a20_already_enabled db 'A20 already enabled...', 13, 10, 0
msg_final db 'Finally loading GDT and switching to protected mode...', 13, 10, 0
drive_number db 0
gdtr:
dw end_of_gdt - gdt - 1
dd gdt
gdt:
; GDT taken from http://www.brokenthorn.com/Resources/OSDev8.html
null_descriptor:
dd 0 ; null descriptor--just fill 8 bytes with zero
dd 0
; Notice that each descriptor is exactally 8 bytes in size. THIS IS IMPORTANT.
; Because of this, the code descriptor has offset 0x8.
code_descriptor: ; code descriptor. Right after null descriptor
dw 0FFFFh ; limit low
dw 0 ; base low
db 0 ; base middle
db 10011010b ; access
db 11001111b ; granularity
db 0 ; base high
; Because each descriptor is 8 bytes in size, the Data descritpor is at offset 0x10 from
; the beginning of the GDT, or 16 (decimal) bytes from start.
data_descriptor: ; data descriptor
dw 0FFFFh ; limit low (Same as code)
dw 0 ; base low
db 0 ; base middle
db 10010010b ; access
db 11001111b ; granularity
db 0 ; base high
end_of_gdt:
[BITS 32]
PM32:
; Now far jump to kernel
jmp 0x8:0x100000
times 510-($-$$) db 0
db 0x55
db 0xAA