diff options
author | Marvin Borner | 2019-11-08 23:13:51 +0100 |
---|---|---|
committer | Marvin Borner | 2019-11-08 23:13:51 +0100 |
commit | 5f0475e159428e50e58f3772d6a759ff86b7b55a (patch) | |
tree | db2610eb6a6613ecba2958f3277b9ec68ee144d6 | |
parent | dd8c010755a3044f0832c7e732e1e3cdedb4a2ac (diff) |
Began implementation of non-grub bootloader
-rw-r--r-- | Makefile | 13 | ||||
-rw-r--r-- | src/bootloader/loader.asm | 556 | ||||
-rw-r--r-- | src/bootloader/make_initrd.c | 2 | ||||
-rw-r--r-- | src/kernel/boot.asm | 65 | ||||
-rw-r--r-- | src/kernel/graphics/vesa.c | 4 | ||||
-rw-r--r-- | src/kernel/kernel.c | 4 | ||||
-rw-r--r-- | src/kernel/linker.ld | 52 | ||||
-rw-r--r-- | src/kernel/paging/paging.c | 2 | ||||
-rw-r--r-- | src/kernel/system.h | 2 |
9 files changed, 623 insertions, 77 deletions
@@ -38,13 +38,10 @@ build: clean fi; \ # Create ISO - mkdir -p ./iso/boot/grub; \ - cp ./build/melvix.bin ./iso/boot/; \ - cp ./src/bootloader/grub.cfg ./iso/boot/grub/; \ - gcc ./src/bootloader/make_initrd.c -o ./build/make_initrd || exit; \ - ./build/make_initrd ./src/bootloader/test.txt test.txt || exit; \ - mv initrd.img ./iso/boot/melvix.initrd || exit; \ - grub-mkrescue -o ./build/melvix.iso ./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; \ + genisoimage -no-emul-boot -b boot/boot.bin -o ./build/melvix.iso ./iso; \ cross: @set -e; \ @@ -78,7 +75,7 @@ debug: @rm -f qemu.log @echo "Starting simulation" @echo "[SERIAL OUTPUT]" - @qemu-system-x86_64 -no-reboot -soundhw pcspk -M accel=kvm:tcg -vga std -serial stdio -rtc base=localtime -d cpu_reset -D qemu.log -m 512M -cdrom ./build/melvix.iso + @qemu-system-x86_64 -no-reboot -M accel=kvm:tcg -vga std -serial stdio -rtc base=localtime -d cpu_reset -D qemu.log -m 512M -cdrom ./build/melvix.iso @echo "[END OF CONNECTION]" .PHONY: build clean cross test debug
\ No newline at end of file diff --git a/src/bootloader/loader.asm b/src/bootloader/loader.asm new file mode 100644 index 0000000..40f11e5 --- /dev/null +++ b/src/bootloader/loader.asm @@ -0,0 +1,556 @@ +# 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/make_initrd.c b/src/bootloader/make_initrd.c index 2ee4716..6c2e7a0 100644 --- a/src/bootloader/make_initrd.c +++ b/src/bootloader/make_initrd.c @@ -32,8 +32,6 @@ int main(char argc, char **argv) { headers[i].magic = 0xBF; } - printf(headers[1].name); - FILE *wstream = fopen("./initrd.img", "w"); unsigned char *data = (unsigned char *) malloc(off); fwrite(&nheaders, sizeof(int), 1, wstream); diff --git a/src/kernel/boot.asm b/src/kernel/boot.asm index 2501ed9..0f967b7 100644 --- a/src/kernel/boot.asm +++ b/src/kernel/boot.asm @@ -1,44 +1,33 @@ -[bits 32] -global start +; The first section of the ELF will be used to locate the entry point. +section .ezlocation +dd _start -start: - mov esp, _sys_stack ; Points stack to stack area - jmp stublet +; Set stack +section .bss +align 16 +global STACK_BOTTOM +global STACK_TOP -; Align with 4 Bytes -ALIGN 4 -mboot: - ; Multiboot macros - MULTIBOOT_PAGE_ALIGN equ 1<<0 - MULTIBOOT_MEMORY_INFO equ 1<<1 - MULTIBOOT_AOUT_KLUDGE equ 1<<16 - MULTIBOOT_HEADER_MAGIC equ 0x1BADB002 - MULTIBOOT_HEADER_FLAGS equ MULTIBOOT_PAGE_ALIGN | MULTIBOOT_MEMORY_INFO | MULTIBOOT_AOUT_KLUDGE - MULTIBOOT_CHECKSUM equ -(MULTIBOOT_HEADER_MAGIC + MULTIBOOT_HEADER_FLAGS) - EXTERN code, bss, end +STACK_BOTTOM: +resb 0x4000 +STACK_TOP: - ; GRUB Multiboot header - dd MULTIBOOT_HEADER_MAGIC - dd MULTIBOOT_HEADER_FLAGS - dd MULTIBOOT_CHECKSUM - ; AOUT kludge - dd mboot - dd code - dd bss - dd end - dd start +section .text -; Endless loop +global _start extern kernel_main -stublet: - ; Load multiboot information - push esp - push ebx +_start: + mov esp, STACK_TOP + push ebx + push eax - cli - call kernel_main - jmp $ + call kernel_main + cli + +hlt_L: + hlt + jmp hlt_L %include "src/kernel/gdt/gdt.asm" @@ -68,7 +57,7 @@ switch_to_user: push test_user iret -; Store the stack -SECTION .bss - resb 0x2000 ; Reserve 8KiB -_sys_stack:
\ No newline at end of file +section .sizedetect +global ASM_KERNEL_END +ASM_KERNEL_END: + ; Kernel size detection diff --git a/src/kernel/graphics/vesa.c b/src/kernel/graphics/vesa.c index a43d486..447c447 100644 --- a/src/kernel/graphics/vesa.c +++ b/src/kernel/graphics/vesa.c @@ -98,7 +98,7 @@ void set_optimal_resolution() { uint16_t highest = 0; - for (uint16_t *mode = video_modes; *mode != 0xFFFF; mode++) { + /*for (uint16_t *mode = video_modes; *mode != 0xFFFF; mode++) { struct vbe_mode_info *mode_info = vbe_get_mode_info(*mode); if (mode_info == 0 || (mode_info->attributes & 0x90) != 0x90 || @@ -125,7 +125,7 @@ void set_optimal_resolution() { vbe_bpl = mode_info->bpp >> 3; fb = (unsigned char *) mode_info->framebuffer; } - } + }*/ if (highest == 0) { serial_write("Mode detection failed!\nTrying common modes...\n"); diff --git a/src/kernel/kernel.c b/src/kernel/kernel.c index c2ab13e..214ecf0 100644 --- a/src/kernel/kernel.c +++ b/src/kernel/kernel.c @@ -36,12 +36,12 @@ void kernel_main(struct multiboot *mboot_ptr) { get_smbios(); // Setup initial ramdisk - assert(mboot_ptr->mods_count > 0); + /*assert(mboot_ptr->mods_count > 0); uint32_t initrd_location = *((uint32_t *) mboot_ptr->mods_addr); uint32_t initrd_end = *(uint32_t *) (mboot_ptr->mods_addr + 4); paging_set_used(0, (initrd_end >> 12) + 1); fs_root = initialise_initrd(initrd_location); - initrd_test(); + initrd_test();*/ // User mode! /* COMMENTED FOR DEVELOPMENT OF KERNEL diff --git a/src/kernel/linker.ld b/src/kernel/linker.ld index 2abbaeb..9062b27 100644 --- a/src/kernel/linker.ld +++ b/src/kernel/linker.ld @@ -1,27 +1,33 @@ -ENTRY(start) -SECTIONS -{ - .text 0x100000 : - { - code = .; _code = .; __code = .; - *(.text) - . = ALIGN(4096); - } +ENTRY(_start) - .data : - { - data = .; _data = .; __data = .; - *(.data) - *(.rodata) - . = ALIGN(4096); - } +/* Where the sections of the object files will be put in the final image */ +SECTIONS { + /* Begin @ 1 MB */ + . = 1M; - .bss : - { - bss = .; _bss = .; __bss = .; - *(.bss) - . = ALIGN(4096); - } + /* Put the multiboot header. Next, the .text section. */ + .text BLOCK(4K) : ALIGN(4K) { + *(.ezlocation) + *(.text) + } - end = .; _end = .; __end = .; + /* Read-only data. */ + .rodata BLOCK(4K) : ALIGN(4K) { + *(.rodata) + } + + /* Read-write data (initialized) */ + .data BLOCK(4K) : ALIGN(4K) { + *(.data) + } + + /* Read-write data (uninitialized) and stack */ + .bss BLOCK(4K) : ALIGN(4K) { + *(COMMON) + *(.bss) + } + + .sizedetect BLOCK(4K) : ALIGN(4K) { + *(.sizedetect) + } } diff --git a/src/kernel/paging/paging.c b/src/kernel/paging/paging.c index bb3b1df..a6bf5cc 100644 --- a/src/kernel/paging/paging.c +++ b/src/kernel/paging/paging.c @@ -19,7 +19,7 @@ void paging_install() { // TODO: Calculate max memory paging_set_present(0, 0x1000000); - paging_set_used(0, ((uint32_t) end >> 12) + 1); + paging_set_used(0, ((uint32_t) ASM_KERNEL_END >> 12) + 1); paging_enable(); vga_log("Installed paging", 4); diff --git a/src/kernel/system.h b/src/kernel/system.h index f87a8d5..18a013b 100644 --- a/src/kernel/system.h +++ b/src/kernel/system.h @@ -4,7 +4,7 @@ /** * The kernel end */ -extern void *end; +extern void *ASM_KERNEL_END; /** * Initialize the basic features of the OS |