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
|
; MIT License, Copyright (c) 2021 Marvin Borner
; This file includes definitions to reduce magic numbers
%include "src/entry/definitions.asm"
bits 16 ; Real mode is 16 Bit
org LOCATION ; Bootsector location
global _start
_start:
jmp .skip_bpb ; Some BIOSes override the BPB area
nop
times 87 db 0 ; Fill BPB area with 0
.skip_bpb:
; Clear registers (some BIOSes are weird)
xor bx, bx
mov ds, bx
mov es, bx
mov ss, bx
; Other registers may contain relevant data
mov sp, LOCATION ; Set stack (top) pointer - grows downwards
mov ax, SCREEN_CLEAR ; Clear screen command
int SCREEN_INT ; CLEAR!
; Print friendly welcome message
mov si, hello_msg
call print
call disk_support ; Is disk supported? Implicitly verifies that this is a 386+ architecture
mov esp, LOCATION ; Clear upper 16 Bit of esp, now that 16 Bit support is guaranteed
cli ; Disable interrupts
jmp load_stage ; Load and execute main stage
; Print function uses si as string pointer
print:
; a and b regs get partially destroyed - push for restoration
push bx
push ax
mov ah, SCREEN_OUT ; Set VGA command
xor bh, bh ; Clear b register (according to BIOS spec)
.putch:
lodsb ; Load next string byte (using ds:si)
test al, al ; Test loaded byte
jz .end ; End if al is zero (using previous test); NULL-termination
int SCREEN_INT ; WRITE!
jmp .putch ; Continue
.end:
; Restore ax/bx
pop ax
pop bx
ret
; Check if disk is supported using ID checks and LBA test
disk_support:
; BIOS puts the disk id into dl
cmp dl, 0x80
jb disk_error ; Error if below 0x80 - probably floppy disk
cmp dl, 0x8f
ja disk_error ; Error if above 0x8f - invalid
; Check if int 0x13 and LBA are supported
mov ah, DISK_EXT_CHECK ; Set needed interrupt values
mov bx, DISK_EXT_CHECK_REQ
int DISK_INT ; CHECK!
jc disk_error ; Carry means something went wrong
cmp bx, DISK_EXT_CHECK_RESP
jne disk_error ; Response is incorrect => error!
ret
; Read sectors from disk using dap information
disk_read:
mov si, dap
mov ah, DISK_READ
int DISK_INT
jc error_loop
ret
; Loads the main stage (main.c)
load_stage:
mov bx, loader
mov [dap.dest], bx
call disk_read
lgdt [gdt] ; Load GDT
; Set protected mode (32 Bit mode)
mov eax, cr0
or ax, 1 ; Set PE (Protection Enable) Bit
mov cr0, eax
jmp 0x08:protected_mode
bits 32
protected_mode:
; Set segment registers
mov ax, 0x10
mov ds, ax
mov es, ax
mov fs, ax
mov gs, ax
mov ss, ax
push dx ; Pass disk
call loader
jmp error_loop
bits 16
; Error handling
error_loop:
cli ; Disable interrupts
hlt ; Wait for interrupt; should wait forever
jmp error_loop ; Loop if returns anyway
disk_error:
mov si, disk_msg
call print
jmp error_loop
; Messages (NULL-terminated strings with newline/return)
hello_msg db "Hello world! Booting...", NEWLINE, RETURN, NULL
disk_msg db "Disk is invalid or unsupported", NEWLINE, RETURN, NULL
; Disk configuration data for DISK_READ interrupt (filled according to spec)
dap: ; Disk Address Packet
db 0x10 ; Disk address packet size
db 0 ; Always 0
.count:
dw 64 ; Number of sectors (512B each) to read ; 0x8000
.dest:
dw 0 ; Destination offset
dw 0 ; Destination segment
.lba:
dd 1 ; LBA number ; Inter stage is directly after first LBA (0)
dd 0 ; More storage bytes
; Global Descriptor Table (GDT)
; There are better explanations for the constants in definitions.asm
gdt:
dw .size - 1 + 8 ; GDT size
dd .start - 8 ; GDT start address
.start:
; Code
dw 0xffff ; Limit
dw 0x0000 ; Base (low 16 bits)
db 0x00 ; Base (mid 8 bits)
db GDT_PRESENT | GDT_DESCRIPTOR | GDT_EXECUTABLE | GDT_READWRITE ; Access
db GDT_GRANULARITY | GDT_SIZE | 0xf ; Granularity
db 0x00 ; Base (high 8 bits)
; Data
dw 0xffff ; Limit
dw 0x0000 ; Base (low 16 bits)
db 0x00 ; Base (mid 8 bits)
db GDT_PRESENT | GDT_DESCRIPTOR | GDT_READWRITE ; Access; Data isn't executable
db GDT_GRANULARITY | GDT_SIZE | 0xf ; Granularity
db 0x00 ; Base (high 8 bits)
.end:
.size: equ .end - .start
; The partition table gets inserted here (0x1b8 -> @440B)
times 0x1b8 - ($ - $$) db 0
times SECTOR_SIZE - 2 - ($ - $$) db 0 ; Fill until 512 (SECTOR_SIZE) - 2 bytes; -2 because of 2B signature
dw SECTOR_END_SIG ; Bootsector end signature
loader:
incbin "build/loader.bin"
; Limit to 0x8000 due to ext2 superblock start at 1024 on partition 1
times 0x8000 - ($ - $$) db 0
superblock:
|