From a3a287f5ca998e563a57108a176fc18667b12c76 Mon Sep 17 00:00:00 2001 From: Bananymous Date: Sat, 11 Nov 2023 22:49:00 +0200 Subject: [PATCH] Bootloader: Continue work on bootloader Bootloader can now get the memory map and read cmdline from user. Now 'just' video mode query, ext2 and ELF parsing are needed :D --- bootloader/arch/x86_64/boot.S | 552 ++++++++++++++++++++++++++----- bootloader/arch/x86_64/linker.ld | 4 +- bootloader/build-and-run.sh | 2 +- 3 files changed, 469 insertions(+), 89 deletions(-) diff --git a/bootloader/arch/x86_64/boot.S b/bootloader/arch/x86_64/boot.S index 9ff1b220..86763eb2 100644 --- a/bootloader/arch/x86_64/boot.S +++ b/bootloader/arch/x86_64/boot.S @@ -1,10 +1,9 @@ # FIXME: don't assume 512 byte sectors -.set SECTOR_SIZE, 512 - -.set GPT_HEADER_ADDR, free_memory_start -.set GPT_ENTRY_ADDR, free_memory_start + SECTOR_SIZE - +.set SECTOR_SIZE_SHIFT, 9 +.set SECTOR_SIZE, 1 << SECTOR_SIZE_SHIFT +.set SCREEN_WIDTH, 80 +.set SCREEN_HEIGHT, 25 .code16 @@ -20,42 +19,49 @@ .section .stage1 stage1_start: - jmp main + jmp stage1_main -# al: character to print +# prints character to screen +# al: ascii character to print putc: - mov $0x0E, %ah + pusha + movb $0x0E, %ah + movb $0x00, %bh int $0x10 + popa ret -# ds:si: null terminated string to print +# prints null terminated string to screen +# ds:si: string address puts: - push %si - push %ax + pushw %si + pushw %bx + pushw %ax -1: + movb $0x0E, %ah + movb $0x00, %bh + +.puts_loop: lodsb test %al, %al - jz 2f + jz .puts_done - call putc - jmp 1b + int $0x10 + jmp .puts_loop -2: - mov $'\r', %al - call putc - mov $'\n', %al - call putc - - pop %ax - pop %si +.puts_done: + popw %ax + popw %bx + popw %si ret +# compares memory between addresses # si: ptr1 # di: ptr2 -# cx: count -# return: 1 if equal, 0 otherwise +# cx: bytes count +# return: +# al: 1 if equal, 0 otherwise memcmp: pushw %si pushw %di @@ -70,12 +76,10 @@ memcmp: # read sectors from disk -# # bx:eax: lba start # cx: lba count (has to less than 0x80) # dl: drive number # ds:di: physical address -# # returns only on success read_from_disk: push %ax @@ -103,7 +107,78 @@ read_from_disk: ret -main: +# Validates that GPT header is valid and usable +# doesn't return on invalid header +validate_gpt_header: + # confirm header (starts with 'EFI PART') + cmpl $0x20494645, (gpt_header + 0) + jne .not_gpt_partition + cmpl $0x54524150, (gpt_header + 4) + jne .not_gpt_partition + ret + + +# Find partition entry with type guid +# si: address of type guid +# dl: drive number +# returns: +# al: non-zero if found +# di: address of partition entry +find_gpt_partition_entry: + # eax := entry_count + movl (gpt_header + 80), %eax + test %eax, %eax + jz .no_gpt_partition_found + + # edx:eax := eax * entry_size + pushw %dx + mull (gpt_header + 84) + test %edx, %edx + jnz .too_gpt_big_entries + popw %dx + + # FIXME: read one entry array section at a time + + # sector count := (arr_size + SECTOR_SIZE - 1) / SECTOR_SIZE + movl %eax, %ecx + shrl $SECTOR_SIZE_SHIFT, %ecx + + # start lba + movl (gpt_header + 72), %eax + movw (gpt_header + 76), %bx + + movw $gpt_entry_data, %di + + call read_from_disk + + # NOTE: 'only' 0xFFFF partitions supported, + # although read will fail with more than 0x80 + movw (gpt_header + 80), %cx + +.loop_gpt_entries: + pushw %cx + movw $16, %cx + call memcmp + popw %cx + + testb %al, %al + jnz .gpt_partition_found + + # add entry size to entry pointer + addw (gpt_header + 84), %di + + loop .loop_gpt_entries + +.no_gpt_partition_found: + movb $0, %al + ret + +.gpt_partition_found: + movb $1, %al + ret + + +stage1_main: # setup segments movw $0, %ax movw %ax, %ds @@ -129,61 +204,19 @@ main: movw $0, %bx movw $1, %cx movb (boot_disk_number), %dl - movw $GPT_HEADER_ADDR, %di + movw $gpt_header, %di call read_from_disk - # confirm header (starts with 'EFI PART') - cmpl $0x20494645, (GPT_HEADER_ADDR + 0) - jne .not_gpt_partition - cmpl $0x54524150, (GPT_HEADER_ADDR + 4) - jne .not_gpt_partition + call validate_gpt_header - # eax := entry_count - movl (GPT_HEADER_ADDR + 80), %eax - test %eax, %eax + movw $bios_boot_guid, %si + movb boot_disk_number, %dl + call find_gpt_partition_entry + testb %al, %al jz .no_bios_boot_partition - # edx:eax := eax * entry_size - mull (GPT_HEADER_ADDR + 84) - test %edx, %edx - jnz .too_gpt_big_entries + # BIOS boot partiton found! - # sector count := (arr_size + SECTOR_SIZE - 1) / SECTOR_SIZE - pushl %eax - addl $(SECTOR_SIZE - 1), %eax - movl $SECTOR_SIZE, %ecx - divl %ecx - movl %eax, %ecx - popl %eax - - # start lba - movl (GPT_HEADER_ADDR + 72), %eax - movw (GPT_HEADER_ADDR + 76), %bx - - movb (boot_disk_number), %dl - movw $GPT_ENTRY_ADDR, %di - call read_from_disk - - # NOTE: 'only' 0xFFFF partitions supported - movw (GPT_HEADER_ADDR + 80), %cx - -.loop_entries: - push %cx - movw $16, %cx - movw $bios_boot_guid, %si - call memcmp - pop %cx - - testb %al, %al - jnz .bios_boot_found - - # add entry size to entry pointer - addw (GPT_HEADER_ADDR + 84), %di - - loop .loop_entries - jmp .no_bios_boot_partition - -.bios_boot_found: # first lba movl 32(%di), %eax movw $0, %bx @@ -200,7 +233,7 @@ main: movw $stage2_start, %di call read_from_disk - jmp stage2_start + jmp stage2_main print_and_halt: call puts @@ -229,7 +262,6 @@ halt: jmp print_and_halt - # 21686148-6449-6E6F-744E-656564454649 bios_boot_guid: .long 0x21686148 # little endian @@ -245,7 +277,7 @@ read_failed_msg: .asciz "read error" not_gpt_partition_msg: - .asciz "not gpt" + .asciz "not GPT" no_bios_boot_partition_msg: .asciz "no bios boot partition" @@ -256,10 +288,6 @@ too_gpt_big_entries_msg: boot_disk_number: .skip 1 -disk_address_packet: - .skip 16 - - ######################################### # @@ -268,7 +296,247 @@ disk_address_packet: ######################################### .section .stage2 + stage2_start: + +# read a character from keyboard +# return: +# al: ascii +# ah: bios scan code +getc: + movb $0x00, %ah + int $0x16 + ret + + +# prints newline to screen +print_newline: + pushw %ax + movb $'\r', %al + call putc + movb $'\n', %al + call putc + pop %ax + ret + + +# prints backspace to screen, can go back a line +print_backspace: + pusha + + # get cursor position + movb $0x03, %ah + movb $0x00, %bh + int $0x10 + + # don't do anyting if on first row + testb %dh, %dh + jz .print_backspace_done + + # go one line up if on first column + test %dl, %dl + jz .print_backspace_go_line_up + + # otherwise decrease column + decb %dl + jmp .print_backspace_do_print + +.print_backspace_go_line_up: + # decrease row and set column to the last one + decb %dh + movb $(SCREEN_WIDTH - 1), %dl + +.print_backspace_do_print: + # set cursor position + movb $0x02, %ah + int $0x10 + + # print 'empty' character (space) + mov $' ', %al + call putc + + # set cursor position + movb $0x02, %ah + int $0x10 + +.print_backspace_done: + popa + ret + + +# print number to screen +# ax: number to print +# bx: number base +# cx: min width (zero pads if shorter) +printnum: + pusha + pushw %cx + + movw $printnum_buffer, %si + xorw %cx, %cx + +.printnum_fill_loop: + # fill buffer with all remainders ax % bx + xorw %dx, %dx + divw %bx + movb %dl, (%si) + incw %si + incw %cx + testw %ax, %ax + jnz .printnum_fill_loop + + # check if zero pad is required + popw %dx + cmpw %cx, %dx + jbe .printnum_print_loop + + xchgw %dx, %cx + subw %dx, %cx + movb $'0', %al +.printnum_pad_zeroes: + call putc + loop .printnum_pad_zeroes + + movw %dx, %cx + +.printnum_print_loop: + decw %si + movb (%si), %al + cmpb $10, %al + jae 1f + addb $'0', %al + jmp 2f +1: addb $('a' - 10), %al +2: call putc + loop .printnum_print_loop + + popa + ret + + +# test if character is printable ascii +# al: character to test +# sets ZF if is printable +isprint: + cmpb $0x20, %al + jb .isprint_done + cmpb $0x7E, %al + ja .isprint_done + cmpb %al, %al +.isprint_done: + ret + + +# fills memory map data structure +# doesn't return on error +get_memory_map: + pusha + + movl $0, (memory_map_entry_count) + + movl $0x0000E820, %eax + movl $0x534D4150, %edx + xorl %ebx, %ebx + movl $20, %ecx + movw $memory_map_entries, %di + + clc + int $0x15 + # If first call returs with CF set, the call failed + jc .get_memory_map_error + +.get_memory_map_rest: + cmpl $0x534D4150, %eax + jne .get_memory_map_error + + # FIXME: don't assume BIOS to always return 20 bytes + cmpl $20, %ecx + jne .get_memory_map_error + + # increment entry count + incl (memory_map_entry_count) + + # increment entry pointer + addw %cx, %di + + # BIOS can indicate end of list by 0 in ebx + testl %ebx, %ebx + jz .get_memory_map_done + + movl $0x0000E820, %eax + movl $0x534D4150, %edx + + clc + int $0x15 + # BIOS can indicate end of list by setting CF + jnc .get_memory_map_rest + +.get_memory_map_done: + popa + ret + +.get_memory_map_error: + movw $memory_map_error_msg, %si + jmp print_and_halt + + +# fills command line buffer +get_command_line: + pusha + + movw $command_line_enter_msg, %si + call puts + + movw $command_line_buffer, %di + +.get_command_line_loop: + call getc + + cmpb $'\b', %al + je .get_command_line_backspace + + # Not sure if some BIOSes return '\n' as enter, but check it just in case + cmpb $'\r', %al + je .get_command_line_done + cmpb $'\n', %al + je .get_command_line_done + + call isprint + jnz .get_command_line_loop + + # put byte to buffer + movb %al, (%di) + incw %di + + # print byte + call putc + + jmp .get_command_line_loop + +.get_command_line_backspace: + # don't do anything if at the beginning + cmpw $command_line_buffer, %di + je .get_command_line_loop + + # decrement buffer pointer + decw %di + + # erase byte in display + call print_backspace + + jmp .get_command_line_loop + +.get_command_line_done: + # null terminate command line + movb $0, (%di) + + call print_newline + + popa + ret + + +stage2_main: # clear screen and enter 80x25 text mode movb $0x03, %al movb $0x00, %ah @@ -277,11 +545,123 @@ stage2_start: # print hello message movw $hello_msg, %si call puts + call print_newline + + call get_memory_map + call get_command_line + + call print_newline + + movw $start_kernel_load_msg, %si + call puts + call print_newline + + movw $command_line_msg, %si + call puts + movw $command_line_buffer, %si + call puts + call print_newline + + movw $memory_map_msg, %si + call puts + call print_newline + + movl (memory_map_entry_count), %edx + movw $memory_map_entries, %si + + movw $16, %bx + movw $4, %cx + +.loop_memory_map: + movb $' ', %al + call putc + call putc + call putc + call putc + + movw 0x06(%si), %ax + call printnum + movw 0x04(%si), %ax + call printnum + movw 0x02(%si), %ax + call printnum + movw 0x00(%si), %ax + call printnum + + movb $',', %al + call putc + movb $' ', %al + call putc + + movw 0x0E(%si), %ax + call printnum + movw 0x0C(%si), %ax + call printnum + movw 0x0A(%si), %ax + call printnum + movw 0x08(%si), %ax + call printnum + + movb $',', %al + call putc + movb $' ', %al + call putc + + movw 0x12(%si), %ax + call printnum + movw 0x10(%si), %ax + call printnum + + call print_newline + + addw $20, %si + + decl %edx + jnz .loop_memory_map + + + jmp halt -1: - jmp 1b hello_msg: .asciz "This is banan-os bootloader" +command_line_msg: +command_line_enter_msg: + .asciz "cmdline: " + +memory_map_msg: + .asciz "memmap:" + +memory_map_error_msg: + .asciz "Failed to get memory map" + +start_kernel_load_msg: + .asciz "Starting to load kernel" + stage2_end: + + +.section .bss + +.align SECTOR_SIZE +gpt_header: + .skip SECTOR_SIZE +gpt_entry_data: + .skip SECTOR_SIZE + +disk_address_packet: + .skip 16 + +printnum_buffer: + .skip 10 + +memory_map_entry_count: + .skip 4 +# 100 entries should be enough... +memory_map_entries: + .skip 20 * 100 + +# 100 character command line +command_line_buffer: + .skip 100 diff --git a/bootloader/arch/x86_64/linker.ld b/bootloader/arch/x86_64/linker.ld index b23bde4c..9351edd2 100644 --- a/bootloader/arch/x86_64/linker.ld +++ b/bootloader/arch/x86_64/linker.ld @@ -1,11 +1,11 @@ SECTIONS { . = 0x7C00; - .stage1 : { *(.stage1*) } + .stage1 : { *(.stage1) } . = ALIGN(512); .stage2 : { *(.stage2) } . = ALIGN(512); - free_memory_start = .; + .bss : { *(.bss) } } \ No newline at end of file diff --git a/bootloader/build-and-run.sh b/bootloader/build-and-run.sh index 0f2cce84..f21857aa 100755 --- a/bootloader/build-and-run.sh +++ b/bootloader/build-and-run.sh @@ -42,4 +42,4 @@ if [ "$1" == "debug" ] ; then fi echo running qemu -qemu-system-x86_64 $QEMU_FLAGS --drive format=raw,file=test.img +env BANAN_DISK_IMAGE_PATH=${DISK_IMAGE_PATH} BANAN_ARCH=x86_64 ../script/qemu.sh $QEMU_FLAGS