Bootloader: Implement basic ext2 filesystem
This can search for files in an ext2 filesystem. Only 12 blocks are currently supported. Now only ELF loading is missing for loading the actual kernel!
This commit is contained in:
parent
8aab3a62cc
commit
a9412aa741
|
@ -6,6 +6,7 @@ set(BOOTLOADER_SOURCES
|
|||
boot.S
|
||||
command_line.S
|
||||
disk.S
|
||||
ext2.S
|
||||
memory_map.S
|
||||
utils.S
|
||||
)
|
||||
|
|
|
@ -67,6 +67,13 @@ stage2_main:
|
|||
call find_root_partition
|
||||
|
||||
call print_root_partition_info
|
||||
call print_newline
|
||||
|
||||
call has_ext2_filesystem
|
||||
testb %al, %al
|
||||
jz print_and_halt
|
||||
|
||||
call ext2_find_kernel
|
||||
|
||||
jmp halt
|
||||
|
||||
|
|
|
@ -305,6 +305,10 @@ find_root_disk:
|
|||
# fills root_partition_entry data structure
|
||||
# NOTE: assumes GPT header is in `gpt_header`
|
||||
# NO REGISTERS SAVED
|
||||
# return:
|
||||
# dl: drive number
|
||||
# ecx: sector count (capped at 0xFFFFFFFF)
|
||||
# bx:eax: first sector
|
||||
.global find_root_partition
|
||||
find_root_partition:
|
||||
pushl %ebp
|
||||
|
@ -385,7 +389,7 @@ find_root_partition:
|
|||
|
||||
# increment 8 byte entry array lba
|
||||
incl 0(%esp)
|
||||
jno .find_root_partition_no_overflow
|
||||
jnc .find_root_partition_no_overflow
|
||||
incl 4(%esp)
|
||||
|
||||
.find_root_partition_no_overflow:
|
||||
|
@ -406,6 +410,32 @@ find_root_partition:
|
|||
movw $root_partition_found_msg, %si
|
||||
call puts; call print_newline
|
||||
|
||||
# ebx:eax := last lba
|
||||
movl (root_partition_entry + 44), %ebx
|
||||
movl (root_partition_entry + 40), %eax
|
||||
|
||||
# ebx:eax -= first lba - 1
|
||||
subl (root_partition_entry + 36), %ebx
|
||||
movl (root_partition_entry + 32), %ecx;
|
||||
decl %ecx
|
||||
subl %ecx, %eax
|
||||
jnc .find_root_partition_count_sub_no_carry
|
||||
decl %ebx
|
||||
.find_root_partition_count_sub_no_carry:
|
||||
|
||||
# ecx: min(partition count, 0xFFFFFFFF)
|
||||
movl $0xFFFFFFFF, %edx
|
||||
movl %eax, %ecx
|
||||
testl %ebx, %ebx
|
||||
cmovnzl %edx, %ecx
|
||||
|
||||
# ebx:eax := first lba
|
||||
# FIXME: confirm ebx bits 16:31 are zero
|
||||
movl (root_partition_entry + 36), %ebx
|
||||
movl (root_partition_entry + 32), %eax
|
||||
|
||||
movb (root_disk_drive_number), %dl
|
||||
|
||||
leavel
|
||||
ret
|
||||
|
||||
|
|
|
@ -0,0 +1,522 @@
|
|||
# FIXME: don't assume 512 byte sectors
|
||||
.set SECTOR_SHIFT, 9
|
||||
.set SECTOR_SIZE, 1 << SECTOR_SHIFT
|
||||
|
||||
# FIXME: don't assume 1024 byte blocks
|
||||
.set EXT2_BLOCK_SHIFT, 10
|
||||
.set EXT2_BLOCK_SIZE, 1 << EXT2_BLOCK_SHIFT
|
||||
.set EXT2_SUPERBLOCK_SIZE, 264
|
||||
.set EXT2_BGD_SHIFT, 5
|
||||
.set EXT2_BGD_SIZE, 1 << EXT2_BGD_SHIFT
|
||||
.set EXT2_INODE_SIZE_MAX, 256
|
||||
.set EXT2_ROOT_INO, 2
|
||||
.set EXT2_GOOD_OLD_REV, 0
|
||||
|
||||
# inode types
|
||||
.set EXT2_S_IMASK, 0xF000
|
||||
.set EXT2_S_IFDIR, 0x4000
|
||||
.set EXT2_S_IFREG, 0x8000
|
||||
|
||||
# superblock offsets
|
||||
.set s_log_block_size, 24
|
||||
.set s_inodes_per_group, 40
|
||||
.set s_magic, 56
|
||||
.set s_rev_level, 76
|
||||
.set s_inode_size, 88
|
||||
|
||||
# block group descriptor offsets
|
||||
.set bg_inode_table, 8
|
||||
|
||||
# inode offsets
|
||||
.set i_mode, 0
|
||||
.set i_size, 4
|
||||
.set i_block, 40
|
||||
|
||||
|
||||
.code16
|
||||
.section .stage2
|
||||
|
||||
# checks whether partition contains ext2 filesystem.
|
||||
# fills ext2_superblock_buffer
|
||||
# dl: drive number
|
||||
# ecx: sector count
|
||||
# bx:eax: first sector
|
||||
# return:
|
||||
# al: 1 if is ext2, 0 otherwise
|
||||
# si: error message on error
|
||||
.global has_ext2_filesystem
|
||||
has_ext2_filesystem:
|
||||
pushl %ecx
|
||||
pushw %bx
|
||||
pushw %di
|
||||
|
||||
# fill ext2_partition_first_sector
|
||||
movw $0, (ext2_partition_first_sector + 6)
|
||||
movw %bx, (ext2_partition_first_sector + 4)
|
||||
movl %eax, (ext2_partition_first_sector + 0)
|
||||
|
||||
# fill ext2_drive_number
|
||||
movb %dl, (ext2_drive_number)
|
||||
|
||||
cmpl $3, %ecx
|
||||
jb .has_ext2_filesystem_does_not_fit
|
||||
|
||||
# one sector
|
||||
movw $1, %cx
|
||||
|
||||
# from byte offset 1024
|
||||
addl $(1024 / SECTOR_SIZE), %eax
|
||||
jnc .has_ext2_filesystem_no_overflow
|
||||
incw %bx
|
||||
.has_ext2_filesystem_no_overflow:
|
||||
|
||||
# into sector buffer
|
||||
movw $ext2_block_buffer, %di
|
||||
|
||||
call read_from_disk
|
||||
|
||||
# copy superblock to its buffer
|
||||
movw $ext2_block_buffer, %si
|
||||
movw $ext2_superblock_buffer, %di
|
||||
movw $EXT2_SUPERBLOCK_SIZE, %cx
|
||||
rep movsb
|
||||
|
||||
# verify magic
|
||||
cmpw $0xEF53, (ext2_superblock_buffer + s_magic)
|
||||
jne .has_ext2_filesystem_invalid_magic
|
||||
|
||||
# verify block size
|
||||
# verify shift fits in one byte
|
||||
movl (ext2_superblock_buffer + s_log_block_size), %ecx
|
||||
testl $0xFFFFFF00, %ecx
|
||||
jnz .has_ext2_filesystem_unsupported_block_size
|
||||
# verify 1024 << s_log_block_size == EXT2_BLOCK_SIZE
|
||||
movl $1024, %eax
|
||||
shll %cl, %eax
|
||||
cmpl $EXT2_BLOCK_SIZE, %eax
|
||||
jne .has_ext2_filesystem_unsupported_block_size
|
||||
|
||||
# fill inode size
|
||||
movl $128, %eax
|
||||
cmpl $EXT2_GOOD_OLD_REV, (ext2_superblock_buffer + s_rev_level)
|
||||
cmovnel (ext2_superblock_buffer + s_inode_size), %eax
|
||||
movl %eax, (ext2_inode_size)
|
||||
|
||||
movb $1, %al
|
||||
jmp .has_ext2_filesystem_done
|
||||
|
||||
.has_ext2_filesystem_does_not_fit:
|
||||
movw $root_partition_does_not_fit_ext2_filesystem_msg, %si
|
||||
movb $0, %al
|
||||
jmp .has_ext2_filesystem_done
|
||||
|
||||
.has_ext2_filesystem_invalid_magic:
|
||||
movw $root_partition_has_invalid_ext2_magic_msg, %si
|
||||
movb $0, %al
|
||||
jmp .has_ext2_filesystem_done
|
||||
|
||||
.has_ext2_filesystem_unsupported_block_size:
|
||||
movw $root_partition_has_unsupported_ext2_block_size_msg, %si
|
||||
movb $0, %al
|
||||
jmp .has_ext2_filesystem_done
|
||||
|
||||
.has_ext2_filesystem_done:
|
||||
popw %di
|
||||
popw %bx
|
||||
popl %ecx
|
||||
ret
|
||||
|
||||
|
||||
# reads block in to ext2_block_buffer
|
||||
# eax: block number
|
||||
ext2_read_block:
|
||||
pushl %eax
|
||||
pushl %ebx
|
||||
pushw %cx
|
||||
pushl %edx
|
||||
pushw %di
|
||||
|
||||
# NOTE: this assumes 1024 block size
|
||||
# eax := (block * block_size) / sector_size := (eax << EXT2_BLOCK_SHIFT) >> SECTOR_SHIFT
|
||||
xorl %edx, %edx
|
||||
shll $EXT2_BLOCK_SHIFT, %eax
|
||||
shrl $SECTOR_SHIFT, %eax
|
||||
|
||||
# ebx:eax := eax + (ext2_partition_first_sector)
|
||||
movl (ext2_partition_first_sector + 4), %ebx
|
||||
addl (ext2_partition_first_sector + 0), %eax
|
||||
jnc .ext2_read_block_no_carry
|
||||
incl %ebx
|
||||
.ext2_read_block_no_carry:
|
||||
|
||||
# sectors per block
|
||||
movw $(EXT2_BLOCK_SIZE / SECTOR_SIZE), %cx
|
||||
|
||||
movw $ext2_block_buffer, %di
|
||||
|
||||
movb (ext2_drive_number), %dl
|
||||
call read_from_disk
|
||||
|
||||
popw %di
|
||||
popl %edx
|
||||
popw %cx
|
||||
popl %ebx
|
||||
popl %eax
|
||||
ret
|
||||
|
||||
|
||||
# reads block group descrtiptor into ext2_block_group_descriptor
|
||||
# eax: block group
|
||||
ext2_read_block_group_descriptor:
|
||||
pushal
|
||||
|
||||
# eax := bgd_byte_offset := 2048 + EXT2_BGD_SIZE * eax := (eax << EXT2_BGD_SHIFT) + 2048
|
||||
shll $EXT2_BGD_SHIFT, %eax
|
||||
addl $2048, %eax
|
||||
|
||||
# eax: bgd_block := bgd_byte_offset / EXT2_BLOCK_SIZE
|
||||
# ebx: bgd_offset := bgd_byte_offset % EXT2_BLOCK_SIZE
|
||||
xorl %edx, %edx
|
||||
movl $EXT2_BLOCK_SIZE, %ebx
|
||||
divl %ebx
|
||||
movl %edx, %ebx
|
||||
|
||||
call ext2_read_block
|
||||
|
||||
# esi := &ext2_block_buffer + bgd_offset := ebx + &ext2_block_buffer
|
||||
# edi := &ext2_block_group_descriptor_buffer
|
||||
movl %ebx, %esi
|
||||
addl $ext2_block_buffer, %esi
|
||||
movl $ext2_block_group_descriptor_buffer, %edi
|
||||
movw $EXT2_BGD_SIZE, %cx
|
||||
rep movsb
|
||||
|
||||
popal
|
||||
ret
|
||||
|
||||
|
||||
# reads inode into ext2_inode_buffer
|
||||
# eax: ino
|
||||
ext2_read_inode:
|
||||
pushal
|
||||
|
||||
# eax := block_group = (ino - 1) / s_inodes_per_group
|
||||
# ebx := inode_index = (ino - 1) % s_inodes_per_group
|
||||
xorl %edx, %edx
|
||||
decl %eax
|
||||
movl (ext2_superblock_buffer + s_inodes_per_group), %ebx
|
||||
divl %ebx
|
||||
movl %edx, %ebx
|
||||
|
||||
call ext2_read_block_group_descriptor
|
||||
|
||||
# eax := inode_table_block := (inode_index * inode_size) / EXT2_BLOCK_SIZE
|
||||
# ebx := inode_table_offset := (inode_index * inode_size) % EXT2_BLOCK_SIZE
|
||||
xorl %edx, %edx
|
||||
movl %ebx, %eax
|
||||
movl (ext2_inode_size), %ebx
|
||||
mull %ebx
|
||||
movl $EXT2_BLOCK_SIZE, %ebx
|
||||
divl %ebx
|
||||
movl %edx, %ebx
|
||||
|
||||
# eax := file system block := eax + bg_inode_table
|
||||
addl (ext2_block_group_descriptor_buffer + bg_inode_table), %eax
|
||||
|
||||
movb (ext2_drive_number), %dl
|
||||
call ext2_read_block
|
||||
|
||||
# copy inode memory
|
||||
# esi := inode_table_offset + ext2_block_buffer := edx + ext2_block_buffer
|
||||
movl %ebx, %esi
|
||||
addl $ext2_block_buffer, %esi
|
||||
# edi := ext2_inode_buffer
|
||||
movl $ext2_inode_buffer, %edi
|
||||
# cx := inode_size
|
||||
movw (ext2_inode_size), %cx
|
||||
rep movsb
|
||||
|
||||
popal
|
||||
ret
|
||||
|
||||
|
||||
# gets block index from n'th data block in inode stored in ext2_inode_buffer
|
||||
# eax: data block index
|
||||
# return:
|
||||
# eax: block index
|
||||
ext2_data_block_index:
|
||||
pushl %ecx
|
||||
|
||||
# calculate max data blocks
|
||||
movl (ext2_inode_buffer + i_size), %ecx
|
||||
addl (ext2_inode_size), %ecx
|
||||
decl %ecx
|
||||
shll $EXT2_BLOCK_SHIFT, %ecx
|
||||
|
||||
# verify data block is within bounds
|
||||
cmpl %ecx, %eax
|
||||
jae .ext2_data_block_index_out_of_bounds
|
||||
|
||||
# check if this is direct block access
|
||||
cmpl $12, %eax
|
||||
jb .ext2_data_block_index_direct
|
||||
|
||||
jmp .ext2_data_block_index_unsupported
|
||||
|
||||
.ext2_data_block_index_direct:
|
||||
movl $(ext2_inode_buffer + i_block), %ecx
|
||||
addl %eax, %ecx
|
||||
movl (%ecx), %eax
|
||||
jmp .ext2_data_block_index_done
|
||||
|
||||
.ext2_data_block_index_out_of_bounds:
|
||||
movw $ext2_data_block_index_out_of_bounds_msg, %si
|
||||
call puts; call print_newline
|
||||
movl $0, %eax
|
||||
jmp .ext2_data_block_index_done
|
||||
|
||||
.ext2_data_block_index_unsupported:
|
||||
movw $ext2_data_block_index_unsupported_msg, %si
|
||||
call puts; call print_newline
|
||||
movl $0, %eax
|
||||
jmp .ext2_data_block_index_done
|
||||
|
||||
.ext2_data_block_index_done:
|
||||
popl %ecx
|
||||
ret
|
||||
|
||||
|
||||
# find inode in inside directory inode stored in ext2_inode_buffer
|
||||
# store the found inode in ext2_inode_buffer
|
||||
# si: name string
|
||||
# cx: name length
|
||||
# return:
|
||||
# eax: ino if inode was found, 0 otherwise
|
||||
ext2_directory_find_inode:
|
||||
pushl %ebx
|
||||
pushw %cx
|
||||
pushw %dx
|
||||
pushw %si
|
||||
pushw %di
|
||||
|
||||
pushl %ebp
|
||||
movl %esp, %ebp
|
||||
subl $8, %esp
|
||||
|
||||
# 0(%esp) := name length
|
||||
movw %cx, 0(%esp)
|
||||
|
||||
# 2(%esp) := name string
|
||||
movw %si, 2(%esp)
|
||||
|
||||
# verify that the name is <= 0xFF bytes
|
||||
cmpw $0xFF, %cx
|
||||
ja .ext2_directory_find_inode_not_found
|
||||
|
||||
# ebx := max data blocks: ceil(i_size / EXT2_BLOCK_SIZE)
|
||||
movl (ext2_inode_buffer + i_size), %ebx
|
||||
addl $EXT2_BLOCK_SHIFT, %ebx
|
||||
decl %ebx
|
||||
shrl $EXT2_BLOCK_SHIFT, %ebx
|
||||
jz .ext2_directory_find_inode_not_found
|
||||
|
||||
# 4(%esp) := current block
|
||||
movl $0, 4(%esp)
|
||||
|
||||
.ext2_directory_find_inode_block_read_loop:
|
||||
# get next block index
|
||||
movl 4(%esp), %eax
|
||||
call ext2_data_block_index
|
||||
test %eax, %eax
|
||||
jz .ext2_directory_find_inode_next_block
|
||||
|
||||
# read current block
|
||||
call ext2_read_block
|
||||
|
||||
# dx := current entry pointer
|
||||
movw $ext2_block_buffer, %si
|
||||
|
||||
.ext2_directory_find_inode_loop_entries:
|
||||
# temporarily store entry pointer in dx
|
||||
movw %si, %dx
|
||||
|
||||
# check if name length matches
|
||||
# cx := name length
|
||||
movw 0(%esp), %cx
|
||||
cmpb 6(%si), %cl
|
||||
jne .ext2_directory_find_inode_next_entry
|
||||
|
||||
# si := entry name
|
||||
addw $8, %si
|
||||
|
||||
# di := asked name
|
||||
movw 2(%esp), %di
|
||||
|
||||
# check if name matches
|
||||
call memcmp
|
||||
test %al, %al
|
||||
# NOTE: dx contains entry pointer
|
||||
jnz .ext2_directory_find_inode_found
|
||||
|
||||
.ext2_directory_find_inode_next_entry:
|
||||
# restore si
|
||||
movw %dx, %si
|
||||
|
||||
# go to next entry if this block contains one
|
||||
addw 4(%si), %si
|
||||
cmpw $(ext2_block_buffer + EXT2_BLOCK_SIZE), %si
|
||||
jb .ext2_directory_find_inode_loop_entries
|
||||
|
||||
.ext2_directory_find_inode_next_block:
|
||||
incl 4(%esp)
|
||||
cmpl %ebx, 4(%esp)
|
||||
jb .ext2_directory_find_inode_block_read_loop
|
||||
|
||||
.ext2_directory_find_inode_not_found:
|
||||
movb $0, %al
|
||||
jmp .ext2_directory_find_inode_done
|
||||
|
||||
.ext2_directory_find_inode_found:
|
||||
# extract ino and read it to ext2_inode_buffer
|
||||
movw %dx, %si
|
||||
movl 0(%si), %eax
|
||||
call ext2_read_inode
|
||||
|
||||
.ext2_directory_find_inode_done:
|
||||
leavel
|
||||
popw %di
|
||||
popw %si
|
||||
popw %dx
|
||||
popw %cx
|
||||
popl %ebx
|
||||
ret
|
||||
|
||||
|
||||
# search for kernel file from filesystem
|
||||
.global ext2_find_kernel
|
||||
ext2_find_kernel:
|
||||
movl $EXT2_ROOT_INO, %eax
|
||||
call ext2_read_inode
|
||||
|
||||
movw $kernel_path, %di
|
||||
.ext2_find_kernel_loop:
|
||||
movw (%di), %si
|
||||
|
||||
# check if this list is done
|
||||
testw %si, %si
|
||||
jz .ext2_find_kernel_loop_done
|
||||
|
||||
# check that current part is directory
|
||||
movw (ext2_inode_buffer + i_mode), %ax
|
||||
andw $EXT2_S_IMASK, %ax
|
||||
cmpw $EXT2_S_IFDIR, %ax
|
||||
jne .ext2_find_kernel_part_not_dir
|
||||
|
||||
# prepare registers for directory finding
|
||||
movw 0(%si), %cx
|
||||
addw $2, %si
|
||||
|
||||
# print search path
|
||||
pushw %si
|
||||
movw $ext2_looking_for_msg, %si
|
||||
call puts
|
||||
popw %si
|
||||
call puts; call print_newline
|
||||
|
||||
# search current directory for this file
|
||||
call ext2_directory_find_inode
|
||||
testl %eax, %eax
|
||||
jz .ext2_find_kernel_part_not_found
|
||||
|
||||
# loop to next part
|
||||
addw $2, %di
|
||||
jmp .ext2_find_kernel_loop
|
||||
|
||||
.ext2_find_kernel_loop_done:
|
||||
|
||||
# check that kernel is a regular file
|
||||
movw (ext2_inode_buffer + i_mode), %ax
|
||||
andw $EXT2_S_IMASK, %ax
|
||||
cmpw $EXT2_S_IFREG, %ax
|
||||
jne .ext2_find_kernel_not_reg
|
||||
|
||||
movw $ext2_kernel_found_msg, %si
|
||||
call puts; call print_newline
|
||||
|
||||
1: jmp 1b
|
||||
|
||||
ret
|
||||
|
||||
.ext2_find_kernel_part_not_dir:
|
||||
movw $ext2_part_not_dir_msg, %si
|
||||
jmp print_and_halt
|
||||
|
||||
.ext2_find_kernel_part_not_found:
|
||||
movw $ext2_part_not_found_msg, %si
|
||||
jmp print_and_halt
|
||||
|
||||
.ext2_find_kernel_not_reg:
|
||||
movw $ext2_kernel_not_reg_msg, %si
|
||||
jmp print_and_halt
|
||||
|
||||
|
||||
kernel_path:
|
||||
.short kernel_path1
|
||||
.short kernel_path2
|
||||
.short 0
|
||||
kernel_path1:
|
||||
.short 4
|
||||
.asciz "boot"
|
||||
kernel_path2:
|
||||
.short 15
|
||||
.asciz "banan-os.kernel"
|
||||
|
||||
|
||||
root_partition_does_not_fit_ext2_filesystem_msg:
|
||||
.asciz "Root partition is too small to contain ext2 filesystem"
|
||||
root_partition_has_invalid_ext2_magic_msg:
|
||||
.asciz "Root partition doesn't contain ext2 magic number"
|
||||
root_partition_has_unsupported_ext2_block_size_msg:
|
||||
.asciz "Root partition has unsupported ext2 block size (only 1024 supported)"
|
||||
|
||||
ext2_part_not_dir_msg:
|
||||
.asciz "inode in root path is not directory"
|
||||
ext2_part_not_found_msg:
|
||||
.asciz " not found"
|
||||
ext2_kernel_not_reg_msg:
|
||||
.asciz "kernel is not a regular file"
|
||||
ext2_kernel_found_msg:
|
||||
.asciz "kernel found!"
|
||||
|
||||
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_looking_for_msg:
|
||||
.asciz "looking for "
|
||||
|
||||
.section .bss
|
||||
|
||||
ext2_block_buffer:
|
||||
.skip EXT2_BLOCK_SIZE
|
||||
|
||||
ext2_partition_first_sector:
|
||||
.skip 8
|
||||
|
||||
ext2_drive_number:
|
||||
.skip 1
|
||||
.skip 3 # padding
|
||||
|
||||
# NOTE: fits in 2 bytes
|
||||
ext2_inode_size:
|
||||
.skip 4
|
||||
|
||||
ext2_superblock_buffer:
|
||||
.skip EXT2_SUPERBLOCK_SIZE
|
||||
|
||||
ext2_block_group_descriptor_buffer:
|
||||
.skip EXT2_BGD_SIZE
|
||||
|
||||
ext2_inode_buffer:
|
||||
.skip EXT2_INODE_SIZE_MAX
|
Loading…
Reference in New Issue