diff options
author | Marvin Borner | 2019-11-09 19:13:55 +0100 |
---|---|---|
committer | Marvin Borner | 2019-11-09 19:13:55 +0100 |
commit | a30a9b21c3e0af7996a551381a8f41075bada7ad (patch) | |
tree | e20309a87c03c3d3473aaf181c4671b53dfd01d9 | |
parent | 5f0475e159428e50e58f3772d6a759ff86b7b55a (diff) |
Started own implementation of asm bootloader
-rw-r--r-- | Makefile | 4 | ||||
-rw-r--r-- | src/bootloader/loader.asm | 556 | ||||
-rw-r--r-- | src/bootloader/stage1.asm | 73 | ||||
-rw-r--r-- | src/bootloader/stage2.asm | 109 |
4 files changed, 185 insertions, 557 deletions
@@ -40,7 +40,9 @@ build: clean # Create ISO mkdir -p ./iso/boot/; \ mv ./build/melvix.bin ./iso/boot/kernel.bin; \ - nasm ./src/bootloader/loader.asm -f bin -o ./iso/boot/boot.bin || exit; \ + nasm ./src/bootloader/stage1.asm -f bin -o ./build/stage1.bin || exit; \ + nasm ./src/bootloader/stage2.asm -f bin -o ./build/stage2.bin || exit; \ + cat ./build/stage1.bin ./build/stage2.bin > ./iso/boot/boot.bin; \ genisoimage -no-emul-boot -b boot/boot.bin -o ./build/melvix.iso ./iso; \ cross: diff --git a/src/bootloader/loader.asm b/src/bootloader/loader.asm deleted file mode 100644 index 40f11e5..0000000 --- a/src/bootloader/loader.asm +++ /dev/null @@ -1,556 +0,0 @@ -# Stolen from https://github.com/jlxip/jotadOS/tree/master/src/JBoot -# License: https://github.com/jlxip/jotadOS/blob/master/LICENSE - -BITS 16 -ORG 0x7C00 - -; First, save the boot drive ID, which is in DL. -; This way, we will know where we're booting from. -; We will only use that drive. -mov [bootDriveID], dl - -jmp start - -; Some neat functions and constants. -print: - mov ah, 0x0E - xor bh, bh - .print_L: - lodsb - test al, al - jz .print_end - int 0x10 - jmp .print_L - .print_end: - ret - -bootDriveID: db 0 - -dapack: - dapack_size: db 0x10 - dapack_null: db 0x00 - dapack_blkcount: dw 0x0001 ; 1 block = 1 sector (2K in ATAPI) - dapack_boffset: dw 0x9000 - dapack_bsegment: dw 0x0000 - dapack_start: dd 0x00000000 - dapack_upper_lba_bits: dd 0x00000000 - -readsector: - ; Input: eax = LBA - ; Output: blocks starting at dapack_boffset - mov dword [dapack_start], eax - - ; Invoke the interrupt - mov ah, 0x42 - mov dl, [bootDriveID] - xor bx, bx - mov ds, bx - mov si, dapack - int 0x13 - ret - -filenameLength dw 1 -filename dw 1 - -findfile: - ; Input: [esp+2] = directory record address - ; [esp+4] = length of filename (only low byte used) - ; [esp+6] = filename* - ; Output: In case of success: ax = 0, bx = (start of directory record) - ; In case of failure: ax = 1 - - ; First, save the data in the stack - mov bx, [esp+2] - mov ax, [esp+4] - mov [filenameLength], ax - mov ax, [esp+6] - mov [filename], ax - - .findfile_L: - ; Get the size (al) - mov al, [bx] - ; If it's zero, the kernel is not there. - test al, al - jz .findfile_notfound - - ; Check if filename length matches. - mov ah, [bx+32] - cmp ah, byte [filenameLength] - ; If they don't match, keep seeking. - jnz .findfile_keep - - ; At this point, they do match. Now compare the strings. - call findfile_check - test ax, ax - jz findfile_found - - ; The filenames don't match. - .findfile_keep: - mov al, [bx] - xor ah, ah - add bx, ax - jmp .findfile_L - - .findfile_notfound: - ; Not found. ax = 1 - xor ax, ax - inc ax - ret - - findfile_check: - ; Compares the filename. - ; Returns 0 in case of success, 1 otherwise. - pusha - add bx, 33 - mov ax, bx - ; ax is the base of the string in the CD. - - xor cx, cx - .findfile_check_L: - ; First character. - mov bx, [filename] - add bx, cx - mov dh, [bx] - ; Second character. - mov bx, ax - add bx, cx - mov dl, [bx] - ; Compare. - cmp dh, dl - jnz .findfile_check_fail - - ; At this point, the characters match. - ; Are we done? - inc cx - cmp cx, word [filenameLength] - jz .findfile_check_success ; Yes - jmp .findfile_check_L ; Nope - - .findfile_check_fail: - popa - xor ax, ax - inc ax - ret - .findfile_check_success: - ; We're done. - popa - xor ax, ax - ret - - findfile_found: - ; Found. ax = 0 - xor ax, ax - ret - -LOAD_PVD: - mov eax, 0x10 - PVD_L: - call readsector - mov bx, [dapack_boffset] - mov bl, [bx] - cmp bl, 0x01 - jz PVD_FOUND - inc eax - jmp PVD_L - PVD_FOUND: - ret - -checkA20: - ; Source: https://wiki.osdev.org/A20_Line - pushf - push ds - push es - push di - push si - cli - xor ax, ax - mov es, ax - not ax - mov ds, ax - mov di, 0x0500 - mov si, 0x0510 - mov al, byte [es:di] - push ax - mov al, byte [ds:si] - push ax - mov byte [es:di], 0x00 - mov byte [ds:si], 0xFF - cmp byte [es:di], 0xFF - pop ax - mov byte [ds:si], al - pop ax - mov byte [es:di], al - mov ax, 0 - jz checkA20_exit - mov ax, 1 - checkA20_exit: - pop si - pop di - pop es - pop ds - popf - ret - -welcome db "Melvix", 0x0A, 0x0D, 0x00 -nolba db "BIOS lacks support for LBA addressing.", 0x00 -noboot db "Boot directory could not be found.", 0x00 -noa20 db "A20 could not be enabled.", 0 -loading db "Loading kernel...", 0x0A, 0x0D, 0x00 -nokernel db "kernel.bin could not be found!", 0 -booting db "Booting...", 0x0A, 0x0D, 0x00 -nomem db "BIOS does not support memory detection!", 0 -memno20 db "BIOS returns memory detection with 24 bytes. This has never been seen!", 0 - -start: -; Clear screen. -mov ax, 0x0003 -int 0x10 - -; Print welcome. -mov si, welcome -call print - -; Check if LBA is supported by the BIOS. -mov ah, 0x41 -mov bx, 0x55AA -int 0x13 -jc lba_not_supported -cmp bx, 0xAA55 -jnz lba_not_supported - -; LBA is supported at this point. - -; Now, check whether A20 is enabled. -call checkA20 -test ax, ax -jnz A20_ENABLED - -; It's not enabled. Enable it through "Fast A20 Gate". -in al, 0x92 -or al, 2 -out 0x92, al - -; Check if it's enabled now. -call checkA20 -test ax, ax -jnz A20_ENABLED - -; It didn't work. Too bad for the user. -mov si, noa20 -call print -jmp $ - -A20_ENABLED: -; A20 is enabled at this point. - -; Enter Big Unreal Mode (to write past the 1M barrier). -; Thanks to: https://wiki.osdev.org/Unreal_Mode -cli ; Disable interrupts -push ds ; Save real mode -lgdt [gdtinfo] ; Load the temporal GDT - -mov eax, cr0 ; Switch to protected mode -or al, 1 -mov cr0, eax - -jmp $+2 ; Tell 386/486 to not crash (y tho?) - -mov bx, 0x08 ; Select descriptor 1 -mov ds, bx - -and al, 0xFE ; Back to real mode -mov cr0, eax -pop ds -; We are now in Big Unreal Mode. - -; About to begin the real shit. -mov si, loading -call print - - - -; Load Primary Volume Descriptor onto memory. -call LOAD_PVD -; PVD is now @ 0x9000 - -; Load the root directory. -mov bx, 0x9000 ; Base -add bx, 156 ; Directory Record of root -add bx, 2 ; LBA -mov eax, dword [bx] -call readsector - -; Find boot directory. -push boot -push boot_len -push 0x9000 -call findfile -add esp, 6 -test ax, ax -jz continue_BOOT - -; No boot directory. -mov si, noboot -call print -jmp $ - -continue_BOOT: -; Directory record address @ bx. -; Load boot directory record. -mov word [dapack_blkcount], 0x0001 ; The boot directory is not so big. -add bx, 2 ; Extent -mov eax, [bx] -call readsector - -; Find "kernel.bin". -push kernelbin -push kernelbin_len -push 0x9000 -call findfile -add esp, 6 -test ax, ax -jz continue_KERNEL - -; No kernel? -mov si, nokernel -call print -jmp $ - -continue_KERNEL: -; The kernel directory record is now @ bx. - -; Bear in mind that we can't read the ELF directly, as BIOS interrupts run on real mode. -; Instead, we'll be loading one block at a time (2K) to 0x9000. -mov word [dapack_blkcount], 1 -mov word [dapack_boffset], 0x9000 - -; We'll load the ELF at 2M, and then parse it and load the kernel at 1M. -; This will work as long as the kernel is below 1M of size. If that point ever -; comes, just change 0x200000 to 0x300000 or something. - -; Get the size of the ELF in blocks. -push bx -add bx, 10 ; Offset for size. -mov eax, [bx] ; Size in bytes -xor edx, edx ; Convert to blocks -mov ebx, 2048 -div ebx -inc eax ; Round up -pop bx -push eax ; Save the number of blocks to read. - -add bx, 2 -mov eax, [bx] ; Starting LBA of the ELF. -push eax ; Save it too. - -; Offsetless count. -xor ecx, ecx -LOAD_KERNEL: - ; Compute starting LBA for current block and read it. - mov eax, [esp] ; Offset - add eax, ecx ; + current offsetless LBA - call readsector - - ; Calculate the current block's starting position. - mov eax, ecx ; Current offsetless LBA - shl eax, 11 ; * 2048 - add eax, 0x200000 ; + 2M - - ; Move the block. I couldn't get "movs" working. - push ecx - xor ecx, ecx - .LOAD_KERNEL_L: - ; Get current doubleword. - mov ebx, ecx - add ebx, 0x9000 - mov ebx, dword [ebx] - - ; Move it. - mov edx, ebx - mov ebx, eax - add ebx, ecx - mov dword [ebx], edx - - ; Go for the next one. - add ecx, 4 - cmp ecx, 2048 - jl .LOAD_KERNEL_L - pop ecx - - inc ecx - cmp ecx, dword [esp+4] - jl LOAD_KERNEL - -; The whole ELF is now in memory! -mov si, booting -call print - -; Get the kernel what it needs. We'll put all of this at 0x9000. -; 0x9000: boot drive ID (byte) -mov dl, [bootDriveID] ; Reference to stage 1 -mov byte [0x9000], dl - -; 0xA000: available RAM (dword) -; We'll do it by BIOS function 0x15, eax=0xE820 -mov di, 0xA000 -mov eax, 0xE820 -xor ebx, ebx -mov ecx, 24 -mov edx, 0x534D4150 -int 0x15 - -; Check if everything went fine (BIOS supports it). -jc BIOS_NO_MEM -cmp eax, 0x534D4150 -jnz BIOS_NO_MEM -cmp cl, 20 -jnz BIOS_MEM_NO20 - -; Go for the next entries. -MEM_L: - ; Are we done? - test ebx, ebx - jz MEM_FINISHED - - ; Nope. Go for the next one. - mov ax, di - xor ch, ch - add ax, cx - mov di, ax - - mov eax, 0xE820 - mov ecx, 24 - int 0x15 - jmp MEM_L -MEM_FINISHED: -; The list is now at 0xA000. - -; Enter protected mode -mov eax, cr0 -or al, 1 -mov cr0, eax - -mov ax, 0x08 ; Select descriptor 1 -mov ds, ax -mov es, ax -mov fs, ax -mov gs, ax -mov ss, ax - -; Everything set. Now it's time to parse the ELF. -; jotadOS is x86, so I'll follow those specs. -; Guidance: https://wiki.osdev.org/ELF - -; First, get the number of entries in the program header table (offset +44, byte) -mov ebx, 0x20002C -mov dl, byte [ebx] -push dx ; Save it in the stack -; Now, the size of each one is 32 bits. Because the ELF contains 32 bit instructions. - -; Get the start of the program header. -mov ebx, 0x20001C -mov ebx, dword [ebx] -push ebx - -; Iterate thru each one -xor dh, dh -PHT: - xor eax, eax - mov al, dh ; Current entry - shl eax, 5 ; * 32 (size) - add eax, dword [esp] ; + Start - add eax, 0x200000 ; + Memory offset - mov ebx, eax - ; It's now at ebx. - - ; Check that the type of segment is 1 (load). - mov eax, dword [ebx] - cmp eax, 1 - jnz .PHT_ignore - - ; At this point, type type is 1. - ; We have to "copy p_filesz bytes from p_offset to p_vaddr". - - ; Get "p_offset" (offset +4, dword) into the stack. - add ebx, 4 - mov eax, dword [ebx] ; p_offset - add eax, 0x200000 ; + Memory offset - push eax - - ; Get "p_vaddr" (offset +8, dword) as well. - add ebx, 4 - mov eax, dword [ebx] ; p_vaddr - push eax - - ; Finally, "p_filesz" (offset +16, dword). - add ebx, 8 - mov eax, dword [ebx] - push eax - - ; Move the data! - push dx - xor ecx, ecx - .MOVE_KERNEL_L: - ; Get current doubleword. - mov ebx, [esp+10] - add ebx, ecx - mov ebx, dword [ebx] - - ; Move it. - mov eax, ebx - mov ebx, dword [esp+6] - add ebx, ecx - mov dword [ebx], eax - - ; Go for the next one. - add ecx, 4 - cmp ecx, dword [esp+2] - jl .MOVE_KERNEL_L - pop dx - add esp, 12 - - .PHT_ignore: - ; Next one! - inc dh - cmp dh, dl - jl PHT - -; Everything set. Use the section at the beginning to locate -; the entry point ('_start'). - -jmp (codedesc - gdt):protectedMode - -protectedMode: -BITS 32 -mov ebx, 0x100000 -mov eax, dword [ebx] -jmp eax - - -BITS 16 -BIOS_NO_MEM: - mov si, nomem - call print - jmp $ -BIOS_MEM_NO20: - mov si, memno20 - call print - jmp $ - -gdtinfo: - dw gdt_end - gdt - 1 ; Size of the table - dd gdt ; Its start -gdt dd 0, 0 -flatdesc db 0xff, 0xff, 0, 0, 0, 10010010b, 11001111b, 0 -codedesc db 0xff, 0xff, 0, 0, 0, 10011010b, 11001111b, 0 -gdt_end: - -lba_not_supported: - mov si, nolba - call print - jmp $ - -boot db "BOOT" -boot_len equ ($ - boot) -kernelbin db "KERNEL.BIN", 0x3B, "1" -kernelbin_len equ ($ - kernelbin)
\ No newline at end of file diff --git a/src/bootloader/stage1.asm b/src/bootloader/stage1.asm new file mode 100644 index 0000000..2f62a4d --- /dev/null +++ b/src/bootloader/stage1.asm @@ -0,0 +1,73 @@ +org 0x7c00 +bits 16 + +STAGE2 equ 0x800 +STAGE2_SECTORS equ 2+1 +TRACKS equ 2 +mov [BOOT_DRIVE], dl + +mov bp, 0x9000 +mov sp, bp + +start: + jmp load_stage2 + +print: + lodsb + or al, al + jz done + mov ah, 0eh + int 10h + jmp print +done: + ret + +clear: + pusha + mov ah, 0x00 + mov al, 0x03 + int 0x10 + popa + ret + +print_message: + xor ax, ax + mov ds, ax + mov es, ax + + call clear + mov si, msg + call print + +disk_load: + pusha + push dx + mov ah, 0x02 + mov al, dh + mov dh, 0x0 + int 0x13 + popa + ret + +load_stage2: + call print_message + mov cl, 2 + mov bx, STAGE2 + mov dh, 1 + mov dl, [BOOT_DRIVE] +load_sector: + call disk_load + cmp cl, STAGE2_SECTORS + je loaded + cmp cl, 15 + add cl, 1 + add bx, 512 + jmp load_sector +loaded: + ret + +msg db "Booting Melvix...", 0 +BOOT_DRIVE db 0 + +times 510 - ($-$$) db 0 +dw 0xAA55 diff --git a/src/bootloader/stage2.asm b/src/bootloader/stage2.asm new file mode 100644 index 0000000..e49eee7 --- /dev/null +++ b/src/bootloader/stage2.asm @@ -0,0 +1,109 @@ +org 0x800 +bits 16 + +KERNEL equ 0x1000 +KERNEL_SECTORS equ 24 + +call load_kernel + +int 0x12 +mov [0x600], ax + +call switch_to_pm + +gtd_start: +gdt_null: + dd 0x0 + dd 0x0 + +gdt_code: + dw 0xffff + dw 0x0 + db 0x0 + db 10011010b + db 11001111b + db 0x0 + +gdt_data: + dw 0xffff + dw 0x0 + db 0x0 + db 10010010b + db 11001111b + db 0x0 +gdt_end: + +gdt_descriptor: + dw gdt_end - gdt_start - 1 + dd gdt_start + +CODE_SEG equ gdt_code - gdt_start +DATA_SEG equ gdt_data - gdt_start + +switch_to_pm: + cli + lgdt [gdt_descriptor] + mov eax, cr0 + or eax, 0x1 + mov cr0, eax + jmp CODE_SEG:init_pm + +bits 32 +init_pm: + mov ax, DATA_SEG + mov ds, ax + mov ss, ax + mov es, ax + mov fs, ax + mov gs, ax + + mov ebp, 0x90000 + mov esp, 0x90000 + call begin_pm + +load_kernel: + mov ax, 3 + mov cl, 4 + mov ch, 0 + mov bx, KERNEL + mov dl, [BOOT_DRIVE] + mov dh, 0 + mov ch, 0 +load_sector: + mov ah, 0x02 + mov al, 1 + int 0x13 + push bx + mov bl, [Sector] + cmp bl, KERNEL_SECTORS + pop bx + je loaded + push bx + mov bl, [Sector] + inc bl + mov [Sector], bl + pop bx + inc cl + cmp cl, 18 + jne continue + add ch, 1 + add ch, 1 + mov cl, 1 +continue: + add bx, BytesPerSector + jmp load_sector +loaded: + ret + +begin_pm: + call KERNEL + jmp $ + +BytesPerSector equ 512 +NumHeads equ 2 +SectorsPerTrack equ 18 +Sector db 0 + +BOOT_DRIVE db 0 + +times 1024-($-$$) db 0 |