summaryrefslogtreecommitdiff
path: root/src/bootloader
diff options
context:
space:
mode:
authorBrett Weiland <brett_weiland@bpcspace.com>2021-03-19 10:54:25 -0500
committerBrett Weiland <brett_weiland@bpcspace.com>2021-03-19 10:54:25 -0500
commit66289aa8ecfa07b20bad424eb9860b196641ef52 (patch)
tree9fb3915b5cb18d9f2c1f4648d3bf3fb56396509f /src/bootloader
first commit
Diffstat (limited to 'src/bootloader')
-rw-r--r--src/bootloader/bios_functions/bios_disk.asm38
-rw-r--r--src/bootloader/bios_functions/print.asm59
-rw-r--r--src/bootloader/bootloader.asm145
-rw-r--r--src/bootloader/cpu_check.asm100
-rw-r--r--src/bootloader/enter_kernel.asm80
-rw-r--r--src/bootloader/enter_kernel_backup87
-rw-r--r--src/bootloader/gdt.asm70
-rw-r--r--src/bootloader/multicore.asm10
-rw-r--r--src/bootloader/notes0
-rw-r--r--src/bootloader/video.asm179
10 files changed, 768 insertions, 0 deletions
diff --git a/src/bootloader/bios_functions/bios_disk.asm b/src/bootloader/bios_functions/bios_disk.asm
new file mode 100644
index 0000000..28fc74d
--- /dev/null
+++ b/src/bootloader/bios_functions/bios_disk.asm
@@ -0,0 +1,38 @@
+bios_disk:
+.load_sectors:
+pusha
+mov es, dx
+
+mov ah, 0x02 ; read disc sectors
+mov ch, 0x00 ; track 0
+mov dh, 0x00 ; head 0
+mov dl, [stage0.boot_device]
+
+int 0x13
+
+jc .failed
+
+mov ah, 0
+popa
+ret
+
+.failed:
+
+mov bx, .loadsectors_error
+mov cx, 0
+call bios_print
+
+push 0
+mov al, ah
+mov ah, 0 ; you need to clean up the bios print function!
+push ax
+mov cx, 1
+mov bx, sp
+call bios_print
+
+
+mov ah, 1
+popa
+ret
+
+.loadsectors_error: db "Error loading sectors: ", 0
diff --git a/src/bootloader/bios_functions/print.asm b/src/bootloader/bios_functions/print.asm
new file mode 100644
index 0000000..d071cd9
--- /dev/null
+++ b/src/bootloader/bios_functions/print.asm
@@ -0,0 +1,59 @@
+;TODO fix null problem, allow direct passes
+bios_print:
+;push ax ; we need to use ax, so let's push it in case whoever called needs it
+pusha
+mov ah, 0x0e ; tells bios we're in tty mode
+
+.print_loop:
+mov al, [bx] ; al is the char MEANT TO BE CX
+cmp al, 0 ; compare the char we're about to print with null
+je .fini ; if it is null, we gonna scoot the fuck outta here
+
+test cx, cx ; if cx is zero, ascii, otherwise hex string
+jne .print_hex
+int 0x10 ; bios call, actually prints the char
+.print_hex_ret:
+inc bx ; adds to the pointer
+jmp .print_loop ; goes back to loop start
+
+.fini:
+mov al, 0xd ; \r
+int 0x10
+mov al, 0xa ; \n
+int 0x10
+popa
+ret
+
+
+.print_hex:
+mov al, '0'
+int 0x10
+
+mov al, 'x'
+int 0x10
+
+mov al, [bx] ; shift bits to get first nibble
+shr al, 4
+call .bios_print_nibble
+
+mov al, [bx]
+and al, 0x0f
+call .bios_print_nibble
+
+mov al, ' '
+int 0x10
+
+jmp .print_hex_ret
+
+
+.bios_print_nibble:
+cmp al, 9 ; see if letter worthy
+ja .print_hex_letter
+add al, 0x30 ;'1'
+int 0x10
+ret
+
+.print_hex_letter:
+add al, 0x57 ;'a'
+int 0x10
+ret
diff --git a/src/bootloader/bootloader.asm b/src/bootloader/bootloader.asm
new file mode 100644
index 0000000..14116db
--- /dev/null
+++ b/src/bootloader/bootloader.asm
@@ -0,0 +1,145 @@
+[bits 16]
+[extern _kernel_size]
+[extern _bootloader_stage1_size]
+[extern _kernel_loc]
+
+;TODO clean up unreal mode
+jmp stage0
+times 3-($-$$) db 0x90 ; a temporary dirty fix to emulate a floppy disk insted of a hard risk
+times 59 db 0 ; (TODO support hard disks)
+
+stage0:
+
+jmp 0:.entry
+.entry:
+
+mov ax, 0
+mov ds, ax
+mov bp, ax
+
+mov ds, ax
+mov es, ax
+
+mov ax, 0x8fc0
+mov ss, ax
+mov ax, 0xfffe
+mov sp, ax
+
+mov al, 0x92
+or al, 2
+out 0x92, al
+
+mov [.boot_device], dl
+
+mov bx, .loadstage2_msg
+mov cx, 0
+call bios_print
+
+; TODO put in an error message and a maximum fail count
+.load_stage1:
+mov al, _bootloader_stage1_size
+mov cl, 0x2 ; read sector 2
+mov dx, 0x0 ; dest segment 0
+mov bx, 0x7e00 ; dest offst 0
+call bios_disk.load_sectors
+
+
+jmp mbr_end.entry
+
+.boot_device: db 0
+
+.loadstage2_msg: db "Loading (stage 2) bootloader...", 0
+
+%include "bootloader/bios_functions/bios_disk.asm"
+%include "bootloader/bios_functions/print.asm"
+
+times 510 - ($-$$) db 0
+dw 0xaa55
+
+%include "bootloader/gdt.asm"
+
+boot_msg:
+.debugmsg: db "debugeroni", 0
+.kernel_loaded: db `Kernel loaded!\r\nBooting to protected, then long mode...`, 0
+.stage2_loaded: db `Done loading bootloader!\r\nLoading kernel...`, 0
+
+
+
+
+mbr_end:
+.entry:
+; entering unreal mode
+mov bx, boot_msg.stage2_loaded
+mov cx, 0
+call bios_print
+
+
+cli
+push ds
+
+lgdt [protected_gdt.descriptor]
+
+mov eax, cr0
+or al, 1
+mov cr0, eax
+
+jmp $+2
+
+mov bx, 0x10 ; descriptor 2, the data descriptor
+mov ds, bx ; put it into the segment register
+mov es, bx ;
+
+and al, 0xfe
+mov cr0, eax
+
+pop ds
+sti
+;we are now in unreal mode
+
+mov cl, 5 ; starting sector TODO automate this
+mov edi, _kernel_loc
+.loop:
+mov al, 0x1 ; sector count
+mov dx, 0x0 ; dest addr segment
+mov bx, 0x500 ; dest addr offset
+call bios_disk.load_sectors
+
+
+inc cl
+
+push cx
+
+mov esi, 0x500
+mov ecx, 512
+a32 rep movsb
+nop
+
+mov bx, boot_msg.debugmsg
+mov cx, 0
+call bios_print
+
+pop cx
+
+cmp cl, _kernel_size
+je .loop_end
+
+jmp .loop
+
+.loop_end:
+
+mov bx, boot_msg.kernel_loaded
+mov cx, 0
+call bios_print
+
+
+call detect_arch
+call vbe_init
+done:
+
+call enter_longmode
+jmp $
+
+%include "bootloader/cpu_check.asm"
+%include "bootloader/video.asm"
+%include "bootloader/enter_kernel.asm"
+times 2048 - ($ - $$) db 0
diff --git a/src/bootloader/cpu_check.asm b/src/bootloader/cpu_check.asm
new file mode 100644
index 0000000..ffdd908
--- /dev/null
+++ b/src/bootloader/cpu_check.asm
@@ -0,0 +1,100 @@
+; TODO move this file to something like "system_check", now that we do ram checks here
+[extern _meminfo_loc]
+detect_arch:
+; todo: figure out how to just handle invalid opcode exceptions, it would be faster and (maybe?) easier
+; todo: digest this part a little better when it's _not_ 1am.
+pushfd ; pushes eflags to stack
+pushfd ; pushes eflags to stack a second time
+xor dword [esp], 0x00200000 ; inverses the ID bit to see if cpuid is a thing
+pop eax ;gets our modified cpuid back
+xor eax, [esp] ; checks with the first time we pushed it; the OG eflag
+popfd ; pops the flags back I guess?
+and eax, 0x00200000 ; checks to see if the bit was inverted
+jz .print_cpuid_error
+;now we detect if we support long slong (aka 64 bit)
+
+;https://www.amd.com/system/files/TechDocs/25481.pdf page 21
+;So apperently some dumbass engineers thought it would be funny to sell CPUs that have the long mode bit set without it being supported, dispite including it in their own documentation.
+;there's a work around by checking other bits, and it's a low priority TODO. However, I really think us coming accross this will be extremely rare.
+mov eax, 0x80000001
+cpuid ;cpuid returns into edx
+and edx, 0x0020000000 ; the 21st bit is cpuid.
+jz .print_long_error
+
+mov eax, 0x00000001
+cpuid
+and edx, 0x00000200
+jz .print_apic_error
+
+
+mov bx, .no_error
+mov cx, 0
+call bios_print
+
+
+;mapping memory
+mov ax, 0
+mov es, ax
+mov ax, _meminfo_loc
+mov di, ax
+
+mov eax, 0xe820
+mov ebx, 0
+mov ecx, 24
+mov edx, 0x534d4150
+int 0x15
+
+jc .print_mem_error
+cmp eax, 0x534D4150
+jne .print_mem_error
+
+.mem_detect_loop:
+add di, 24
+mov [di + 20], dword 1
+mov eax, 0xe820
+mov ecx, 24
+
+int 0x15
+jc .mem_detected
+cmp ebx, 0
+je .mem_detected
+jmp .mem_detect_loop
+.mem_detected:
+
+ret
+
+
+.print_cpuid_error:
+mov bx, .cpuid_error
+mov cx, 0
+call bios_print
+jmp $
+
+.print_long_error:
+mov bx, .arch_error ; lets just test for now
+mov cx, 0
+call bios_print
+jmp $
+
+.print_apic_error:
+mov bx, .apic_error
+mov cx, 0
+call bios_print
+
+.print_mem_error:
+mov bx, .mem_error
+mov cx, 0
+call bios_print
+
+
+.cpuid_error:
+ db "No CPUID capabilitity", 0x0
+.arch_error:
+ db "This operating system was compiled to run in 64 bit mode!", 0x0
+.apic_error:
+ db "No apic support", 0x0
+.mem_error:
+ db "Could not get information on memory!", 0x0
+.no_error:
+ db "CPU info gathered!", 0x0
+
diff --git a/src/bootloader/enter_kernel.asm b/src/bootloader/enter_kernel.asm
new file mode 100644
index 0000000..c9991e3
--- /dev/null
+++ b/src/bootloader/enter_kernel.asm
@@ -0,0 +1,80 @@
+[extern main]
+[extern _kernel_stack_loc]
+enter_longmode:
+cli
+
+; TODO check if a20 is already set
+mov al, 0x92
+or al, 2
+out 0x92, al
+
+lgdt [protected_gdt.descriptor]
+mov eax, cr0
+or eax, 0x1
+mov cr0, eax
+
+mov eax, 0x8
+mov ds, eax
+
+jmp 0x8:init_longmode
+
+bits 32
+init_longmode:
+mov ebp, 0xffff
+mov esp, ebp
+mov ax, PROTECTED_DATA_SEGMENT
+mov ds, ax
+mov ss, ax
+mov es, ax
+mov fs, ax
+mov gs, ax
+
+mov edi, 0x4000 ; 0x3000
+mov cr3, edi
+mov eax, 0
+mov ecx, 0xc00 ; 0x1000
+rep stosd
+mov edi, cr3
+
+
+mov DWORD [edi], 0x5003 ; pml4e[0] = pdpe
+add edi, 0x1000
+mov DWORD [edi], 0x6003 ; pdpe[0] = pde
+add edi, 0x1000
+mov DWORD [edi], 0x83 ; pde[0] = pte
+
+mov eax, cr4
+or eax, 1 << 5
+mov cr4, eax
+
+;end of setting up pages
+
+mov ecx, 0xc0000080
+rdmsr
+or eax, 1 << 8
+wrmsr
+
+mov eax, cr0
+or eax, 1 << 31 | 1 << 0 ; this is where we set paging and protected mode (respectively)!
+mov cr0, eax
+
+
+mov ecx, 0xc0000080
+rdmsr
+or eax, 1 << 8
+wrmsr
+
+mov eax, cr0
+or eax, 1 << 31
+mov cr0, eax
+
+lgdt [long_gdt.descriptor]
+
+jmp LONG_CODE_SEGMENT:enter_kernel
+enter_kernel:
+bits 64
+mov rbp, _kernel_stack_loc
+mov rsp, _kernel_stack_loc
+call main ; where we actually call the kernel
+jmp $
+ret
diff --git a/src/bootloader/enter_kernel_backup b/src/bootloader/enter_kernel_backup
new file mode 100644
index 0000000..7631f20
--- /dev/null
+++ b/src/bootloader/enter_kernel_backup
@@ -0,0 +1,87 @@
+[extern main]
+[extern _kernel_stack_loc]
+enter_longmode:
+cli
+
+; TODO check if a20 is already set
+mov al, 0x92
+or al, 2
+out 0x92, al
+
+lgdt [protected_gdt.descriptor]
+mov eax, cr0
+or eax, 0x1
+mov cr0, eax
+
+mov eax, 0x8
+mov ds, eax
+
+jmp 0x8:init_longmode
+
+bits 32
+init_longmode:
+mov ebp, 0xffff
+mov esp, ebp
+mov ax, PROTECTED_DATA_SEGMENT
+mov ds, ax
+mov ss, ax
+mov es, ax
+mov fs, ax
+mov gs, ax
+
+mov edi, 0x3000 ; this is where our page tables will be
+mov cr3, edi
+mov eax, 0 ; what we'll be putting there
+mov ecx, 4096 ; how many times we'll put it there
+rep stosd ; kinda like memset(&edi, ecx, edi)
+mov edi, cr3
+
+mov DWORD [edi], 0x4003 ; pml4e[0] = pdpe
+add edi, 0x1000
+mov DWORD [edi], 0x5003 ; pdpe[0] = pde
+add edi, 0x1000
+mov DWORD [edi], 0x6003 ; pde[0] = pte
+add edi, 0x1000
+
+mov ebx, 0x00000003 ; the flags
+mov ecx, 512; the loop counter, will map 2 mib
+
+.idmap_pte_loop:
+mov DWORD [edi], ebx
+add ebx, 0x1000 ; physical address. Should leave us at 0x7000
+add edi, 8 ; position in page table
+loop .idmap_pte_loop
+
+mov eax, cr4
+or eax, 1 << 5
+mov cr4, eax
+
+mov ecx, 0xc0000080
+rdmsr
+or eax, 1 << 8
+wrmsr
+
+mov eax, cr0
+or eax, 1 << 31 | 1 << 0 ; this is where we set paging and protected mode (respectively)!
+mov cr0, eax
+
+
+mov ecx, 0xc0000080
+rdmsr
+or eax, 1 << 8
+wrmsr
+
+mov eax, cr0
+or eax, 1 << 31
+mov cr0, eax
+
+lgdt [long_gdt.descriptor]
+
+jmp LONG_CODE_SEGMENT:enter_kernel
+enter_kernel:
+bits 64
+mov rbp, _kernel_stack_loc
+mov rsp, _kernel_stack_loc
+call main ; where we actually call the kernel
+jmp $
+ret
diff --git a/src/bootloader/gdt.asm b/src/bootloader/gdt.asm
new file mode 100644
index 0000000..d506cdf
--- /dev/null
+++ b/src/bootloader/gdt.asm
@@ -0,0 +1,70 @@
+protected_gdt:
+.gdt_start: ; we need to start with a null gdt
+dd 0
+dd 0
+
+.gdt_code:
+dw 0xffff
+dw 0x0000
+db 0x00
+db 10011010b
+db 11001111b
+db 0x0000
+
+
+.gdt_data:
+dw 0xffff
+dw 0x0000
+db 0x00
+db 10010010b
+db 11001111b
+db 0x0000
+
+.gdt_end:
+
+.descriptor:
+ dw .gdt_end - .gdt_start - 1
+dq .gdt_start
+
+PROTECTED_CODE_SEGMENT equ .gdt_code - .gdt_start
+PROTECTED_DATA_SEGMENT equ .gdt_data - .gdt_start
+
+
+long_gdt:
+.gdt_start:
+;and now we set up a temporary GDT creating a 1:1 mapping
+dw 0xffff
+dw 0
+db 0 ;this is the invalid GDT
+db 0
+db 1
+db 0
+
+;now for the code GDT:
+.gdt_code:
+dw 0 ; segment limit 15:00 (I don't think this matters in 64 bit mode!)
+dw 0 ; base address 15:00
+db 0 ; base address 23:16
+db 10011010b ;1st flags and type. The first four bits (1010) are type, and the last are flags. See https://www.cs.bham.ac.uk/~exr/lectures/opsys/10_11/lectures/os-dev.pdf
+db 10101111b ;1111 is segment limit continued. 0: available, 0: 64 bit (change?), 1: 32 bit segment, 1: granularity (shifts 3 hex didgets to get all of memory)
+db 0
+
+; the data GDT is the exact same, and it overlaps. We don't care, we'll be booting into long mode right after then programming in C
+; TODO: clarify once you figure out what you're doing
+.gdt_data:
+dw 0
+dw 0
+db 0
+db 10010010b
+db 00000000b
+db 0
+
+.gdt_end: ; later calculates offset in defs below
+
+
+.descriptor:
+ dw .gdt_end - .gdt_start - 1
+dq .gdt_start
+
+LONG_CODE_SEGMENT equ .gdt_code - .gdt_start
+LONG_DATA_SEGMENT equ .gdt_data - .gdt_start
diff --git a/src/bootloader/multicore.asm b/src/bootloader/multicore.asm
new file mode 100644
index 0000000..fa33ea5
--- /dev/null
+++ b/src/bootloader/multicore.asm
@@ -0,0 +1,10 @@
+multicore_boot:
+jmp $
+
+mov ecx, 0x0000001b
+rdmsr
+;mov [apic_register] dx:ax
+
+apic_register: dq 0
+multicore_msg1: db "CPUs available: ", 0
+
diff --git a/src/bootloader/notes b/src/bootloader/notes
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/src/bootloader/notes
diff --git a/src/bootloader/video.asm b/src/bootloader/video.asm
new file mode 100644
index 0000000..5b292c2
--- /dev/null
+++ b/src/bootloader/video.asm
@@ -0,0 +1,179 @@
+[extern _vbe_infoblock]
+vbe_init:
+.init_video:
+;getting edid
+
+mov ax, 0
+mov es, ax
+mov di, 0x500
+
+mov ax, 0x4f15
+mov bx, 0x1
+mov cx, 0
+mov dx, 0
+
+int 0x10
+
+cmp al, 0x4f
+jne .vbe_unsupported
+cmp ah, 0x00
+jne .edid_error
+
+
+mov ah, [0x500+58]
+shr ah, 4
+mov al, [0x500+56]
+push ax
+
+mov ah, [0x500+61]
+shr ah, 4
+mov al, [0x500+59]
+push ax
+
+mov al, BYTE [0x500+20]
+test al, 0x80
+jz .res_unsupported ; uses an analog signal
+
+shr al, 4
+and al, 7
+
+.cdp_6:
+cmp al, 0b001
+jne .cdp_8
+mov ax, 18
+jmp .colordepth_found
+
+.cdp_8:
+cmp al, 0b010
+jne .cdp_10
+mov ax, 24
+jmp .colordepth_found
+
+.cdp_10:
+cmp al, 0b011
+jne .cdp_12
+mov ax, 30
+jmp .colordepth_found
+
+.cdp_12:
+cmp al, 0b100
+jne .cdp_14
+mov ax, 36
+jmp .colordepth_found
+
+.cdp_14:
+cmp al, 0b101
+jne .cdp_16
+mov ax, 42
+jmp .colordepth_found
+
+.cdp_16:
+cmp al, 0b110
+jne .cdp_undefined
+mov ax, 48
+jmp .colordepth_found
+
+.cdp_undefined:
+mov ax, 24
+; TODO print warning, this only happens when we can't find bitdepth
+
+.colordepth_found:
+push ax
+
+
+
+
+; _______________________________________________________________________________________________________
+
+; When we get things sorted out, some time you should come back here and find a good resolution that isn't
+; native as a fallback.
+; Maybe redo this part in C?
+
+;getting available modes
+mov ax, 0
+mov es, ax
+mov ax, 0x500
+mov di, ax
+
+;get vbe capabilities
+mov ax, 0x4f00
+int 0x10
+cmp ax, 0x4f
+jne .vbe_generic_error
+cmp DWORD [0x500], "VESA"
+jne .vbe_generic_error
+
+; now we loop through the modes
+mov ebx, [0x500+14] ; pointer to modes
+mov di, 0x600
+mov ax, 0
+mov es, ax
+.mode_loop:
+mov ax, 0x4f01
+mov cx, [ebx]
+cmp cx, 0xffff
+je .res_unsupported
+
+int 0x10
+
+mov ax, [esp]
+cmp al, BYTE [0x600+25] ; might be wrong
+jne .mode_loop_next
+
+; width (1920)
+mov ax, [esp+4]
+cmp ax, [0x600+18]
+jne .mode_loop_next
+
+; height
+mov ax, [esp+2]
+cmp ax, [0x600+20]
+jne .mode_loop_next
+
+
+
+jmp .mode_found
+
+.mode_loop_next:
+add ebx, 2
+jmp .mode_loop
+
+.mode_found:
+mov ax, 0x4f02
+mov bx, cx
+int 0x10
+;getting rid of stack
+pop ax
+pop ax
+pop ax
+ret
+; ________________________________________________________________________________________________________
+
+.vbe_unsupported:
+mov bx, .unsupported_msg
+mov cx, 0
+call bios_print
+jmp $
+
+.vbe_generic_error:
+mov bx, .lazyerror_msg
+mov cx, 0
+call bios_print
+jmp $
+
+.edid_error:
+mov bx, .ediderror_msg
+mov cx, 0
+call bios_print
+jmp $
+
+.res_unsupported:
+mov bx, .res_unsupported_msg
+mov cx, 0
+call bios_print
+jmp $
+
+.unsupported_msg: db "Your BIOS doesn't support VESA. It's probably time to get a new computer!", 0
+.lazyerror_msg: db "A VESA error has occured.", 0
+.ediderror_msg: db "EDID error", 0
+.res_unsupported_msg: db "Native resolution not supported!", 0