-
Notifications
You must be signed in to change notification settings - Fork 215
/
Copy pathstage_1.s
245 lines (201 loc) · 4.76 KB
/
stage_1.s
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
241
242
243
244
245
.section .boot-first-stage, "awx"
.global _start
.intel_syntax noprefix
.code16
# This stage initializes the stack, enables the A20 line, loads the rest of
# the bootloader from disk, and jumps to stage_2.
_start:
# zero segment registers
xor ax, ax
mov ds, ax
mov es, ax
mov ss, ax
mov fs, ax
mov gs, ax
# clear the direction flag (e.g. go forward in memory when using
# instructions like lodsb)
cld
# initialize stack
mov sp, 0x7c00
lea si, boot_start_str
call real_mode_println
enable_a20:
# enable A20-Line via IO-Port 92, might not work on all motherboards
in al, 0x92
test al, 2
jnz enable_a20_after
or al, 2
and al, 0xFE
out 0x92, al
enable_a20_after:
enter_protected_mode:
# clear interrupts
cli
push ds
push es
lgdt [gdt32info]
mov eax, cr0
or al, 1 # set protected mode bit
mov cr0, eax
jmp protected_mode # tell 386/486 to not crash
protected_mode:
mov bx, 0x10
mov ds, bx # set data segment
mov es, bx # set extra segment
and al, 0xfe # clear protected mode bit
mov cr0, eax
unreal_mode:
pop es # get back old extra segment
pop ds # get back old data segment
sti
# back to real mode, but internal data segment register is still loaded
# with gdt segment -> we can access the full 4GiB of memory
mov bx, 0x0f01 # attrib/char of smiley
mov eax, 0xb8f00 # note 32 bit offset
mov word ptr ds:[eax], bx
check_int13h_extensions:
mov ah, 0x41
mov bx, 0x55aa
# dl contains drive number
int 0x13
jc no_int13h_extensions
load_rest_of_bootloader_from_disk:
mov eax, offset _rest_of_bootloader_start_addr
# dap buffer segment
mov ebx, eax
shr ebx, 4 # divide by 16
mov [dap_buffer_seg], bx
# buffer offset
shl ebx, 4 # multiply by 16
sub eax, ebx
mov [dap_buffer_addr], ax
mov eax, offset _rest_of_bootloader_start_addr
# number of disk blocks to load
mov ebx, offset _rest_of_bootloader_end_addr
sub ebx, eax # end - start
shr ebx, 9 # divide by 512 (block size)
mov [dap_blocks], bx
# number of start block
lea ebx, _start
sub eax, ebx
shr eax, 9 # divide by 512 (block size)
mov [dap_start_lba], eax
lea si, dap
mov ah, 0x42
int 0x13
jc rest_of_bootloader_load_failed
# reset segment to 0
mov word ptr [dap_buffer_seg], 0
jump_to_second_stage:
lea eax, [stage_2]
jmp eax
spin:
jmp spin
# print a string and a newline
# IN
# si: points at zero-terminated String
# CLOBBER
# ax
real_mode_println:
call real_mode_print
mov al, 13 # \r
call real_mode_print_char
mov al, 10 # \n
jmp real_mode_print_char
# print a string
# IN
# si: points at zero-terminated String
# CLOBBER
# ax
real_mode_print:
cld
real_mode_print_loop:
# note: if direction flag is set (via std)
# this will DECREMENT the ptr, effectively
# reading/printing in reverse.
lodsb al, BYTE PTR [si]
test al, al
jz real_mode_print_done
call real_mode_print_char
jmp real_mode_print_loop
real_mode_print_done:
ret
# print a character
# IN
# al: character to print
# CLOBBER
# ah
real_mode_print_char:
mov ah, 0x0e
int 0x10
ret
# print a number in hex
# IN
# bx: the number
# CLOBBER
# al, cx
real_mode_print_hex:
mov cx, 4
.lp:
mov al, bh
shr al, 4
cmp al, 0xA
jb .below_0xA
add al, 'A' - 0xA - '0'
.below_0xA:
add al, '0'
call real_mode_print_char
shl bx, 4
loop .lp
ret
real_mode_error:
call real_mode_println
jmp spin
no_int13h_extensions:
lea si, no_int13h_extensions_str
jmp real_mode_error
rest_of_bootloader_load_failed:
lea si, rest_of_bootloader_load_failed_str
jmp real_mode_error
boot_start_str: .asciz "Booting (first stage)..."
error_str: .asciz "Error: "
no_int13h_extensions_str: .asciz "No support for int13h extensions"
rest_of_bootloader_load_failed_str: .asciz "Failed to load rest of bootloader"
gdt32info:
.word gdt32_end - gdt32 - 1 # last byte in table
.word gdt32 # start of table
gdt32:
# entry 0 is always unused
.quad 0
codedesc:
.byte 0xff
.byte 0xff
.byte 0
.byte 0
.byte 0
.byte 0x9a
.byte 0xcf
.byte 0
datadesc:
.byte 0xff
.byte 0xff
.byte 0
.byte 0
.byte 0
.byte 0x92
.byte 0xcf
.byte 0
gdt32_end:
dap: # disk access packet
.byte 0x10 # size of dap
.byte 0 # unused
dap_blocks:
.word 0 # number of sectors
dap_buffer_addr:
.word 0 # offset to memory buffer
dap_buffer_seg:
.word 0 # segment of memory buffer
dap_start_lba:
.quad 0 # start logical block address
.org 510
.word 0xaa55 # magic number for bootable disk