From 8ddc5288b9e89bd5998b770b55865af10e4e2d88 Mon Sep 17 00:00:00 2001 From: Nocturn9x Date: Fri, 18 Nov 2022 22:19:40 +0100 Subject: [PATCH] Split bootloader into 2 stages. vga_print is broken --- src/boot/mbr.s | 91 ++++++++++++++++++++++++++++---------------- src/boot/switch32.s | 11 +----- src/boot/util/disk.s | 7 +++- src/boot/util/io.s | 21 +++++----- src/kernel/main.c | 7 ++-- 5 files changed, 80 insertions(+), 57 deletions(-) diff --git a/src/boot/mbr.s b/src/boot/mbr.s index de9ebab..5cce3cc 100644 --- a/src/boot/mbr.s +++ b/src/boot/mbr.s @@ -17,10 +17,10 @@ ; 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 +[bits 16] ; All x86 CPUs start in 16 bit (aka "real") mode, so we tell nasm to emit 16-bit code -; We save the value of the current boot drive -mov [boot_drive], dl +kernel_offset: equ 0x1000 +reserved_sectors: equ 4 ; This isn't needed inside the qemu emulator, but ; real hardware is unlikely to start up with the @@ -31,6 +31,9 @@ mov ss, ax mov es, ax xor ax, ax +; We save the value of the current boot drive +mov [boot_drive], dl + ; Now we setup the stack by setting the ; base pointer to a location that's far ; enough from where the code for the BIOS @@ -38,54 +41,78 @@ xor ax, ax mov sp, 0x9000 mov bp, sp call bios_cls -mov si, real_mode_msg +mov si, loading_stage2_msg call bios_println -call load_kernel -call switch_to_protected_mode -jmp $ ; Keeps jumping at the current address (loops forever) +mov bx, stage2 +mov cl, 2 +mov dh, reserved_sectors +mov dl, [boot_drive] +call load_disk +jmp stage2 -; Now we include our "function definitions" (after the -; loop, so they're never executed unless explicitly called) +; Variables needed in the boot +; sector +boot_drive: db 0 +loading_stage2_msg: db "TSBL - INFO: Stage 1 loaded, loading stage 2", 0 + +; We include just the bare minimum needed to load the +; second stage of our bootloader %include "src/boot/util/disk.s" %include "src/boot/util/io.s" -%include "src/boot/gdt.s" -%include "src/boot/switch32.s" +; Padding and magic number +times 510 - ($ - $$) db 0 +dw 0xaa55 -; 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 "TSOS: Booting", 0 -protected_mode_msg: db "TSOS: Protected mode OK", 0 -loading_kernel_msg: db "TSOS: Loading kernel", 0 -boot_drive: db 0 - - -[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 +[bits 16] +stage2: + ; Here we're no longer limited by the size of the + ; boot sector, so we can perform the more complex + ; part of the boot process + mov si, stage2_loaded_msg call bios_println + call load_kernel + call switch_to_protected_mode + + +load_kernel: + ; Loads the kernel into memory mov bx, kernel_offset - mov cl, 2 + mov cl, reserved_sectors + 1 mov dh, 4 mov dl, [boot_drive] call load_disk + mov si, kernel_loaded_msg + call bios_println ret [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 have already written - mov ecx, 0x140 + call vga_print + call enableA20 + mov esi, calling_kernel_msg call vga_print call kernel_offset + cli + hlt jmp $ -; Padding and magic number -times 510 - ($-$$) db 0 -dw 0xaa55 + +; Now we include the necessary files to switch to +; 32-bit (aka protected) mode +%include "src/boot/gdt.s" +%include "src/boot/switch32.s" +%include "src/boot/util/enablea20.s" + + +; Here we define our variables used in the second stage +protected_mode_msg: db "TSBL - INFO: Successfully switched to protected mode, enabling A20 line", 0 +kernel_loaded_msg: db "TSBL - INFO: Kernel loaded, switching to protected mode", 0 +calling_kernel_msg: db "TSBL - INFO: Calling kernel entrypoint", 0 +stage2_loaded_msg: db "TSBL - INFO: Stage 2 loaded, loading kernel", 0 + + +times (512 * reserved_sectors) - ($ - $$) db 0 ; Pads the section to exactly reserved_sectors \ No newline at end of file diff --git a/src/boot/switch32.s b/src/boot/switch32.s index 67d8716..3ecf020 100644 --- a/src/boot/switch32.s +++ b/src/boot/switch32.s @@ -17,13 +17,7 @@ [bits 16] switch_to_protected_mode: cli ; Now we disable interrupts - ; We enable the A20 line using - ; the ol' but (mostly) reliable - ; keyboard controller method - mov al, 0xd1 - out 0x64, al - mov al, 0xdf - out 0x60, al + lgdt [gdt_descriptor] ; Time to load the GDT descriptor ; We set 32-bit mode in CR0. Almost done! mov eax, cr0 @@ -45,5 +39,4 @@ switch32: ; We're not in 32 bit mode, yay! mov ebp, 0x1f8400 ; We also make the stack larger (2MiB) mov esp, ebp - - call BEGIN_32BIT ; We call back into mbr.s which loads the kernel \ No newline at end of file + call BEGIN_32BIT ; We call back into mbr.s which loads the kernel diff --git a/src/boot/util/disk.s b/src/boot/util/disk.s index 79eb6d3..6bed450 100644 --- a/src/boot/util/disk.s +++ b/src/boot/util/disk.s @@ -42,6 +42,8 @@ disk_error: call bios_print mov dh, ah ; Error code is in ah call bios_printh + mov si, parenthesis + call bios_print call bios_newline jmp disk_loop @@ -55,5 +57,6 @@ disk_loop: jmp $ -disk_read_error_msg: db "TSOS: Read error: ", 0 -disk_sectors_error_msg: db "TSOS: Incomplete read", 0 +parenthesis: db ')', 0 +disk_read_error_msg: db "TSOS - ERROR: Disk read failed (error code ", 0 +disk_sectors_error_msg: db "TSOS - ERROR: Disk read failed (sector number mismatch)", 0 diff --git a/src/boot/util/io.s b/src/boot/util/io.s index 96acdcd..becbf52 100644 --- a/src/boot/util/io.s +++ b/src/boot/util/io.s @@ -124,7 +124,10 @@ HEX_OUT: db '0x0000', 0 ; reserve memory for our new string [bits 32] -; I/O routines that directly manipulate video memory +; I/O routines that work with the VGA controller and video memory +; to print to the screen + + VMEM_START: equ 0xb8000 ; Video memory always starts at this address ; A character on the screen in VGA text mode is composed of 2 bytes: ; the first byte is the ASCII codepoint to be printed, while the next @@ -135,27 +138,23 @@ TEXT_COLOR: equ 0x07 vga_print: ; Prints a null-terminated string located - ; on the esi register. Offsets the write - ; by ecx bytes + ; on the esi register. pusha - mov edx, VMEM_START - add edx, ecx + mov ebx, VMEM_START vga_print_loop: mov al, [esi] - 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 + je vga_print_done + mov ah, TEXT_COLOR + mov [ebx], ax ; Write the 2-byte character to video memory inc esi ; Go to the next character in the string - add edx, 2 ; Go to the next character in video memory + add ebx, 2 jmp vga_print_loop - vga_print_done: popa ret - vga_printh: ; Prints the value of edx in hexadecimal format pusha diff --git a/src/kernel/main.c b/src/kernel/main.c index 7cfbfeb..ff84861 100644 --- a/src/kernel/main.c +++ b/src/kernel/main.c @@ -24,9 +24,10 @@ i32 kmain(void) { The kernel entry point of TSOS */ - // We skip the log messages from - // the bootloader - kprintln("\n\n\n\nTSOS: Kernel load OK"); + + // TODO: Set VGA cursor position from assembly. Skipping + // log messages like this is just awful + kprintln("\n\n\nTSKL - INFO: Kernel booted successfully"); // TODO... return 0x022172; // :D } \ No newline at end of file