.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