forked from Bananymous/banan-os
				
			
		
			
				
	
	
		
			750 lines
		
	
	
		
			16 KiB
		
	
	
	
		
			ArmAsm
		
	
	
	
			
		
		
	
	
			750 lines
		
	
	
		
			16 KiB
		
	
	
	
		
			ArmAsm
		
	
	
	
| # FIXME: don't assume 512 byte sectors
 | |
| .set SECTOR_SHIFT,	9
 | |
| .set SECTOR_SIZE,	1 << SECTOR_SHIFT
 | |
| 
 | |
| .set EXT2_MAX_BLOCK_SIZE,	4096
 | |
| .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_first_data_block,	20
 | |
| .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
 | |
| 	adcw $0, %bx
 | |
| 
 | |
| 	# 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_MAX_BLOCK_SIZE
 | |
| 	movl $1024, %eax
 | |
| 	shll %cl, %eax
 | |
| 	cmpl $EXT2_MAX_BLOCK_SIZE, %eax
 | |
| 	ja .has_ext2_filesystem_unsupported_block_size
 | |
| 
 | |
| 	# fill block size and shift
 | |
| 	movl %eax, (ext2_block_size)
 | |
| 	addl $10, %ecx
 | |
| 	movl %ecx, (ext2_block_shift)
 | |
| 
 | |
| 	# 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:
 | |
| 	pushal
 | |
| 
 | |
| 	# ecx := sectors_per_block := block_size / sector_size
 | |
| 	movl (ext2_block_size), %ecx
 | |
| 	shrl $SECTOR_SHIFT, %ecx
 | |
| 
 | |
| 	# ebx:eax := block * sectors_per_block + (ext2_partition_first_sector)
 | |
| 	xorl %ebx, %ebx
 | |
| 	mull %ecx
 | |
| 	addl (ext2_partition_first_sector + 0), %eax
 | |
| 	adcl (ext2_partition_first_sector + 4), %ebx
 | |
| 
 | |
| 	movw $ext2_block_buffer, %di
 | |
| 	movb (ext2_drive_number), %dl
 | |
| 	call read_from_disk
 | |
| 
 | |
| 	popal
 | |
| 	ret
 | |
| 
 | |
| 
 | |
| # reads block group descrtiptor into ext2_block_group_descriptor
 | |
| #	eax:	block group
 | |
| ext2_read_block_group_descriptor:
 | |
| 	pushal
 | |
| 
 | |
| 	# ebx := bgd_block_byte_offset := (s_first_data_block + 1) * block_size
 | |
| 	#                              := (s_first_data_block + 1) << ext2_block_shift
 | |
| 	movl (ext2_superblock_buffer + s_first_data_block), %ebx
 | |
| 	incl %ebx
 | |
| 	movb (ext2_block_shift), %cl
 | |
| 	shll %cl, %ebx
 | |
| 
 | |
| 	# eax := bgd_byte_offset := bgd_block_byte_offset + EXT2_BGD_SIZE * block_group;
 | |
| 	#                        := bgd_block_byte_offset + (block_group << EXT2_BGD_SHIFT)
 | |
| 	movb $EXT2_BGD_SHIFT, %cl
 | |
| 	shll %cl, %eax
 | |
| 	addl %ebx, %eax
 | |
| 
 | |
| 	# eax: bgd_block  := bgd_byte_offset / block_size
 | |
| 	# ebx: bgd_offset := bgd_byte_offset % block_size
 | |
| 	xorl %edx, %edx
 | |
| 	divl (ext2_block_size)
 | |
| 	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
 | |
| 	divl (ext2_superblock_buffer + s_inodes_per_group)
 | |
| 	movl %edx, %ebx
 | |
| 
 | |
| 	call ext2_read_block_group_descriptor
 | |
| 
 | |
| 	# eax := inode_table_block  := (inode_index * inode_size) / block_size
 | |
| 	# ebx := inode_table_offset := (inode_index * inode_size) % block_size
 | |
| 	movl %ebx, %eax
 | |
| 	mull (ext2_inode_size)
 | |
| 	divl (ext2_block_size)
 | |
| 	movl %edx, %ebx
 | |
| 
 | |
| 	# eax := filesystem_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
 | |
| 	  # ecx := inode_size
 | |
| 	movl (ext2_inode_size), %ecx
 | |
| 	rep movsb
 | |
| 
 | |
| 	# reset indirect cache to zero
 | |
| 	movl $0, (ext2_inode_indirect_number)
 | |
| 
 | |
|  .ext2_read_inode_done:
 | |
| 	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 %ebx
 | |
| 	pushl %ecx
 | |
| 	pushl %edx
 | |
| 	pushl %esi
 | |
| 	pushl %edi
 | |
| 
 | |
| 	# ebx := max_data_blocks := (file_size + block_size - 1) / block_size
 | |
| 	#                        := (i_size + ext2_block_size - 1) >> ext2_block_shift
 | |
| 	# cl  := ext2_block_shift
 | |
| 	movl (ext2_inode_buffer + i_size), %ebx
 | |
| 	addl (ext2_block_size), %ebx
 | |
| 	decl %ebx
 | |
| 	movb (ext2_block_shift), %cl
 | |
| 	shrl %cl, %ebx
 | |
| 
 | |
| 	# verify data block is within bounds
 | |
| 	cmpl %ebx, %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
 | |
| 	subl $12, %eax
 | |
| 
 | |
| 	# cl  := indices_per_block_shift := ext2_block_shift - 2
 | |
| 	# ebx := comp
 | |
| 	subb $2, %cl
 | |
| 	movl $1, %ebx
 | |
| 	shll %cl, %ebx
 | |
| 
 | |
| 	# check if this is singly indirect block access
 | |
| 	cmpl %ebx, %eax
 | |
| 	jb .ext2_data_block_index_singly_indirect
 | |
| 	subl %ebx, %eax
 | |
| 	shll %cl, %ebx
 | |
| 
 | |
| 	# check if this is doubly indirect block access
 | |
| 	cmpl %ebx, %eax
 | |
| 	jb .ext2_data_block_index_doubly_indirect
 | |
| 	subl %ebx, %eax
 | |
| 	shll %cl, %ebx
 | |
| 
 | |
| 	# check if this is triply indirect block access
 | |
| 	cmpl %ebx, %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), %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:
 | |
| 	# edx := cache index := (index & ~(block_size / 4 - 1)) | depth
 | |
| 	#                    := (index & -(block_size >> 2)) | depth
 | |
| 	movl (ext2_block_size), %edx
 | |
| 	shrl $2, %edx
 | |
| 	negl %edx
 | |
| 	andl %ebx, %edx
 | |
| 	orw %cx, %dx
 | |
| 
 | |
| 	# check whether this block is already cached
 | |
| 	cmpl $0, (ext2_inode_indirect_number)
 | |
| 	je .ext2_data_block_index_indirect_no_cache
 | |
| 	cmpl %edx, (ext2_inode_indirect_number)
 | |
| 	je .ext2_data_block_index_indirect_cached
 | |
| 
 | |
|  .ext2_data_block_index_indirect_no_cache:
 | |
| 	# update cache block number, will be cached when found
 | |
| 	movl %edx, (ext2_inode_indirect_number)
 | |
| 
 | |
| 	# eax := current block
 | |
| 	# ebx := index
 | |
| 	# cx  := depth
 | |
|  .ext2_data_block_index_indirect_loop:
 | |
| 	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), %al
 | |
| 	subb $2, %al
 | |
| 	decb %cl
 | |
| 	mulb %cl
 | |
| 	movb %al, %cl
 | |
| 
 | |
| 	# ebx := ebx >> shift
 | |
| 	shrl %cl, %ebx
 | |
| 
 | |
|  .ext2_data_block_index_no_shift:
 | |
| 	# edx := index of next block (ebx & (block_size / 4 - 1))
 | |
| 	movl (ext2_block_size), %edx
 | |
| 	shrl $2, %edx
 | |
| 	decl %edx
 | |
| 	andl %ebx, %edx
 | |
| 
 | |
| 	# 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_loop
 | |
| 
 | |
| 	# cache last read block
 | |
| 	movw $ext2_block_buffer, %si
 | |
| 	movw $ext2_inode_indirect_buffer, %di
 | |
| 	movw (ext2_block_size), %cx
 | |
| 	rep movsb
 | |
| 
 | |
| 	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_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_indirect_cached:
 | |
| 	movl $ext2_inode_indirect_buffer, %esi
 | |
| 	movl (ext2_block_size), %edx
 | |
| 	shrl $2, %edx
 | |
| 	decl %edx
 | |
| 	andl %edx, %ebx
 | |
| 	movl (%esi, %ebx, 4), %eax
 | |
| 
 | |
|  .ext2_data_block_index_done:
 | |
| 	popl %edi
 | |
| 	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)
 | |
| 
 | |
| 	# eax := first_byte / block_size
 | |
| 	# edx := first_byte % block_size
 | |
| 	# when edx == 0, no partial read needed
 | |
| 	xorl %edx, %edx
 | |
| 	divl (ext2_block_size)
 | |
| 	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
 | |
| 
 | |
| 	call memcpy32
 | |
| 
 | |
| 	# 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
 | |
| 	movb (ext2_block_shift), %cl
 | |
| 	shrl %cl, %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)
 | |
| 
 | |
| 	movl $ext2_block_buffer, %esi
 | |
| 	call memcpy32
 | |
| 
 | |
| 	# 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
 | |
| 
 | |
| 
 | |
| # 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 / block_size)
 | |
| 	movl (ext2_inode_buffer + i_size), %ebx
 | |
| 	addl (ext2_block_size), %ebx
 | |
| 	decl %ebx
 | |
| 	movb (ext2_block_shift), %cl
 | |
| 	shrl %cl, %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
 | |
| 	movw $ext2_block_buffer, %di
 | |
| 	addw (ext2_block_size), %di
 | |
| 	cmpw %di, %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:
 | |
| 	xorb %al, %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
 | |
| # 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
 | |
| 
 | |
| 	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
 | |
| 
 | |
| 	popw %si
 | |
| 	popw %di
 | |
| 	popw %cx
 | |
| 	popl %eax
 | |
| 	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
 | |
| 
 | |
| .section .data
 | |
| 
 | |
| 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 (1 KiB, 2 KiB and 4 KiB are 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_invalid_msg:
 | |
| 	.asciz "data block index is invalid"
 | |
| 
 | |
| ext2_looking_for_msg:
 | |
| 	.asciz "looking for "
 | |
| 
 | |
| .section .bss
 | |
| 
 | |
| .align SECTOR_SIZE
 | |
| ext2_block_buffer:
 | |
| 	.skip EXT2_MAX_BLOCK_SIZE
 | |
| 
 | |
| ext2_inode_indirect_buffer:
 | |
| 	.skip EXT2_MAX_BLOCK_SIZE
 | |
| ext2_inode_indirect_number:
 | |
| 	.skip 4
 | |
| 
 | |
| 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_block_size:
 | |
| 	.skip 4
 | |
| ext2_block_shift:
 | |
| 	.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
 |