boot.s (6396B)
1 ; Load code to 0x7c00 (Boot Sector address). 2 [org 0x7c00] 3 [bits 16] 4 5 MAGICOFF equ (0x7c00 + 510) 6 KERNLOAD equ 0x1000 7 KERNOFF equ 0x1000 8 STACKSIZE equ 8*1024*1024 9 10 section .text 11 global _start 12 13 ; Entry point. 14 _start: 15 cli ; Disable interrupts. 16 jmp 0x0000:zeroseg 17 zeroseg: 18 xor ax, ax ; Clear segment registers. 19 mov ds, ax 20 mov es, ax 21 mov fs, ax 22 mov gs, ax 23 mov ss, ax ; Stack starts at 0. 24 mov bp, _start 25 mov sp, bp 26 cld ; Read strings from low to high. 27 sti ; Enable interrupts back. 28 29 call a20_test 30 call a20_enable 31 call kernel_load ; Read kernel from disk to memory. 32 jmp pm_enter ; Enter 32-bit Protected Mode. 33 jmp $ ; Safety hang. 34 35 ; Test to see if the A20 line is enabled. 36 ; As a reference we will use the magic number 0xaa55 since we know it's always 37 ; going to have the same value and be located at address 0x7dfe (0x7c00 + 510). 38 ; If the return code in `ax` is 1, the A20 is enabled, and if it's 0 it's disabled. 39 a20_test: 40 pusha 41 42 mov ax, [MAGICOFF] ; The magic number is there. 43 mov dx, ax 44 45 ; We'll try to advance 1MB in memory. If the end result hasn't wrapped up 46 ; around zero, it means that our processor is able to access more than 1MB 47 ; of addresses, so the A20 is enabled. Using 0x7dfe again, the expected 48 ; address after the 1MB advance should be: 49 ; 50 ; 0x100000 + 0x7dfe = 0x107dfe 51 ; 52 ; The formula for memory segmentation is: 53 ; 54 ; address = segment * 16 + offset 55 ; 56 ; To find the offset, the segment will be 0xffff, so `0xffff * 16 = 0xffff0`. 57 ; Applying the formula to calculate our offset, we have: 58 ; 59 ; 0xffff0 + offset = 0x107dfe => 60 ; offset = 0x107dfe - 0xffff0 => 61 ; offset = 0x7e0e 62 63 push bx 64 mov bx, 0xffff 65 mov es, bx ; Assign value to the segment register. 66 pop bx 67 68 mov bx, 0x7e0e 69 mov dx, [es:bx] 70 71 cmp ax, dx 72 je cont ; If 0xaa55, the A20 line might be disabled. 73 popa 74 mov ax, 0x01 ; Success code 1. 75 ret 76 cont: 77 mov ax, [0x7dff] 78 mov dx, ax 79 80 push bx 81 mov bx, 0xffff ; Make another segment. 82 mov es, bx 83 pop bx 84 85 mov bx, 0x7e0f 86 mov dx, [es:bx] 87 88 cmp ax, dx ; Now it really is disabled if ax == [es:bx]. 89 je exit 90 popa 91 mov ax, 0x01 ; Success code 1. 92 ret 93 exit: 94 popa 95 xor ax, ax ; Error code 0. 96 ret 97 98 ; Enable the A20 line. We'll try enabling it using the following ways: 99 ; - BIOS interrupt. 100 ; - Keyboard controller. 101 ; - FastA20. 102 a20_enable: 103 pusha 104 105 ; BIOS interrupt. 106 mov ax, 0x2401 ; A20-Gate Active. 107 int 0x15 108 109 call a20_test 110 cmp ax, 0x01 111 je a20_done 112 jmp a20_fail 113 114 ; Keyboard controller. 115 sti ; Enable interrupts. 116 117 call a20_waitc 118 mov al, 0xad ; Disable controller. 119 out 0x64, al ; Send data to port 0x64. 120 call a20_waitc 121 mov al, 0xd0 ; We want to read from the controller. 122 out 0x64, al 123 124 call a20_waitd 125 in al, 0x60 ; Read data from port 0x60. 126 push ax ; Save data. 127 128 call a20_waitc 129 mov al, 0xd1 ; We want to send data. 130 out 0x64, al 131 132 call a20_waitc 133 pop ax 134 or al, 0x02 ; Write the second bit back. 135 out 0x60, al ; Send data to the data port. 136 137 call a20_waitc 138 mov al, 0xae ; Enable controller. 139 out 0x64, al 140 141 call a20_waitc 142 sti ; Enable interrupts again. 143 144 call a20_test 145 cmp ax, 0x01 146 je a20_done 147 jmp a20_fail 148 149 ; Wait for the keyboard controller to be available for receiving commands. 150 a20_waitc: 151 in al, 0x64 ; Read from port 0x64. 152 test al, 0x02 ; Test the second bit. 153 jnz a20_waitc ; If it's 1, it's busy. 154 ret 155 ; Wait for the keyboard controller to send data back. 156 a20_waitd: 157 in al, 0x64 ; Read from port 0x64 again. 158 test al, 0x01 ; Test the first bit. 159 jz a20_waitd ; If it's 1, the data is not ready to be sent. 160 ret 161 162 ; FastA20 163 in al, 0x92 ; FastA20 uses port 0x92. 164 or al, 0x02 ; Mask second bit. 165 out 0x92, al ; Send data back to port 0x92. 166 167 call a20_test 168 cmp ax, 0x01 169 je a20_done 170 jmp a20_fail 171 172 ; The A20 is enabled. Go on. 173 a20_done: 174 popa 175 ret 176 177 ; The A20 is disabled. 178 a20_fail: 179 mov si, str_a20_fail 180 call puts 181 jmp $ 182 183 ; Load kernel from disk to memory. 184 kernel_load: 185 pusha 186 mov ah, 0x42 ; BIOS read disk extended function. 187 mov dl, [BOOTDRV] ; Set boot drive. 188 mov si, disk_packet 189 int 0x13 ; BIOS read disk interrupt. 190 191 jc diskerr ; There's an error. Too bad. 192 popa 193 ret 194 diskerr: 195 mov si, str_diskerr 196 call puts 197 jmp $ ; There's nothing we can do at this point. 198 199 disk_packet: 200 size: db 0x10 201 zero: db 0x00 202 count: dw 0x0030 ; FIXME: i don't know why this works... 203 off16: dw KERNLOAD 204 seg16: dw 0x0000 205 lba: dq 1 206 207 ; Set up the GDT (Global Descriptor Table). 208 gdt: 209 gdt_null: ; offset = 0x0 210 dq 0x00000000 211 212 gdt_kernel_code: ; offset = 0x08 213 dw 0xffff ; segment limit 0..15 214 dw 0x0000 ; segment base 0..15 215 db 0x00 ; segment base 16..23 set for 0K 216 db 0x9f ; flags: type 217 db 0xcf ; flags: limit 218 db 0x00 ; segment base 24..32 219 220 gdt_kernel_data: ; offset = 0x10 221 dw 0xffff 222 dw 0x0000 223 db 0x00 224 db 0x93 225 db 0xcf 226 db 0x00 227 228 gdt_userland_code: ; offset = 0x18 229 dw 0xffff 230 dw 0x0000 231 db 0x00 232 db 0x9e 233 db 0xcf 234 db 0x00 235 236 gdt_userland_data: ; offset = 0x20 237 dw 0xffff 238 dw 0x0000 239 db 0x00 240 db 0x92 241 db 0xcf 242 db 0x00 243 244 gdt_ptr: 245 dw $ - gdt - 1 246 dd gdt 247 248 GDT_CODESEG equ gdt_kernel_code - gdt 249 GDT_DATASEG equ gdt_kernel_data - gdt 250 251 ; Set up the GDT and go into Protected Mode. 252 pm_enter: 253 cli ; Disable BIOS interrupts. 254 lgdt [gdt_ptr] ; Load the GDT. 255 mov eax, cr0 256 or eax, 1 ; Protected Mode Enable. 257 mov cr0, eax ; There's no going back now. 258 jmp GDT_CODESEG:pm_init 259 260 ; We're in Protected Mode. 261 [bits 32] 262 pm_init: 263 mov ax, GDT_DATASEG 264 mov ds, ax 265 mov es, ax 266 mov fs, ax 267 mov gs, ax 268 mov ss, ax 269 270 xor eax, eax 271 mov ebx, eax 272 mov ecx, eax 273 mov edx, eax 274 mov esi, eax 275 mov edi, eax 276 mov ebp, kern_stack_bottom 277 mov esp, kern_stack_top 278 279 ; Hand control over to the C kernel. Godspeed! You Black Emperor. 280 jmp GDT_CODESEG:KERNOFF 281 jmp $ 282 283 ; Print a null-terminated string. 284 [bits 16] 285 puts: 286 pusha 287 mov ah, 0x0e ; Tell BIOS to display a character. 288 loop: 289 mov al, [si] ; Load character. 290 cmp al, 0 ; Check for \0. 291 jne putc ; If it's not \0, print the character. 292 popa ; We're done, pop everything. 293 ret ; Return back to where we were. 294 putc: 295 int 0x10 ; BIOS print interrupt. 296 inc si ; `str++` 297 jmp loop ; Go to the next character. 298 299 ; String declarations. 300 str_diskerr: db "disk err: load", 0x0a, 0x0d, 0x00 301 str_a20_fail: db "a20: disabled", 0x0a, 0x0d, 0x00 302 303 ; Hard disk. 304 BOOTDRV: db 0x80 305 306 ; Padding to 512 bytes. The last 2 bytes will come from the magic number. 307 times 510 - ($ - $$) db 0 308 ; Magic number. 309 dw 0xaa55 310 311 section .bss 312 align 4096 313 kern_stack_bottom: 314 resb STACKSIZE 315 kern_stack_top: