From 66289aa8ecfa07b20bad424eb9860b196641ef52 Mon Sep 17 00:00:00 2001 From: Brett Weiland Date: Fri, 19 Mar 2021 10:54:25 -0500 Subject: first commit --- src/bootloader/bios_functions/bios_disk.asm | 38 ++++++ src/bootloader/bios_functions/print.asm | 59 +++++++++ src/bootloader/bootloader.asm | 145 ++++++++++++++++++++++ src/bootloader/cpu_check.asm | 100 ++++++++++++++++ src/bootloader/enter_kernel.asm | 80 +++++++++++++ src/bootloader/enter_kernel_backup | 87 ++++++++++++++ src/bootloader/gdt.asm | 70 +++++++++++ src/bootloader/multicore.asm | 10 ++ src/bootloader/notes | 0 src/bootloader/video.asm | 179 ++++++++++++++++++++++++++++ 10 files changed, 768 insertions(+) create mode 100644 src/bootloader/bios_functions/bios_disk.asm create mode 100644 src/bootloader/bios_functions/print.asm create mode 100644 src/bootloader/bootloader.asm create mode 100644 src/bootloader/cpu_check.asm create mode 100644 src/bootloader/enter_kernel.asm create mode 100644 src/bootloader/enter_kernel_backup create mode 100644 src/bootloader/gdt.asm create mode 100644 src/bootloader/multicore.asm create mode 100644 src/bootloader/notes create mode 100644 src/bootloader/video.asm (limited to 'src/bootloader') 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 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 -- cgit v1.2.3