Compare commits

...

14 Commits

Author SHA1 Message Date
Bananymous a312d75bb2 Bootloader: Implement VESA video mode query and pass it to kernel
Kernel now gets framebuffer from bootloader. Framebuffer dimensions
and bpp are hardcoded in bootloader, but will probably be read from
config file at some point.
2023-11-17 22:45:35 +02:00
Bananymous a554bd0fd8 Bootloader: Fix kernel memset to zero 2023-11-17 21:05:02 +02:00
Bananymous f0d2a211ea Bootloader add temporary initial command line
This will probably be read from some config file at some point
2023-11-17 20:38:38 +02:00
Bananymous 065eec430e Kernel/Bootloader: banan-os can now be booted with my bootloader :D 2023-11-17 20:33:02 +02:00
Bananymous 5f4d81a502 Bootloader: Clear screen, better memcpy
Clear screen before jumping to kernel. Memcpy now uses ebx as offset
register, so only one register has to updated every loop
2023-11-17 20:31:42 +02:00
Bananymous 41065d2f9a Kernel: Don't calculate divisor in a for loop in ext2 inodes 2023-11-17 19:02:01 +02:00
Bananymous 3daf3d53a3 Kernel: Serial now uses random size for some serial ports
If the serial port doesn't repond with a size, just use a random
one. There is no reason to ditch the whole output if you cannot
determine its size.
2023-11-17 18:56:02 +02:00
Bananymous ec56e9c6f1 Kernel: Don't use multiboot2 explicitly. Parse it to common structure
This allows support of multiple different bootloaders
2023-11-17 18:54:59 +02:00
Bananymous 07ae1bbf34 Bootloader: Load kernel to memory and jump to it! 2023-11-17 16:36:29 +02:00
Bananymous 03b80ed113 Bootloader enter unreal mode at the start of stage2 2023-11-17 14:22:21 +02:00
Bananymous 407a7b80c5 Bootloader: Fix getting command line 2023-11-17 13:17:44 +02:00
Bananymous 8b4f169d0f Bootloader: implement reading from inode 2023-11-17 13:17:44 +02:00
Bananymous 6f9b3ab5de Bootloader: add support for indirect inode blocks 2023-11-16 13:34:21 +02:00
Bananymous aa7a8124ce Bootloader: Add helpers for printing n bit hexadecimal numbers 2023-11-16 13:30:01 +02:00
21 changed files with 1056 additions and 121 deletions

View File

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

View File

@ -53,9 +53,15 @@ stage2_main:
movw $hello_msg, %si
call puts; call print_newline
call enter_unreal_mode
movw $unreal_enter_msg, %si
call puts; call print_newline
call get_memory_map
call read_user_command_line
call vesa_find_video_mode
call print_newline
movw $start_kernel_load_msg, %si
@ -74,11 +80,91 @@ stage2_main:
jz print_and_halt
call ext2_find_kernel
movl $ext2_inode_read_bytes, %esi
jmp halt
call elf_read_kernel_to_memory
call vesa_set_target_mode
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
movl %eax, %ecx
movl $0xD3C60CFF, %eax
movl $banan_boot_info, %ebx
xorl %edx, %edx
xorl %esi, %esi
xorl %edi, %edi
jmp *%ecx
.code16
enter_unreal_mode:
cli
pushw %ds
lgdt gdtr
movl %cr0, %eax
orb $1, %al
movl %eax, %cr0
ljmpl $0x8, $.enter_unreal_mode_pmode
.enter_unreal_mode_pmode:
movw $0x10, %bx
movw %bx, %ds
andb 0xFE, %al
movl %eax, %cr0
ljmpl $0x0, $.enter_unreal_mode_unreal
.enter_unreal_mode_unreal:
popw %ds
sti
ret
hello_msg:
.asciz "This is banan-os bootloader"
unreal_enter_msg:
.asciz "Entered unreal mode"
start_kernel_load_msg:
.asciz "Starting to load kernel"
gdt:
.quad 0x0000000000000000
.quad 0x00009A000000FFFF
.quad 0x00CF92000000FFFF
.quad 0x00CF9A000000FFFF
gdtr:
.short . - gdt - 1
.quad gdt
banan_boot_info:
boot_command_line:
.long command_line
boot_framebuffer:
.long framebuffer
boot_memory_map:
.long memory_map

View File

@ -6,10 +6,20 @@
# NO REGISTERS SAVED
.global read_user_command_line
read_user_command_line:
# print initial command line
movw $command_line_enter_msg, %si
call puts
movw $command_line_buffer, %si
call puts
# prepare registers for input
movw $command_line_enter_msg, %si
movw $command_line_buffer, %di
.read_user_command_line_goto_end:
cmpb $0, (%di)
jz .read_user_command_line_loop
incw %di
jmp .read_user_command_line_goto_end
.read_user_command_line_loop:
call getc
@ -23,9 +33,13 @@ read_user_command_line:
cmpb $'\n', %al
je .read_user_command_line_done
pushw %ax
call isprint
testb %al, %al
jnz .read_user_command_line_loop
jz .read_user_command_line_loop
popw %ax
# put byte to buffer
movb %al, (%di)
@ -61,9 +75,9 @@ read_user_command_line:
command_line_enter_msg:
.asciz "cmdline: "
.section .bss
.global command_line
command_line:
# 100 character command line
command_line_buffer:
.skip 100
.ascii "root=/dev/sda2"
.skip 100 - 28

222
bootloader/elf.S Normal file
View File

@ -0,0 +1,222 @@
.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_memset_done
.elf_read_kernel_to_memory_memset:
movb $0, (%edi)
incl %edi
decl %ecx
jnz .elf_read_kernel_to_memory_memset
.elf_read_kernel_to_memory_memset_done:
# 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

@ -232,8 +232,8 @@ ext2_read_inode:
addl $ext2_block_buffer, %esi
# edi := ext2_inode_buffer
movl $ext2_inode_buffer, %edi
# cx := inode_size
movw (ext2_inode_size), %cx
# ecx := inode_size
movl (ext2_inode_size), %ecx
rep movsb
popal
@ -245,7 +245,10 @@ ext2_read_inode:
# return:
# eax: block index
ext2_data_block_index:
pushl %ebx
pushl %ecx
pushl %edx
pushl %esi
# calculate max data blocks
movl (ext2_inode_buffer + i_size), %ecx
@ -260,13 +263,87 @@ ext2_data_block_index:
# check if this is direct block access
cmpl $12, %eax
jb .ext2_data_block_index_direct
subl $12, %eax
jmp .ext2_data_block_index_unsupported
# check if this is singly indirect block access
cmpl $(EXT2_BLOCK_SIZE / 4), %eax
jb .ext2_data_block_index_singly_indirect
subl $(EXT2_BLOCK_SIZE / 4), %eax
# check if this is doubly indirect block access
cmpl $((EXT2_BLOCK_SIZE / 4) * (EXT2_BLOCK_SIZE / 4)), %eax
jb .ext2_data_block_index_doubly_indirect
subl $((EXT2_BLOCK_SIZE / 4) * (EXT2_BLOCK_SIZE / 4)), %eax
# check if this is triply indirect block access
cmpl $((EXT2_BLOCK_SIZE / 4) * (EXT2_BLOCK_SIZE / 4) * (EXT2_BLOCK_SIZE / 4)), %eax
jb .ext2_data_block_index_triply_indirect
# otherwise this is invalid access
jmp .ext2_data_block_index_invalid
.ext2_data_block_index_direct:
movl $(ext2_inode_buffer + i_block), %ecx
addl %eax, %ecx
movl (%ecx), %eax
movl $(ext2_inode_buffer + i_block), %esi
movl (%esi, %eax, 4), %eax
jmp .ext2_data_block_index_done
.ext2_data_block_index_singly_indirect:
movl %eax, %ebx
movl (ext2_inode_buffer + i_block + 12 * 4), %eax
movw $1, %cx
jmp .ext2_data_block_index_indirect
.ext2_data_block_index_doubly_indirect:
movl %eax, %ebx
movl (ext2_inode_buffer + i_block + 13 * 4), %eax
movw $2, %cx
jmp .ext2_data_block_index_indirect
.ext2_data_block_index_triply_indirect:
movl %eax, %ebx
movl (ext2_inode_buffer + i_block + 14 * 4), %eax
movw $3, %cx
jmp .ext2_data_block_index_indirect
# eax := current block
# ebx := index
# cx := depth
.ext2_data_block_index_indirect:
call ext2_read_block
# store depth and index
pushw %cx
pushl %ebx
cmpw $1, %cx
jbe .ext2_data_block_index_no_shift
# cl := shift
movb $(EXT2_BLOCK_SHIFT - 2), %al
decb %cl
mulb %cl
movb %al, %cl
# ebx := ebx >> cl
shrl %cl, %ebx
.ext2_data_block_index_no_shift:
# edx := index of next block
movl %ebx, %eax
xorl %edx, %edx
movl $(EXT2_BLOCK_SIZE / 4), %ebx
divl %ebx
# eax := next block
movl $ext2_block_buffer, %esi
movl (%esi, %edx, 4), %eax
# restore depth and index
popl %ebx
popw %cx
loop .ext2_data_block_index_indirect
jmp .ext2_data_block_index_done
.ext2_data_block_index_out_of_bounds:
@ -275,14 +352,112 @@ ext2_data_block_index:
movl $0, %eax
jmp .ext2_data_block_index_done
.ext2_data_block_index_unsupported:
movw $ext2_data_block_index_unsupported_msg, %si
.ext2_data_block_index_invalid:
movw $ext2_data_block_index_invalid_msg, %si
call puts; call print_newline
movl $0, %eax
jmp .ext2_data_block_index_done
.ext2_data_block_index_done:
popl %esi
popl %edx
popl %ecx
popl %ebx
ret
# read bytes from inode (implements read callback)
# eax: first byte
# ecx: byte count
# edi: buffer
# returns only on success
.global ext2_inode_read_bytes
ext2_inode_read_bytes:
pushal
pushl %ebp
movl %esp, %ebp
subl $8, %esp
# save read info
movl %eax, 0(%esp)
movl %ecx, 4(%esp)
# check if eax % EXT2_BLOCK_SIZE != 0,
# then we need to read a partial block starting from an offset
xorl %edx, %edx
movl $EXT2_BLOCK_SIZE, %ebx
divl %ebx
testl %edx, %edx
jz .ext2_inode_read_bytes_no_partial_start
# get data block index and read block
call ext2_data_block_index
call ext2_read_block
# ecx := byte count (min(block_size - edx, remaining_bytes))
movl $EXT2_BLOCK_SIZE, %ecx
subl %edx, %ecx
cmpl %ecx, 4(%esp)
cmovbl 4(%esp), %ecx
# update remaining read info
addl %ecx, 0(%esp)
subl %ecx, 4(%esp)
# esi := start sector data (block_buffer + index * SECTOR_SIZE)
movl $ext2_block_buffer, %esi
addl %edx, %esi
# very dumb memcpy with 32 bit addresses
movl $0, %ebx
.ext2_inode_read_bytes_memcpy_partial:
movb (%esi, %ebx), %al
movb %al, (%edi, %ebx)
incl %ebx
decl %ecx
jnz .ext2_inode_read_bytes_memcpy_partial
addl %ebx, %edi
# check if all sectors are read
cmpl $0, 4(%esp)
je .ext2_inode_read_bytes_done
.ext2_inode_read_bytes_no_partial_start:
# eax := data block index (byte_start / block_size)
movl 0(%esp), %eax
shrl $(EXT2_BLOCK_SHIFT), %eax
# get data block index and read block
call ext2_data_block_index
call ext2_read_block
# calculate bytes to copy (min(block_size, remaining_bytes))
movl $EXT2_BLOCK_SIZE, %ecx
cmpl %ecx, 4(%esp)
cmovbl 4(%esp), %ecx
# update remaining read info
addl %ecx, 0(%esp)
subl %ecx, 4(%esp)
# very dumb memcpy with 32 bit addresses
movl $ext2_block_buffer, %esi
movl $0, %ebx
.ext2_inode_read_bytes_memcpy:
movb (%esi, %ebx), %al
movb %al, (%edi, %ebx)
incl %ebx
decl %ecx
jnz .ext2_inode_read_bytes_memcpy
addl %ebx, %edi
# read next block if more sectors remaining
cmpl $0, 4(%esp)
jnz .ext2_inode_read_bytes_no_partial_start
.ext2_inode_read_bytes_done:
leavel
popal
ret
@ -393,8 +568,14 @@ ext2_directory_find_inode:
# search for kernel file from filesystem
# returns only on success
.global ext2_find_kernel
ext2_find_kernel:
pushl %eax
pushw %cx
pushw %di
pushw %si
movl $EXT2_ROOT_INO, %eax
call ext2_read_inode
@ -443,8 +624,10 @@ ext2_find_kernel:
movw $ext2_kernel_found_msg, %si
call puts; call print_newline
1: jmp 1b
popw %si
popw %di
popw %cx
popl %eax
ret
.ext2_find_kernel_part_not_dir:
@ -490,8 +673,8 @@ ext2_kernel_found_msg:
ext2_data_block_index_out_of_bounds_msg:
.asciz "data block index out of bounds"
ext2_data_block_index_unsupported_msg:
.asciz "unsupported data block index"
ext2_data_block_index_invalid_msg:
.asciz "data block index is invalid"
ext2_looking_for_msg:
.asciz "looking for "

156
bootloader/framebuffer.S Normal file
View File

@ -0,0 +1,156 @@
.set TARGET_WIDTH, 800
.set TARGET_HEIGHT, 600
.set TARGET_BPP, 32
.code16
.section .stage2
# Find suitable video mode
# return:
# ax: video mode number if found, 0 otherwise
.global vesa_find_video_mode
vesa_find_video_mode:
pushw %ax
pushw %cx
pushw %di
pushl %esi
# clear target mode and frame buffer
movw $0, (vesa_target_mode)
movl $0, (framebuffer + 0)
movl $0, (framebuffer + 4)
movl $0, (framebuffer + 8)
movl $0, (framebuffer + 12)
movw $0, (framebuffer + 16)
# get vesa information
movw $0x4F00, %ax
movw $vesa_info_buffer, %di
int $0x10
cmpb $0x4F, %al; jne .vesa_unsupported
cmpb $0x00, %ah; jne .vesa_error
# confirm that response starts with 'VESA'
cmpl $0x41534556, (vesa_info_buffer)
jne .vesa_error
# confirm that version is atleast 2.0
cmpw $0x0200, (vesa_info_buffer + 0x04)
jb .vesa_unsupported_version
movl $(vesa_info_buffer + 0x0E), %esi
movl (%esi), %esi
.vesa_find_video_mode_loop_modes:
cmpw $0xFFFF, (%esi)
je .vesa_find_video_mode_loop_modes_done
# get info of next mode
movw $0x4F01, %ax
movw (%esi), %cx
movw $vesa_mode_info_buffer, %di
int $0x10
cmpb $0x4F, %al; jne .vesa_unsupported
cmpb $0x00, %ah; jne .vesa_error
# check whether in graphics mode
testb $0x10, (vesa_mode_info_buffer + 0)
jz .vesa_find_video_mode_next_mode
# compare mode's dimensions
cmpw $TARGET_WIDTH, (vesa_mode_info_buffer + 0x12)
jne .vesa_find_video_mode_next_mode
cmpw $TARGET_HEIGHT, (vesa_mode_info_buffer + 0x14)
jne .vesa_find_video_mode_next_mode
cmpb $TARGET_BPP, (vesa_mode_info_buffer + 0x19)
jne .vesa_find_video_mode_next_mode
movl (vesa_mode_info_buffer + 0x28), %esi
movl %esi, (framebuffer + 0)
movw (vesa_mode_info_buffer + 0x10), %ax
movw %ax, (framebuffer + 4)
movl $TARGET_WIDTH, (framebuffer + 8)
movl $TARGET_HEIGHT, (framebuffer + 12)
movb $TARGET_BPP, (framebuffer + 16)
movb $1, (framebuffer + 17)
movw %cx, (vesa_target_mode)
jmp .vesa_find_video_mode_loop_modes_done
.vesa_find_video_mode_next_mode:
addl $2, %esi
jmp .vesa_find_video_mode_loop_modes
.vesa_find_video_mode_loop_modes_done:
popl %esi
popw %di
popw %cx
popw %ax
ret
.vesa_unsupported:
movw $vesa_unsupported_msg, %si
jmp print_and_halt
.vesa_unsupported_version:
movw $vesa_unsupported_version_msg, %si
jmp print_and_halt
.vesa_error:
movw $vesa_error_msg, %si
jmp print_and_halt
# set mode found from vesa_find_video_mode. if no mode
# was found, set it to 80x25 text mode to clear the screen.
.global vesa_set_target_mode
vesa_set_target_mode:
pushw %ax
pushw %bx
movw (vesa_target_mode), %bx
testw %bx, %bx
jz .vesa_set_target_mode_generic
movw $0x4F02, %ax
orw $0x4000, %bx
int $0x10
jmp .set_video_done
.vesa_set_target_mode_generic:
movb $0x03, %al
movb $0x00, %ah
int $0x10
.set_video_done:
popw %bx
popw %ax
ret
vesa_error_msg:
.asciz "VESA error"
vesa_unsupported_msg:
.asciz "VESA unsupported"
vesa_unsupported_version_msg:
.asciz "VESA unsupported version"
vesa_success_msg:
.asciz "VESA success"
.section .bss
vesa_info_buffer:
.skip 512
vesa_mode_info_buffer:
.skip 256
vesa_target_mode:
.skip 2
.global framebuffer
framebuffer:
.skip 4 # address
.skip 4 # pitch
.skip 4 # width
.skip 4 # height
.skip 1 # bpp
.skip 1 # type

View File

@ -122,6 +122,8 @@ memory_map_error_msg:
.section .bss
.global memory_map
memory_map:
memory_map_entry_count:
.skip 4
# 100 entries should be enough...

View File

@ -196,6 +196,68 @@ print_number:
popa
ret
# prints 8 bit hexadecimal number to screen
# al: number to print
.global print_hex8
print_hex8:
pushw %ax
pushw %bx
pushw %cx
movw $16, %bx
movw $2, %cx
andw $0xFF, %ax
call print_number
popw %cx
popw %bx
popw %ax
ret
# prints 16 bit hexadecimal number to screen
# ax: number to print
.global print_hex16
print_hex16:
pushw %bx
pushw %cx
movw $16, %bx
movw $4, %cx
call print_number
popw %cx
popw %bx
ret
# prints 32 bit hexadecimal number to screen
# eax: number to print
.global print_hex32
print_hex32:
pushl %eax
pushw %dx
movw %ax, %dx
shrl $16, %eax;
call print_hex16
movw %dx, %ax
call print_hex16
popw %dx
popl %eax
ret
# prints 64 bit hexadecimal number to screen
# edx:eax: number to print
.global print_hex64
print_hex64:
xchgl %eax, %edx
call print_hex32
xchgl %eax, %edx
call print_hex32
ret
# test if character is printable ascii
# al: character to test
# return:

View File

@ -12,6 +12,7 @@ set(KERNEL_SOURCES
font/prefs.psf.o
kernel/ACPI.cpp
kernel/APIC.cpp
kernel/BootInfo.cpp
kernel/CPUID.cpp
kernel/Debug.cpp
kernel/Device/Device.cpp

View File

@ -4,7 +4,6 @@
#include <kernel/LockGuard.h>
#include <kernel/Memory/kmalloc.h>
#include <kernel/Memory/PageTable.h>
#include <kernel/multiboot2.h>
extern uint8_t g_kernel_start[];
extern uint8_t g_kernel_end[];
@ -145,6 +144,14 @@ namespace Kernel
prepare_fast_page();
// Map main bios area below 1 MiB
map_range_at(
0x000E0000,
P2V(0x000E0000),
0x00100000 - 0x000E0000,
PageTable::Flags::Present
);
// Map (phys_kernel_start -> phys_kernel_end) to (virt_kernel_start -> virt_kernel_end)
ASSERT((vaddr_t)g_kernel_start % PAGE_SIZE == 0);
map_range_at(
@ -169,22 +176,6 @@ namespace Kernel
g_userspace_end - g_userspace_start,
Flags::Execute | Flags::UserSupervisor | Flags::Present
);
// Map multiboot memory
paddr_t multiboot2_data_start = (vaddr_t)g_multiboot2_info & PAGE_ADDR_MASK;
paddr_t multiboot2_data_end = (vaddr_t)g_multiboot2_info + g_multiboot2_info->total_size;
size_t multiboot2_needed_pages = BAN::Math::div_round_up<size_t>(multiboot2_data_end - multiboot2_data_start, PAGE_SIZE);
vaddr_t multiboot2_vaddr = reserve_free_contiguous_pages(multiboot2_needed_pages, KERNEL_OFFSET);
map_range_at(
multiboot2_data_start,
multiboot2_vaddr,
multiboot2_needed_pages * PAGE_SIZE,
Flags::ReadWrite | Flags::Present
);
g_multiboot2_info = (multiboot2_info_t*)(multiboot2_vaddr + ((vaddr_t)g_multiboot2_info % PAGE_SIZE));
}
void PageTable::prepare_fast_page()
@ -371,7 +362,7 @@ namespace Kernel
{
ASSERT(vaddr);
ASSERT(vaddr != fast_page());
if (vaddr >= KERNEL_OFFSET)
if (vaddr >= KERNEL_OFFSET && s_current)
ASSERT_GTE(vaddr, (vaddr_t)g_kernel_start);
if ((vaddr >= KERNEL_OFFSET) != (this == s_kernel))
Kernel::panic("mapping {8H} to {8H}, kernel: {}", paddr, vaddr, this == s_kernel);

View File

@ -21,8 +21,8 @@ multiboot2_start:
.short 5
.short 0
.long 20
.long 1920
.long 1080
.long 800
.long 600
.long 32
# legacy start
@ -50,11 +50,9 @@ multiboot2_end:
g_kernel_cmdline:
.skip 4096
.global g_multiboot2_info
g_multiboot2_info:
bootloader_magic:
.skip 8
.global g_multiboot2_magic
g_multiboot2_magic:
bootloader_info:
.skip 8
.section .data
@ -167,8 +165,8 @@ initialize_paging:
_start:
# Initialize stack and multiboot info
movl $V2P(g_boot_stack_top), %esp
movl %eax, V2P(g_multiboot2_magic)
movl %ebx, V2P(g_multiboot2_info)
movl %eax, V2P(bootloader_magic)
movl %ebx, V2P(bootloader_info)
call check_requirements
call enable_sse
@ -200,8 +198,11 @@ higher_half:
# call global constuctors
call _init
# call to the kernel itself (clear ebp for stacktrace)
# call to the kernel itself (clear rbp for stacktrace)
xorq %rbp, %rbp
movl V2P(bootloader_magic), %edi
movl V2P(bootloader_info), %esi
call kernel_main
# call global destructors

View File

@ -0,0 +1,36 @@
#pragma once
#include <stdint.h>
#define BANAN_BOOTLOADER_MAGIC 0xD3C60CFF
#define BANAN_BOOTLOADER_FB_RGB 1
struct BananBootFramebufferInfo
{
uint32_t address;
uint32_t pitch;
uint32_t width;
uint32_t height;
uint8_t bpp;
uint8_t type;
};
struct BananBootloaderMemoryMapEntry
{
uint64_t address;
uint64_t length;
uint32_t type;
} __attribute__((packed));
struct BananBootloaderMemoryMapInfo
{
uint32_t entry_count;
BananBootloaderMemoryMapEntry entries[];
} __attribute__((packed));
struct BananBootloaderInfo
{
uint32_t command_line_addr;
uint32_t framebuffer_addr;
uint32_t memory_map_addr;
} __attribute__((packed));

View File

@ -0,0 +1,47 @@
#pragma once
#include <BAN/String.h>
#include <BAN/StringView.h>
#include <BAN/Vector.h>
namespace Kernel
{
enum class FramebufferType
{
NONE,
UNKNOWN,
RGB
};
struct FramebufferInfo
{
paddr_t address;
uint32_t pitch;
uint32_t width;
uint32_t height;
uint8_t bpp;
FramebufferType type = FramebufferType::NONE;
};
struct MemoryMapEntry
{
uint32_t type;
paddr_t address;
uint64_t length;
};
struct BootInfo
{
BAN::String command_line;
FramebufferInfo framebuffer;
BAN::Vector<MemoryMapEntry> memory_map_entries;
};
bool validate_boot_magic(uint32_t magic);
void parse_boot_info(uint32_t magic, uint32_t info);
BAN::StringView get_early_boot_command_line(uint32_t magic, uint32_t info);
extern BootInfo g_boot_info;
}

View File

@ -13,11 +13,18 @@
#define MULTIBOOT2_FRAMEBUFFER_TYPE_RGB 1
#define MULTIBOOT2_MAGIC 0x36d76289
struct multiboot2_tag_t
{
uint32_t type;
uint32_t size;
multiboot2_tag_t* next() { return (multiboot2_tag_t*)((uintptr_t)this + ((size + 7) & ~7)); }
const multiboot2_tag_t* next() const
{
return reinterpret_cast<const multiboot2_tag_t*>(
reinterpret_cast<uintptr_t>(this) + ((size + 7) & ~7)
);
}
} __attribute__((packed));
struct multiboot2_cmdline_tag_t : public multiboot2_tag_t
@ -62,14 +69,3 @@ struct multiboot2_info_t
uint32_t reserved;
multiboot2_tag_t tags[];
} __attribute__((packed));
extern "C" multiboot2_info_t* g_multiboot2_info;
extern "C" uint32_t g_multiboot2_magic;
inline multiboot2_tag_t* multiboot2_find_tag(uint32_t type)
{
for (auto* tag = g_multiboot2_info->tags; tag->type != MULTIBOOT2_TAG_END; tag = tag->next())
if (tag->type == type)
return tag;
return nullptr;
}

View File

@ -2,7 +2,6 @@
#include <BAN/StringView.h>
#include <kernel/ACPI.h>
#include <kernel/Memory/PageTable.h>
#include <kernel/multiboot2.h>
#include <lai/core.h>
@ -85,11 +84,14 @@ namespace Kernel
static const RSDP* locate_rsdp()
{
// FIXME: add this back
#if 0
// Check the multiboot headers
if (auto* rsdp_new = (multiboot2_rsdp_tag_t*)multiboot2_find_tag(MULTIBOOT2_TAG_NEW_RSDP))
return (const RSDP*)rsdp_new->data;
if (auto* rsdp_old = (multiboot2_rsdp_tag_t*)multiboot2_find_tag(MULTIBOOT2_TAG_OLD_RSDP))
return (const RSDP*)rsdp_old->data;
#endif
// Look in main BIOS area below 1 MB
for (uintptr_t addr = P2V(0x000E0000); addr < P2V(0x000FFFFF); addr += 16)

135
kernel/kernel/BootInfo.cpp Normal file
View File

@ -0,0 +1,135 @@
#include <kernel/BootInfo.h>
#include <kernel/BananBootloader.h>
#include <kernel/multiboot2.h>
namespace Kernel
{
BootInfo g_boot_info;
void parse_boot_info_multiboot2(uint32_t info)
{
const auto& multiboot2_info = *reinterpret_cast<const multiboot2_info_t*>(info);
for (const auto* tag = multiboot2_info.tags; tag->type != MULTIBOOT2_TAG_END; tag = tag->next())
{
if (tag->type == MULTIBOOT2_TAG_CMDLINE)
{
const auto& command_line_tag = *reinterpret_cast<const multiboot2_cmdline_tag_t*>(tag);
MUST(g_boot_info.command_line.append(command_line_tag.cmdline));
}
else if (tag->type == MULTIBOOT2_TAG_FRAMEBUFFER)
{
const auto& framebuffer_tag = *reinterpret_cast<const multiboot2_framebuffer_tag_t*>(tag);
g_boot_info.framebuffer.address = framebuffer_tag.framebuffer_addr;
g_boot_info.framebuffer.pitch = framebuffer_tag.framebuffer_pitch;
g_boot_info.framebuffer.width = framebuffer_tag.framebuffer_width;
g_boot_info.framebuffer.height = framebuffer_tag.framebuffer_height;
g_boot_info.framebuffer.bpp = framebuffer_tag.framebuffer_bpp;
if (framebuffer_tag.framebuffer_type == MULTIBOOT2_FRAMEBUFFER_TYPE_RGB)
g_boot_info.framebuffer.type = FramebufferType::RGB;
else
g_boot_info.framebuffer.type = FramebufferType::UNKNOWN;
}
else if (tag->type == MULTIBOOT2_TAG_MMAP)
{
const auto& mmap_tag = *reinterpret_cast<const multiboot2_mmap_tag_t*>(tag);
const size_t entry_count = (mmap_tag.size - sizeof(multiboot2_mmap_tag_t)) / mmap_tag.entry_size;
MUST(g_boot_info.memory_map_entries.resize(entry_count));
for (size_t i = 0; i < entry_count; i++)
{
const auto& mmap_entry = *reinterpret_cast<const multiboot2_mmap_entry_t*>(reinterpret_cast<uintptr_t>(tag) + sizeof(multiboot2_mmap_tag_t) + i * mmap_tag.entry_size);
dprintln("entry {16H} {16H} {8H}",
(uint64_t)mmap_entry.base_addr,
(uint64_t)mmap_entry.length,
(uint64_t)mmap_entry.type
);
g_boot_info.memory_map_entries[i].address = mmap_entry.base_addr;
g_boot_info.memory_map_entries[i].length = mmap_entry.length;
g_boot_info.memory_map_entries[i].type = mmap_entry.type;
}
}
}
}
BAN::StringView get_early_boot_command_line_multiboot2(uint32_t info)
{
const auto& multiboot2_info = *reinterpret_cast<const multiboot2_info_t*>(info);
for (const auto* tag = multiboot2_info.tags; tag->type != MULTIBOOT2_TAG_END; tag = tag->next())
if (tag->type == MULTIBOOT2_TAG_CMDLINE)
return reinterpret_cast<const multiboot2_cmdline_tag_t*>(tag)->cmdline;
return {};
}
void parse_boot_info_banan_bootloader(uint32_t info)
{
const auto& banan_bootloader_info = *reinterpret_cast<const BananBootloaderInfo*>(info);
const char* command_line = reinterpret_cast<const char*>(banan_bootloader_info.command_line_addr);
MUST(g_boot_info.command_line.append(command_line));
const auto& framebuffer = *reinterpret_cast<BananBootFramebufferInfo*>(banan_bootloader_info.framebuffer_addr);
if (framebuffer.type == BANAN_BOOTLOADER_FB_RGB)
{
g_boot_info.framebuffer.address = framebuffer.address;
g_boot_info.framebuffer.width = framebuffer.width;
g_boot_info.framebuffer.height = framebuffer.height;
g_boot_info.framebuffer.pitch = framebuffer.pitch;
g_boot_info.framebuffer.bpp = framebuffer.bpp;
g_boot_info.framebuffer.type = FramebufferType::RGB;
}
const auto& memory_map = *reinterpret_cast<BananBootloaderMemoryMapInfo*>(banan_bootloader_info.memory_map_addr);
MUST(g_boot_info.memory_map_entries.resize(memory_map.entry_count));
for (size_t i = 0; i < memory_map.entry_count; i++)
{
const auto& mmap_entry = memory_map.entries[i];
g_boot_info.memory_map_entries[i].address = mmap_entry.address;
g_boot_info.memory_map_entries[i].length = mmap_entry.length;
g_boot_info.memory_map_entries[i].type = mmap_entry.type;
}
}
BAN::StringView get_early_boot_command_line_banan_bootloader(uint32_t info)
{
const auto& banan_bootloader_info = *reinterpret_cast<const BananBootloaderInfo*>(info);
return reinterpret_cast<const char*>(banan_bootloader_info.command_line_addr);
}
bool validate_boot_magic(uint32_t magic)
{
if (magic == MULTIBOOT2_MAGIC)
return true;
if (magic == BANAN_BOOTLOADER_MAGIC)
return true;
return false;
}
void parse_boot_info(uint32_t magic, uint32_t info)
{
switch (magic)
{
case MULTIBOOT2_MAGIC:
return parse_boot_info_multiboot2(info);
case BANAN_BOOTLOADER_MAGIC:
return parse_boot_info_banan_bootloader(info);
}
ASSERT_NOT_REACHED();
}
BAN::StringView get_early_boot_command_line(uint32_t magic, uint32_t info)
{
switch (magic)
{
case MULTIBOOT2_MAGIC:
return get_early_boot_command_line_multiboot2(info);
case BANAN_BOOTLOADER_MAGIC:
return get_early_boot_command_line_banan_bootloader(info);
}
ASSERT_NOT_REACHED();
}
}

View File

@ -56,9 +56,7 @@ namespace Kernel
const uint32_t indices_per_block = blksize() / sizeof(uint32_t);
uint32_t divisor = 1;
for (uint32_t i = 1; i < depth; i++)
divisor *= indices_per_block;
const uint32_t divisor = (depth > 1) ? indices_per_block * (depth - 1) : 1;
const uint32_t next_block = block_buffer.span().as_span<uint32_t>()[(index / divisor) % indices_per_block];
if (next_block == 0)

View File

@ -1,7 +1,7 @@
#include <kernel/BootInfo.h>
#include <kernel/LockGuard.h>
#include <kernel/Memory/Heap.h>
#include <kernel/Memory/PageTable.h>
#include <kernel/multiboot2.h>
extern uint8_t g_kernel_end[];
@ -26,30 +26,33 @@ namespace Kernel
void Heap::initialize_impl()
{
auto* mmap_tag = (multiboot2_mmap_tag_t*)multiboot2_find_tag(MULTIBOOT2_TAG_MMAP);
if (mmap_tag == nullptr)
if (g_boot_info.memory_map_entries.empty())
Kernel::panic("Bootloader did not provide a memory map");
for (size_t offset = sizeof(*mmap_tag); offset < mmap_tag->size; offset += mmap_tag->entry_size)
for (const auto& entry : g_boot_info.memory_map_entries)
{
auto* mmap_entry = (multiboot2_mmap_entry_t*)((uintptr_t)mmap_tag + offset);
dprintln("{16H}, {16H}, {8H}",
entry.address,
entry.length,
entry.type
);
if (mmap_entry->type == 1)
{
paddr_t start = mmap_entry->base_addr;
if (start < V2P(g_kernel_end))
start = V2P(g_kernel_end);
if (auto rem = start % PAGE_SIZE)
start += PAGE_SIZE - rem;
if (entry.type != 1)
continue;
paddr_t end = mmap_entry->base_addr + mmap_entry->length;
if (auto rem = end % PAGE_SIZE)
end -= rem;
paddr_t start = entry.address;
if (start < V2P(g_kernel_end))
start = V2P(g_kernel_end);
if (auto rem = start % PAGE_SIZE)
start += PAGE_SIZE - rem;
// Physical pages needs atleast 2 pages
if (end > start + PAGE_SIZE)
MUST(m_physical_ranges.emplace_back(start, end - start));
}
paddr_t end = entry.address + entry.length;
if (auto rem = end % PAGE_SIZE)
end -= rem;
// Physical pages needs atleast 2 pages
if (end > start + PAGE_SIZE)
MUST(m_physical_ranges.emplace_back(start, end - start));
}
size_t total = 0;

View File

@ -57,7 +57,11 @@ namespace Kernel
auto& driver = s_serial_drivers[i];
driver.m_port = s_serial_ports[i];
if (!driver.initialize_size())
continue;
{
// if size detection fails, just use some random size
driver.m_width = 999;
driver.m_height = 999;
}
count++;
}
}

View File

@ -1,42 +1,41 @@
#include <BAN/Errors.h>
#include <kernel/BootInfo.h>
#include <kernel/Debug.h>
#include <kernel/Memory/PageTable.h>
#include <kernel/multiboot2.h>
#include <kernel/Terminal/VesaTerminalDriver.h>
using namespace Kernel;
VesaTerminalDriver* VesaTerminalDriver::create()
{
auto* framebuffer_tag = (multiboot2_framebuffer_tag_t*)multiboot2_find_tag(MULTIBOOT2_TAG_FRAMEBUFFER);
if (framebuffer_tag == nullptr)
if (g_boot_info.framebuffer.type == FramebufferType::NONE)
{
dprintln("Bootloader did not provide framebuffer");
return nullptr;
}
if (framebuffer_tag->framebuffer_type != MULTIBOOT2_FRAMEBUFFER_TYPE_RGB)
if (g_boot_info.framebuffer.type != FramebufferType::RGB)
{
dprintln("unsupported framebuffer type {}", framebuffer_tag->framebuffer_type);
dprintln("unsupported framebuffer type");
return nullptr;
}
if (framebuffer_tag->framebuffer_bpp != 24 && framebuffer_tag->framebuffer_bpp != 32)
if (g_boot_info.framebuffer.bpp != 24 && g_boot_info.framebuffer.bpp != 32)
{
dprintln("Unsupported bpp {}", framebuffer_tag->framebuffer_bpp);
dprintln("Unsupported bpp {}", g_boot_info.framebuffer.bpp);
return nullptr;
}
dprintln("Graphics Mode {}x{} ({} bpp)",
(uint32_t)framebuffer_tag->framebuffer_width,
(uint32_t)framebuffer_tag->framebuffer_height,
(uint8_t)framebuffer_tag->framebuffer_bpp
g_boot_info.framebuffer.width,
g_boot_info.framebuffer.height,
g_boot_info.framebuffer.bpp
);
paddr_t paddr = framebuffer_tag->framebuffer_addr & PAGE_ADDR_MASK;
paddr_t paddr = g_boot_info.framebuffer.address & PAGE_ADDR_MASK;
size_t needed_pages = range_page_count(
framebuffer_tag->framebuffer_addr,
framebuffer_tag->framebuffer_pitch * framebuffer_tag->framebuffer_height
g_boot_info.framebuffer.address,
g_boot_info.framebuffer.pitch * g_boot_info.framebuffer.height
);
vaddr_t vaddr = PageTable::kernel().reserve_free_contiguous_pages(needed_pages, KERNEL_OFFSET);
@ -45,10 +44,10 @@ VesaTerminalDriver* VesaTerminalDriver::create()
PageTable::kernel().map_range_at(paddr, vaddr, needed_pages * PAGE_SIZE, PageTable::Flags::UserSupervisor | PageTable::Flags::ReadWrite | PageTable::Flags::Present);
auto* driver = new VesaTerminalDriver(
framebuffer_tag->framebuffer_width,
framebuffer_tag->framebuffer_height,
framebuffer_tag->framebuffer_pitch,
framebuffer_tag->framebuffer_bpp,
g_boot_info.framebuffer.width,
g_boot_info.framebuffer.height,
g_boot_info.framebuffer.pitch,
g_boot_info.framebuffer.bpp,
vaddr
);
driver->set_cursor_position(0, 0);

View File

@ -1,5 +1,6 @@
#include <kernel/ACPI.h>
#include <kernel/Arch.h>
#include <kernel/BootInfo.h>
#include <kernel/Debug.h>
#include <kernel/FS/DevFS/FileSystem.h>
#include <kernel/FS/ProcFS/FileSystem.h>
@ -12,7 +13,6 @@
#include <kernel/Memory/Heap.h>
#include <kernel/Memory/kmalloc.h>
#include <kernel/Memory/PageTable.h>
#include <kernel/multiboot2.h>
#include <kernel/PCI.h>
#include <kernel/PIC.h>
#include <kernel/Process.h>
@ -31,13 +31,9 @@ struct ParsedCommandLine
BAN::StringView root;
};
static bool should_disable_serial()
static bool should_disable_serial(BAN::StringView full_command_line)
{
auto* cmdline_tag = (multiboot2_cmdline_tag_t*)multiboot2_find_tag(MULTIBOOT2_TAG_CMDLINE);
if (cmdline_tag == nullptr)
return false;
const char* start = cmdline_tag->cmdline;
const char* start = full_command_line.data();
const char* current = start;
while (true)
{
@ -59,13 +55,8 @@ static ParsedCommandLine cmdline;
static void parse_command_line()
{
auto* cmdline_tag = (multiboot2_cmdline_tag_t*)multiboot2_find_tag(MULTIBOOT2_TAG_CMDLINE);
if (cmdline_tag == nullptr)
return;
BAN::StringView full_command_line(cmdline_tag->cmdline);
auto full_command_line = Kernel::g_boot_info.command_line.sv();
auto arguments = MUST(full_command_line.split(' '));
for (auto argument : arguments)
{
if (argument == "noapic")
@ -83,27 +74,31 @@ TerminalDriver* g_terminal_driver = nullptr;
static void init2(void*);
extern "C" void kernel_main()
extern "C" void kernel_main(uint32_t boot_magic, uint32_t boot_info)
{
using namespace Kernel;
DISABLE_INTERRUPTS();
if (!should_disable_serial())
if (!validate_boot_magic(boot_magic))
{
Serial::initialize();
dprintln("Unrecognized boot magic {8H}", boot_magic);
return;
}
if (!should_disable_serial(get_early_boot_command_line(boot_magic, boot_info)))
{
Serial::initialize();
dprintln("Serial output initialized");
}
if (g_multiboot2_magic != 0x36d76289)
{
dprintln("Invalid multiboot magic number");
return;
}
kmalloc_initialize();
dprintln("kmalloc initialized");
parse_boot_info(boot_magic, boot_info);
dprintln("boot info parsed");
GDT::initialize();
dprintln("GDT initialized");