Created
February 9, 2022 13:09
-
-
Save jondlove/3c048f1701bfba13bd802b3fc9d2a783 to your computer and use it in GitHub Desktop.
A simple bootloader for booting on non-EFI x86 systems
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
[bits 16] | |
; X86_BOOT.ASM | |
; | |
; Written by Jonathan Love, 2017 | |
; In my third/fourth/more attempt at actually getting to Protected Mode | |
; | |
; Bootloader for x86 MBR booting | |
; | |
; For interrupt codes, refer http://www.ctyme.com/intr/int.htm | |
; For data ports, refer to Intel's PCH Documentation | |
; For general x86 details, refer to Intel's SDM Documentation | |
; Current State: | |
; Mode: Real | |
; A20 Line: No | |
; Stack: No | |
; Put memory into known configuration | |
; Data segment is within our loaded code, at 0x07c0 | |
mov ax, 0x07c0 | |
mov ds, ax | |
; Configure our stack to *end* right after our bootloader | |
mov ax, 0x07e0 | |
mov ss, ax | |
; Gives us 8K of stack space | |
mov sp, 0x2000 | |
; Current State: | |
; Mode: Real | |
; A20 Line: No | |
; Stack: 0x7e00 -> 0x9E00 | |
; Print some words on the screen | |
call clear_screen | |
push 0x0000 | |
call movecursor | |
push msg_boot | |
call print | |
push msg_newline | |
call print | |
; Current State: | |
; Mode: Real | |
; A20 Line: No | |
; Stack: 0x7e00 -> 0x9E00 | |
; Enable the A20 line, so we can access >1MB RAM | |
; We use FAST A20, and assume A20 is disabled | |
; There are many caveats. We ignore these for now | |
; Ref. Intel PCH, "PORT92" | |
in al, 0x92 | |
or al, 2 ; Bit 1 on PORT92 controls the ALT_A20_GATE | |
out 0x92, al ; Special Port | |
; Current State: | |
; Mode: Real | |
; A20 Line: Yes | |
; Stack: 0x7e00 -> 0x9E00 | |
; Let's use some protection | |
push msg_protected | |
call print | |
push msg_newline | |
call print | |
; We'll use a protected, flat, unpaged model | |
; The kernel can change as necessary | |
; Ref: Intel SDM, Vol 3A. Ch 3. | |
cli ; Disable interrupts | |
lgdt [gdtr] ; Load GDT Register | |
mov eax, cr0 ; Load control register flags | |
or al, 1 ; Set (PE) Protection Enable | |
mov cr0, eax | |
; Current State: | |
; Mode: Protected | |
hlt | |
clear_screen: | |
; Clears the screen | |
; | |
; @param none | |
; @returns none | |
push bp | |
mov bp, sp | |
pusha | |
mov ah, 0x07 ; Int 10h: scroll window | |
mov al, 0x00 ; Scroll window far enough to clear | |
mov bh, 0x07 ; White on black | |
mov cx, 0x0000 ; Top left of screen | |
mov dh, 0x18 ; 18h = 24 rows | |
mov dl, 0x4f ; 4fh = 79 cols | |
int 0x10 ; Interrupt: video, scroll window down | |
popa | |
mov sp, bp | |
pop bp | |
ret | |
movecursor: | |
; Moves the cursor to a specified location | |
; | |
; @param word Location to move to | |
; @returns none | |
push bp | |
mov bp, sp | |
pusha | |
mov dx, [bp+4] ; Get the first parameter | |
mov ah, 0x02 ; Int 10h: Set cursor position | |
mov bh, 0x00 ; Screen page | |
int 0x10 | |
popa | |
mov bp, sp | |
pop bp | |
ret | |
print: | |
; Print string to screen | |
; | |
; @param word Address of message to write | |
; @returns none | |
push bp | |
mov bp, sp | |
pusha | |
mov si, [bp+4] ; Get the first parameter, put in source index | |
mov bh, 0x00 ; Screen page | |
mov bl, 0x00 ; Foreground colour | |
mov ah, 0x0e ; Int 10h: write char | |
.char: | |
mov al, [si] ; Get current char from source index | |
add si, 1 ; Move to next char | |
or al, 0 | |
je .return ; Finish printing, if we've reached null terminator | |
int 0x10 ; Else, print char | |
jmp .char | |
.return: | |
popa | |
mov sp, bp | |
pop bp | |
ret | |
msg_boot: db "Booting...", 0 ; Null terminated string | |
msg_newline: db 10, 13, 0 | |
msg_protected: db "Loading Protected Mode", 0 | |
gdtr: | |
; Global Descriptor Table | |
; Intel SDM Vol 3A. Ch 3. | |
; Contains a null segment, a code segment, and a data segment | |
; We have no task-state-segment (TSS) because we don't have | |
; multi-tasking. There is also no Thread Local Storage | |
; We have no userland segments, as there is no userland. | |
; Null Segment | |
dq 0 | |
; Kernel Code Segment | |
dw 0xffff ; Segment limit 0:15 | |
dw 0x0000 ; Base 0:15 | |
db 0x00 ; Base 16:23 | |
db 10011010b ; Access Byte: | |
; Present: 1 | |
; Priv Lvl: 00 | |
; Descriptor: 1 (code/data) | |
; Type: | |
; Executable: 1 (yes, code segment) | |
; Conforming: 0 (can only access in specified privlvl) | |
; Read/Write: 1 (Code sector is readable) | |
; Accessed: 0 (Segment has not been accessed) | |
db 11001111b ; Flags/Limit 16:19: | |
; Flags | |
; Granularity: 1 (4 KiB blocks) | |
; Size: 1 (32-bit protected mode) | |
; 64-bit segment: 0 (No) | |
; [Reserved]: 0 | |
; Limit 16:19: 1111 | |
db 0x00 ; Base 24:31 | |
; Kernel Data Segment | |
dw 0xffff ; Segment limit 0:15 | |
dw 0x0000 ; Base 0:15 | |
db 0x00 ; Base 16:23 | |
db 10010010b ; Access Byte: | |
; Present: 1 | |
; Priv Lvl: 00 | |
; Descriptor: 1 (code/data) | |
; Type: | |
; Executable: 0 (no, data segment) | |
; Conforming: 0 (can only access in specified privlvl) | |
; Read/Write: 1 (Data sector is read/writeable) | |
; Accessed: 0 (Segment has not been accessed) | |
db 11001111b ; Flags/Limit 16:19: | |
; Flags | |
; Granularity: 1 (4 KiB blocks) | |
; Size: 1 (32-bit protected mode) | |
; 64-bit segment: 0 (No) | |
; [Reserved]: 0 | |
; Limit 16:19: 1111 | |
db 0x00 ; Base 24:31 | |
; Padding, to ensure this code, when compiled, is 512 bytes | |
times 510-($-$$) db 0 | |
; Signature to indicate bootable MBR partition | |
dw 0xAA55 |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment