From 641a2dec00e1b282d6d6e392ba86166b9014d5d0 Mon Sep 17 00:00:00 2001 From: Bananymous Date: Fri, 17 Nov 2023 16:36:29 +0200 Subject: [PATCH] Bootloader: Load kernel to memory and jump to it! --- bootloader/CMakeLists.txt | 1 + bootloader/boot.S | 28 ++++- bootloader/elf.S | 221 ++++++++++++++++++++++++++++++++++++++ bootloader/ext2.S | 20 +++- 4 files changed, 265 insertions(+), 5 deletions(-) create mode 100644 bootloader/elf.S diff --git a/bootloader/CMakeLists.txt b/bootloader/CMakeLists.txt index f2fbbaca..fb83c945 100644 --- a/bootloader/CMakeLists.txt +++ b/bootloader/CMakeLists.txt @@ -6,6 +6,7 @@ set(BOOTLOADER_SOURCES boot.S command_line.S disk.S + elf.S ext2.S memory_map.S utils.S diff --git a/bootloader/boot.S b/bootloader/boot.S index 524687d4..4e12bbde 100644 --- a/bootloader/boot.S +++ b/bootloader/boot.S @@ -78,10 +78,35 @@ stage2_main: jz print_and_halt call ext2_find_kernel + movl $ext2_inode_read_bytes, %esi - jmp halt + call elf_read_kernel_to_memory + + cli + # setup protected mode + movl %cr0, %ebx + orb $1, %bl + movl %ebx, %cr0 + + + # jump to kernel in protected mode + ljmpl $0x18, $protected_mode + + +.code32 +protected_mode: + movw $0x10, %bx + movw %bx, %ds + movw %bx, %es + movw %bx, %fs + movw %bx, %gs + movw %bx, %ss + jmp *%eax + + +.code16 enter_unreal_mode: cli pushw %ds @@ -120,6 +145,7 @@ gdt: .quad 0x0000000000000000 .quad 0x00009A000000FFFF .quad 0x00CF92000000FFFF + .quad 0x00CF9A000000FFFF gdtr: .short . - gdt - 1 .quad gdt diff --git a/bootloader/elf.S b/bootloader/elf.S new file mode 100644 index 00000000..059c4581 --- /dev/null +++ b/bootloader/elf.S @@ -0,0 +1,221 @@ +.set SECTOR_SIZE, 512 + +# file header field offsets +.set e_type, 16 +.set e_machine, 18 +.set e_version, 20 +.set e_entry, 24 +.set e_phoff, 32 +.set e_shoff, 40 +.set e_flags, 48 +.set e_ehsize, 52 +.set e_phentsize, 54 +.set e_phnum, 56 +.set e_shentsize, 58 +.set e_shnum, 60 +.set e_shstrndx, 62 + +# e_ident offsets +.set EI_CLASS, 4 +.set EI_DATA, 5 +.set EI_VERSION, 6 + +# e_ident constants +.set ELFMAGIC, 0x464C457F +.set ELFCLASS64, 2 +.set ELFDATA2LSB, 1 +.set EV_CURRENT, 1 + +# e_type constants +.set ET_EXEC, 2 + +# program header field offsets +.set p_type, 0 +.set p_flags, 4 +.set p_offset, 8 +.set p_vaddr, 16 +.set p_paddr, 24 +.set p_filesz, 32 +.set p_memsz, 40 +.set p_align, 48 + +# p_type constants +.set PT_NULL, 0 +.set PT_LOAD, 1 + +.code16 +.section .stage2 + +# Validate file header stored in elf_file_header +# returns only on success +elf_validate_file_header: + cmpl $ELFMAGIC, (elf_file_header) + jne .elf_validate_file_header_invalid_magic + + cmpb $ELFCLASS64, (elf_file_header + EI_CLASS) + jne .elf_validate_file_header_only_64bit_supported + + cmpb $ELFDATA2LSB, (elf_file_header + EI_DATA) + jne .elf_validate_file_header_only_little_endian_supported + + cmpb $EV_CURRENT, (elf_file_header + EI_VERSION) + jne .elf_validate_file_header_not_current_version + + cmpl $EV_CURRENT, (elf_file_header + e_version) + jne .elf_validate_file_header_not_current_version + + cmpw $ET_EXEC, (elf_file_header + e_type) + jne .elf_validate_file_header_not_executable + + ret + + .elf_validate_file_header_invalid_magic: + movw $elf_validate_file_header_invalid_magic_msg, %si + jmp print_and_halt + .elf_validate_file_header_only_64bit_supported: + movw $elf_validate_file_header_only_64bit_supported_msg, %si + jmp print_and_halt + .elf_validate_file_header_only_little_endian_supported: + movw $elf_validate_file_header_only_little_endian_supported_msg, %si + jmp print_and_halt + .elf_validate_file_header_not_current_version: + movw $elf_validate_file_header_not_current_version_msg, %si + jmp print_and_halt + .elf_validate_file_header_not_executable: + movw $elf_validate_file_header_not_executable_msg, %si + jmp print_and_halt + + +# read callback format +# eax: first byte +# ecx: byte count +# edi: buffer +# returns only on success + + +# reads kernel to memory +# esi: callback for reading from kernel image +# return: +# eax: kernel entry address +.global elf_read_kernel_to_memory +elf_read_kernel_to_memory: + pushal + pushl %ebp + movl %esp, %ebp + subl $2, %esp + + # read file header + movl $0, %eax + movl $64, %ecx + movl $elf_file_header, %edi + call *%esi + + call elf_validate_file_header + + cmpl $0, (elf_file_header + e_phoff + 4) + jnz .elf_read_kernel_to_memory_unsupported_offset + + # current program header + movw $0, -2(%ebp) + + .elf_read_kernel_to_memory_loop_program_headers: + movw -2(%ebp), %cx + cmpw (elf_file_header + e_phnum), %cx + jae .elf_read_kernel_to_memory_done + + # eax := program_header_index * e_phentsize + e_phoff + xorl %eax, %eax + movw %cx, %ax + xorl %ebx, %ebx + movw (elf_file_header + e_phentsize), %bx + mull %ebx + addl (elf_file_header + e_phoff), %eax + jc .elf_read_kernel_to_memory_unsupported_offset + + # setup program header size and address + movl $56, %ecx + movl $elf_program_header, %edi + + # read the program header + call *%esi + + # test if program header is empty + cmpl $PT_NULL, (elf_program_header + p_type) + je .elf_read_kernel_to_memory_null_program_header + + # confirm that the program header is loadable + cmpl $PT_LOAD, (elf_program_header + p_type) + jne .elf_read_kernel_to_memory_not_loadable_header + + # memset p_filesz -> p_memsz to 0 + movl (elf_program_header + p_filesz), %ebx + + movl (elf_program_header + p_vaddr), %edi + andl $0x7FFFFFFF, %edi + addl %ebx, %edi + + movl (elf_program_header + p_memsz), %ecx + subl %ebx, %ecx + jz .elf_read_kernel_to_memory_no_memset + + .elf_read_kernel_to_memory_memset: + movb $0, (%edi) + decl %ecx + jnz .elf_read_kernel_to_memory_memset + .elf_read_kernel_to_memory_no_memset: + + # read file specified in program header to memory + movl (elf_program_header + p_offset), %eax + movl (elf_program_header + p_vaddr), %edi + andl $0x7FFFFFFF, %edi + movl (elf_program_header + p_filesz), %ecx + + call print_hex32; call print_newline + + call *%esi + + .elf_read_kernel_to_memory_null_program_header: + incw -2(%ebp) + jmp .elf_read_kernel_to_memory_loop_program_headers + + .elf_read_kernel_to_memory_done: + leavel + popal + + # set kernel entry address + movl (elf_file_header + e_entry), %eax + andl $0x7FFFFF, %eax + + ret + + .elf_read_kernel_to_memory_unsupported_offset: + movw $elf_read_kernel_to_memory_unsupported_offset_msg, %si + jmp print_and_halt + .elf_read_kernel_to_memory_not_loadable_header: + movw $elf_read_kernel_to_memory_not_loadable_header_msg, %si + jmp print_and_halt + + +elf_validate_file_header_invalid_magic_msg: + .asciz "ELF: file has invalid ELF magic" +elf_validate_file_header_only_64bit_supported_msg: + .asciz "ELF: file is not targettint 64 bit" +elf_validate_file_header_only_little_endian_supported_msg: + .asciz "ELF: file is not in little endian format" +elf_validate_file_header_not_current_version_msg: + .asciz "ELF: file is not in current ELF version" +elf_validate_file_header_not_executable_msg: + .asciz "ELF: file is not an executable" + +elf_read_kernel_to_memory_unsupported_offset_msg: + .asciz "ELF: unsupported offset (only 32 bit offsets supported)" +elf_read_kernel_to_memory_not_loadable_header_msg: + .asciz "ELF: kernel contains non-loadable program header" + +.section .bss + +elf_file_header: + .skip 64 + +elf_program_header: + .skip 56 diff --git a/bootloader/ext2.S b/bootloader/ext2.S index 365726d4..6937f266 100644 --- a/bootloader/ext2.S +++ b/bootloader/ext2.S @@ -408,8 +408,14 @@ ext2_inode_read_bytes: movl $ext2_block_buffer, %esi addl %edx, %esi - # copy partial block to destination buffer - rep movsb # not sure if this uses (si or esi) and (di or edi) + # very dumb memcpy with 32 bit addresses + .ext2_inode_read_bytes_memcpy_partial: + movb (%esi), %al + movb %al, (%edi) + incl %esi + incl %edi + decl %ecx + jnz .ext2_inode_read_bytes_memcpy_partial # check if all sectors are read cmpl $0, 4(%esp) @@ -433,9 +439,15 @@ ext2_inode_read_bytes: addl %ecx, 0(%esp) subl %ecx, 4(%esp) - # copy bytes from block into destination + # very dumb memcpy with 32 bit addresses movl $ext2_block_buffer, %esi - rep movsb # not sure if this uses (si or esi) and (di or edi) + .ext2_inode_read_bytes_memcpy: + movb (%esi), %al + movb %al, (%edi) + incl %esi + incl %edi + decl %ecx + jnz .ext2_inode_read_bytes_memcpy # read next block if more sectors remaining cmpl $0, 4(%esp)