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