Bootloader: Load kernel to memory and jump to it!

This commit is contained in:
Bananymous 2023-11-17 16:36:29 +02:00
parent 03b80ed113
commit 07ae1bbf34
4 changed files with 265 additions and 5 deletions

View File

@ -6,6 +6,7 @@ set(BOOTLOADER_SOURCES
boot.S boot.S
command_line.S command_line.S
disk.S disk.S
elf.S
ext2.S ext2.S
memory_map.S memory_map.S
utils.S utils.S

View File

@ -78,10 +78,35 @@ stage2_main:
jz print_and_halt jz print_and_halt
call ext2_find_kernel 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: enter_unreal_mode:
cli cli
pushw %ds pushw %ds
@ -120,6 +145,7 @@ gdt:
.quad 0x0000000000000000 .quad 0x0000000000000000
.quad 0x00009A000000FFFF .quad 0x00009A000000FFFF
.quad 0x00CF92000000FFFF .quad 0x00CF92000000FFFF
.quad 0x00CF9A000000FFFF
gdtr: gdtr:
.short . - gdt - 1 .short . - gdt - 1
.quad gdt .quad gdt

221
bootloader/elf.S Normal file
View File

@ -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

View File

@ -408,8 +408,14 @@ ext2_inode_read_bytes:
movl $ext2_block_buffer, %esi movl $ext2_block_buffer, %esi
addl %edx, %esi addl %edx, %esi
# copy partial block to destination buffer # very dumb memcpy with 32 bit addresses
rep movsb # not sure if this uses (si or esi) and (di or edi) .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 # check if all sectors are read
cmpl $0, 4(%esp) cmpl $0, 4(%esp)
@ -433,9 +439,15 @@ ext2_inode_read_bytes:
addl %ecx, 0(%esp) addl %ecx, 0(%esp)
subl %ecx, 4(%esp) subl %ecx, 4(%esp)
# copy bytes from block into destination # very dumb memcpy with 32 bit addresses
movl $ext2_block_buffer, %esi 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 # read next block if more sectors remaining
cmpl $0, 4(%esp) cmpl $0, 4(%esp)