From c06dbb72809a0de5977021a5e98c717535e84dd3 Mon Sep 17 00:00:00 2001 From: Nocturn9x Date: Wed, 16 Nov 2022 15:25:53 +0100 Subject: [PATCH] Initial kernel work --- .gitignore | 3 +- src/bootloader/mbr.s | 170 ++++++++++++------------------------- src/bootloader/util/disk.s | 3 +- src/bootloader/util/io.s | 23 +++-- 4 files changed, 74 insertions(+), 125 deletions(-) diff --git a/.gitignore b/.gitignore index 9227b93..2ba8428 100644 --- a/.gitignore +++ b/.gitignore @@ -68,4 +68,5 @@ _deps # TSOS stuff *.bin -dist/ \ No newline at end of file +dist/ + diff --git a/src/bootloader/mbr.s b/src/bootloader/mbr.s index edf6cb3..ec5848b 100644 --- a/src/bootloader/mbr.s +++ b/src/bootloader/mbr.s @@ -3,124 +3,64 @@ ; do some basic setup and then call into the kernel [org 0x7c00] ; Address where the code expects to be loaded in. The BIOS always loads us here +kernel_offset: equ 0x1000 + +; We save the value of the current boot drive +mov [boot_drive], dl + +; Now we setup the stack by setting the +; base pointer to address 0x8000. The address +; itself doesn't matter as long as it's far +; enough away from memory already in use by +; the BIOS +mov bp, 0x9000 +mov sp, bp +call bios_cls +mov si, real_mode_msg +call bios_println +call load_kernel +call switch_to_protected_mode +jmp $ ; Keeps jumping at the current address (loops forever) + + +; Now we include our "function definitions" (after the +; loop, so they're never executed unless explicitly called) +%include "src/bootloader/util/disk.s" +%include "src/bootloader/util/io.s" +%include "src/bootloader/gdt.s" +%include "src/bootloader/switch32.s" + [bits 16] ; All x86 CPUs start in 16 bit (aka "real") mode, so we tell nasm to emit 16-bit code +load_kernel: ; Loads the kernel into memory + mov si, loading_kernel_msg + call bios_println + mov bx, kernel_offset + mov dh, 1 + mov dl, [boot_drive] + call load_disk + ret + +; Here we define our variables: They need to be defined after the +; halting because otherwise they will be executed as code +real_mode_msg: db "Booted in real mode", 0 +protected_mode_msg: db "Switched to protected mode", 0 +loading_kernel_msg: db "Loading kernel", 0 +boot_drive: db 0 -start: - ; Copied from https://github.com/limine-bootloader/limine/blob/trunk/stage1/hdd/bootsect.asm. - ; Quote: - ; Some BIOSes will do a funny and decide to overwrite bytes of code in - ; the section where a FAT BPB would be, potentially overwriting - ; bootsector code. Avoid that by filling the BPB area with dummy values. - ; Some of the values have to be set to certain values in order to boot - ; on even quirkier machines. - ; Source: https://github.com/freebsd/freebsd-src/blob/82a21151cf1d7a3e9e95b9edbbf74ac10f386d6a/stand/i386/boot2/boot1.S - jmp skip_bpb - nop - bpb: - times 3-($-$$) db 0 - .bpb_oem_id: db "TSOS " - .bpb_sector_size: dw 512 - .bpb_sects_per_cluster: db 0 - .bpb_reserved_sects: dw 0 - .bpb_fat_count: db 0 - .bpb_root_dir_entries: dw 0 - .bpb_sector_count: dw 0 - .bpb_media_type: db 0 - .bpb_sects_per_fat: dw 0 - .bpb_sects_per_track: dw 18 - .bpb_heads_count: dw 2 - .bpb_hidden_sects: dd 0 - .bpb_sector_count_big: dd 0 - .bpb_drive_num: db 0 - .bpb_reserved: db 0 - .bpb_signature: db 0 - .bpb_volume_id: dd 0 - .bpb_volume_label: db "TSOS " - .bpb_filesystem_type: times 8 db 0 - ; The skip_bpb and initialize_cs code is adapted - ; from the Limine bootloader as well - skip_bpb: - cli - cld - jmp 0x0000:initialise_cs - initialise_cs: - xor si, si - mov ds, si - mov es, si - mov ss, si - sti - ; We're not made for floppy disks, these are dead anyways. - ; So if the value the BIOS passed is <0x80, just assume it has passed - ; an incorrect value. - cmp dl, 0x80 - jb invalid_boot_device - ; Values above 0x8f are dubious so we assume we weren't booted properly - ; for those either - cmp dl, 0x8f - ja invalid_boot_device - continue: - ; First off, we setup the stack by setting the - ; base pointer to address 0x8000. The address - ; itself doesn't matter as long as it's far - ; enough away from memory already in use by - ; the BIOS - mov bp, 0x8000 - mov sp, bp ; The stack starts out empty, so sp == bp +[bits 32] +BEGIN_32BIT: ; After the switch we will get here + mov esi, protected_mode_msg + ; My modified print function takes an offset + ; to add to the start of the video memory that + ; is added before writing. We skip the first + ; 320 bytes so that we don't overwrite the log + ; messages we may have written beforehand + mov ecx, 0x140 + call vga_println + call kernel_offset + jmp $ - ; Since we have a stack, we can now call functions, - ; so we print a simple startup message using BIOS - ; routines - mov si, startup_msg - call bios_print - ; We also inform the user we're loading a few - ; sectors from the boot drive - mov si, disk_read_info - and dx, 0xff ; Gets rid of the high 8 bits of dx so - ; we only print the hex value of dh, - ; which is the type of storage device - ; we're booting from (0 = floppy, - ; 1 = floppy2, 0x80 = hdd, 0x81 = hdd2) - call bios_print - call bios_printh - call bios_newline - - ; We load data from the current boot drive. The data is copied - ; to memory starting at address 0x9000 - mov bx, 0x9000 - mov dh, 2 ; Read 3 sectors (2 for our dummy sectors, 1 for our variables) - ; The dl register is already set by the BIOS - call load_disk - - ; Now we retrieve the test data we placed in the other - ; sectors and print it in hexadecimal format - mov dx, [0x9000] - call bios_printh ; Should print 0xDADA - call bios_newline - mov dx, [0x9000 + 512] - call bios_printh ; Should print OxFACE - - endless_loop: - jmp $ ; Keeps jumping at the current address (loops forever) - - invalid_boot_device: - mov si, invalid_boot_device_msg - call bios_println - - ; Now we include our "function definitions" (after the - ; loop, so they're never executed unless explicitly called) - %include "src/bootloader/util/disk.s" - %include "src/bootloader/util/io.s" - - ; Here we define our variables: They need to be defined after the - ; halting because otherwise they will be executed as code - startup_msg: db "TSOS is starting up", 0xA, 0xD, 0 - invalid_boot_device_msg: db "Invalid boot device", 0 - -; padding and magic number +; Padding and magic number times 510 - ($-$$) db 0 dw 0xaa55 - -; We add more sectors to our binary so we can read them -times 256 dw 0xdada ; sector 2 = 512 bytes -times 256 dw 0xface ; sector 3 = 512 bytes diff --git a/src/bootloader/util/disk.s b/src/bootloader/util/disk.s index 809273e..b222350 100644 --- a/src/bootloader/util/disk.s +++ b/src/bootloader/util/disk.s @@ -43,6 +43,5 @@ disk_loop: jmp $ -disk_read_info: db "Booting from disk type ", 0 disk_read_error_msg: db "Read error: ", 0 -disk_sectors_error_msg: db "Read error (wrong number of sectors)", 0 +disk_sectors_error_msg: db "Sector read count error", 0 diff --git a/src/bootloader/util/io.s b/src/bootloader/util/io.s index f7ea815..8da6445 100644 --- a/src/bootloader/util/io.s +++ b/src/bootloader/util/io.s @@ -97,6 +97,14 @@ bios_printh: popa ret +bios_cls: + pusha + mov ah, 0x00 + mov al, 0x03 ; text mode 80x25 16 colours + int 0x10 + popa + ret + HEX_OUT: db '0x0000', 0 ; reserve memory for our new string @@ -108,23 +116,24 @@ VMEM_START: equ 0xb8000 ; Video memory always starts at this address ; the first byte is the ASCII codepoint to be printed, while the next ; octet represents additional formatting information (color, blink, ; underline, etc.). More info: https://en.wikipedia.org/wiki/VGA_text_mode -WHITE_ON_BLACK: equ 0x0f +TEXT_COLOR: equ 0x07 vga_print: ; Prints a null-terminated string located - ; on the esi register + ; on the esi register. Offsets the write + ; by ecx bytes pusha mov edx, VMEM_START - + add edx, ecx vga_print_loop: mov al, [esi] - mov ah, WHITE_ON_BLACK + mov ah, TEXT_COLOR cmp al, 0 je vga_print_done ; If we're at the null byte, we exit mov [edx], ax ; Write the 2-byte character to video memory inc esi ; Go to the next character in the string - add ebx, 2 ; Go to the next character in video memory + add edx, 2 ; Go to the next character in video memory jmp vga_print_loop vga_print_done: @@ -137,8 +146,8 @@ vga_println: ; esi register and terminates it with a newline pusha call vga_print - mov esi, NEWLINE - call vga_print + ;mov esi, NEWLINE + ;call vga_print popa ret