summaryrefslogtreecommitdiff
path: root/boot
diff options
context:
space:
mode:
authorAiden Gall <aiden@aidengall.xyz>2024-05-14 19:43:28 +0100
committerAiden Gall <aiden@aidengall.xyz>2024-05-14 19:51:05 +0100
commit93585dc4da3be099e1ffe7e757aa7caff2e1f013 (patch)
treec1335acdd74cff12e545ce386ab2dbd176eaa473 /boot
initial commit
Diffstat (limited to 'boot')
-rw-r--r--boot/bios.inc128
-rw-r--r--boot/boot.asm144
-rw-r--r--boot/page.inc44
-rw-r--r--boot/segdesc.inc40
4 files changed, 356 insertions, 0 deletions
diff --git a/boot/bios.inc b/boot/bios.inc
new file mode 100644
index 0000000..ccc94e3
--- /dev/null
+++ b/boot/bios.inc
@@ -0,0 +1,128 @@
+macro bios_a20_enable {
+ mov ax, 0x2401
+ int 0x15
+}
+
+macro bios_vga_write_mode_3 {
+ mov ax, 0x03
+ int 0x10
+}
+
+; assumes dl = drive number
+macro bios_disk_read dst*, cyl*, head*, sector*, count*, err_label* {
+ local read_disk
+
+ assert 0 <= cyl & cyl <= 1023
+ assert 0 <= head & head <= 15
+ assert 1 <= sector & sector <= 17
+ assert 1 <= count & count <= 128
+
+ ; try to read disk up to 3 times
+ mov di, 3
+
+read_disk:
+ ; ah = 0x02, al = number of sectors to read
+ mov ax, (0x0200 or count)
+
+ ; es:bx = pointer to destination buffer
+ if dst = 0
+ xor bx, bx
+ else
+ mov bx, dst
+ end if
+
+ ; ch = track/cylinder number, cl = sector number
+ mov cx, ((cyl and 0xff) shl 8) or ((cyl and 0x300) shl 6) or sector
+
+ ; dh = head number
+ if head = 0
+ xor dh, dh
+ else
+ mov dh, head
+ end if
+
+ int 0x13
+
+ jc .error
+ cmp ax, count
+ jne .error
+
+ jmp .exit
+.error:
+ dec di
+ jz err_label
+
+ ; reset disk on error
+ xor ah, ah
+ stc
+ int 0x13
+ jc err_label
+ jmp read_disk
+.exit:
+}
+
+macro bios_e820_mmap mmap*, err_label* {
+ local e820, e820_entry
+e820:
+ virtual at di
+ label e820_entry
+ .base dq ?
+ .length dq ?
+ .type dd ?
+ .ext_attr dd ?
+ .sizeof = $ - e820_entry
+ end virtual
+
+ mov di, mmap + 4
+ xor ebx, ebx
+ xor bp, bp
+
+ mov edx, 0x0534d4150
+ mov eax, 0xe820
+ mov byte [.ext_attr], 0x01
+ mov ecx, .sizeof
+ int 0x15
+ jc err_label
+
+ mov edx, 0x0534d4150
+ cmp eax, edx
+ jne err_label
+
+ test ebx, ebx
+ jz err_label
+
+ jmp @f
+
+.e820_loop:
+ mov ax, 0xe820
+ mov byte [.ext_attr], 0x01
+ mov ecx, .sizeof
+ int 0x15
+ jc err_label
+
+ mov edx, 0x0534d4150
+@@:
+ jcxz .skip_entry
+
+ ; ACPI < 3.0
+ cmp cl, (.sizeof-4)
+ jbe .not_ext
+
+ ; ignore data flag
+ test byte [.ext_attr], 0x01
+ jz .skip_entry
+
+.not_ext:
+ mov ecx, dword [.length]
+ or ecx, dword [.length + 4]
+ jz .skip_entry
+
+ inc bp
+ add di, .sizeof
+.skip_entry:
+ test ebx, ebx
+ jnz .e820_loop
+
+ ; store e820 mmap entry count at beginning
+ mov [mmap], bp
+}
diff --git a/boot/boot.asm b/boot/boot.asm
new file mode 100644
index 0000000..eedde07
--- /dev/null
+++ b/boot/boot.asm
@@ -0,0 +1,144 @@
+format ELF64
+
+PML4_TABLE = 0x1000
+E820_MMAP = 0xf000
+
+MSR_EFER = 0xc0000080
+
+include 'bios.inc'
+include 'page.inc'
+include 'segdesc.inc'
+
+section '.text.boot' executable
+
+org 0x7c00
+use16
+ jmp far 0x0000:@f ; ensure cs = 0x0000
+@@:
+ xor ax, ax
+ mov ds, ax
+ mov es, ax
+
+ ; put stack directly bellow bootloader
+ mov ss, ax
+ mov sp, $$
+ cld
+
+ mov [boot_disk], dl
+
+ bios_disk_read ($$+0x200), 0, 0, 2, 57, disk_error
+
+ bios_e820_mmap E820_MMAP, e820_error
+
+ bios_a20_enable
+ bios_vga_write_mode_3
+
+ page_table_init_real PML4_TABLE
+
+ ; disable IRQs
+ mov al, 0xff
+ out 0xa1, al
+ out 0x21, al
+
+ lidt [dummy_idtr]
+
+ ; enable PAE (1<<5) and PGE (1<<7)
+ mov eax, (1 shl 7) or (1 shl 5)
+ mov cr4, eax
+
+ ; point cr3 at PML4
+ mov edx, edi
+ mov cr3, edx
+
+ ; enable long mode
+ mov ecx, MSR_EFER
+ rdmsr
+ or eax, (1 shl 8)
+ wrmsr
+
+ ; enable protected mode
+ mov ebx, cr0
+ or ebx, (1 shl 31) or (1 shl 0)
+ mov cr0, ebx
+
+ lgdt [gdtr]
+
+ jmp far (gdt.kcode-gdt):longmode
+
+e820_error:
+ ; change beginning of error message ("disk" -> "e820")
+ mov dword [errmsg], 0x30323865
+disk_error:
+ mov si, errmsg
+ mov ah, 0x0e
+ jmp @f
+.loop:
+ int 0x10
+@@:
+ lodsb
+ test al, al
+ jnz .loop
+
+ ; wait until keypress, then jump to reset vector (reboot)
+ xor ah, ah
+ int 0x16
+ jmp far 0xffff:0x0000
+
+use64
+longmode:
+ mov ax, (gdt.kdata-gdt)
+ mov ds, ax
+ mov es, ax
+ mov ss, ax
+
+ cli
+
+ ; copy kernel to 0x100000
+ mov ecx, (57*0x200)/8
+ mov esi, 0x7e00
+ mov edi, 0x100000
+ rep movsq
+
+ xor eax, eax
+ xor ebx, ebx
+ xor ecx, ecx
+ xor edx, edx
+ xor edi, edi
+
+ ; put stack at top of addressable memory
+ mov esp, 0x200000
+ mov ebp, esp
+
+ jmp 0x100000
+
+align 4
+dw 0
+label gdtr fword
+ .limit dw (gdt.sizeof - 1)
+ .addr dd gdt
+
+align 4
+errmsg db "disk error: press any key to reboot",0
+
+align 8
+gdt:
+ .null dq 0
+ .kcode seg_desc 0, 0, (GDT_CODE_64 or SEG_PRIV0)
+ .kdata seg_desc 0, 0, (GDT_DATA_64 or SEG_PRIV0)
+ .ucode seg_desc 0, 0, (GDT_CODE_64 or SEG_PRIV3)
+ .udata seg_desc 0, 0, (GDT_DATA_64 or SEG_PRIV3)
+ .sizeof = $ - gdt
+
+align 4
+boot_disk db 0, 0
+label dummy_idtr fword
+ .limit dw 0
+ .addr dd 0
+
+if ($-$$) <= 510
+ db 510-($-$$) dup 0
+else
+ display "ERR: boot sector image exceeds 512 bytes",10
+ assert ($-$$) <= 510
+end if
+db 0x55,0xaa
diff --git a/boot/page.inc b/boot/page.inc
new file mode 100644
index 0000000..ec2a8ad
--- /dev/null
+++ b/boot/page.inc
@@ -0,0 +1,44 @@
+PAGE_SIZE = 0x1000
+
+PAGE_PRESENT = (1 shl 0)
+PAGE_WRITE = (1 shl 1)
+
+macro page_table_init_real pml4_table* {
+ local page_table
+
+ ; zero out page table
+ mov edi, pml4_table
+ push di
+ mov ecx, (PAGE_SIZE*4)/4
+ xor eax, eax
+ cld
+ rep stosd
+ pop di
+
+ virtual at di
+ label page_table
+ .PML4T rb PAGE_SIZE
+ .PDPT rb PAGE_SIZE
+ .PDT rb PAGE_SIZE
+ .PT rb PAGE_SIZE
+ .sizeof = $ - page_table
+ end virtual
+
+ ; create first entry
+ mov word [.PML4T], (pml4_table + .PDPT - page_table) or PAGE_PRESENT or PAGE_WRITE
+ mov word [.PDPT], (pml4_table + .PDT - page_table) or PAGE_PRESENT or PAGE_WRITE
+ mov word [.PDT], (pml4_table + .PT - page_table) or PAGE_PRESENT or PAGE_WRITE
+
+ ; identity map pages
+ push di
+ mov di, (pml4_table + .PT - page_table)
+ mov ax, (PAGE_PRESENT or PAGE_WRITE)
+@@:
+ mov [di], eax
+ add eax, PAGE_SIZE
+ add di, 8
+ cmp di, pml4_table + .sizeof
+ jb @b
+
+ pop di
+}
diff --git a/boot/segdesc.inc b/boot/segdesc.inc
new file mode 100644
index 0000000..b4d14dc
--- /dev/null
+++ b/boot/segdesc.inc
@@ -0,0 +1,40 @@
+; segment type
+SEG_ACCESSED = 0001b
+SEG_CODE_READ = 0010b
+SEG_DATA_WRITE = 0010b
+SEG_CODE_CONFORM = 0100b
+SEG_DATA_EXPDOWN = 0100b
+SEG_EXECUTE = 1000b
+
+; privilege level
+SEG_PRIV0 = (00b shl 0x05)
+SEG_PRIV1 = (01b shl 0x05)
+SEG_PRIV2 = (10b shl 0x05)
+SEG_PRIV3 = (11b shl 0x05)
+
+; other flags
+SEG_DESCTYPE = (1b shl 0x04) ; descriptor type
+SEG_PRES = (1b shl 0x07) ; present
+SEG_SAVL = (1b shl 0x0C) ; available for system use
+SEG_LONG = (1b shl 0x0D) ; long mode
+SEG_SIZE = (1b shl 0x0E) ; size
+SEG_GRAN = (1b shl 0x0F) ; granularity
+
+; segment descriptor flag sets for convenience
+GDT_CODE_32 = SEG_DESCTYPE or SEG_PRES or SEG_SIZE or SEG_GRAN or SEG_EXECUTE or SEG_CODE_READ
+GDT_DATA_32 = SEG_DESCTYPE or SEG_PRES or SEG_SIZE or SEG_GRAN or SEG_DATA_WRITE
+GDT_CODE_64 = SEG_DESCTYPE or SEG_PRES or SEG_LONG or SEG_EXECUTE or SEG_CODE_READ
+GDT_DATA_64 = SEG_DESCTYPE or SEG_PRES or SEG_LONG or SEG_DATA_WRITE
+
+struc seg_desc base*, limit*, flag* {
+ assert 0x0 <= limit & limit <= 0x000fffff
+ assert 0x0 <= base & base <= 0xffffffff
+ assert (flag or 0xf0ff) = 0xf0ff
+
+ . dq ((limit shl 0x20) and 0x000f000000000000) or \
+ ((flag shl 0x28) and 0x00f0ff0000000000) or \
+ ((base shl 0x10) and 0x000000ff00000000) or \
+ ((base shl 0x20) and 0xff00000000000000) or \
+ ((base shl 0x10) and 0x00000000ffff0000) or \
+ ((limit shl 0x00) and 0x000000000000ffff)
+}