492 lines
9.3 KiB
ArmAsm
492 lines
9.3 KiB
ArmAsm
|
# FIXME: don't assume 512 byte sectors
|
||
|
.set SECTOR_SIZE_SHIFT, 9
|
||
|
.set SECTOR_SIZE, 1 << SECTOR_SIZE_SHIFT
|
||
|
|
||
|
.code16
|
||
|
|
||
|
.section .stage1
|
||
|
|
||
|
.global stage2_start
|
||
|
.global stage2_end
|
||
|
|
||
|
# check that drive has int13 ext
|
||
|
# dl: drive number
|
||
|
# returns only if drive does have the extension
|
||
|
drive_has_int13_ext:
|
||
|
pusha
|
||
|
|
||
|
movb $0x41, %ah
|
||
|
movw $0x55AA, %bx
|
||
|
int $0x13
|
||
|
jc .drive_has_int13_ext_no_int13_ext
|
||
|
|
||
|
popa
|
||
|
ret
|
||
|
|
||
|
.drive_has_int13_ext_no_int13_ext:
|
||
|
mov $no_int13_ext_msg, %si
|
||
|
jmp print_and_halt
|
||
|
|
||
|
|
||
|
# read sectors from disk
|
||
|
# bx:eax: lba start
|
||
|
# cx: lba count (has to less than 0x80)
|
||
|
# dl: drive number
|
||
|
# ds:di: physical address
|
||
|
# returns only on success
|
||
|
.global read_from_disk
|
||
|
read_from_disk:
|
||
|
pusha
|
||
|
|
||
|
call drive_has_int13_ext
|
||
|
|
||
|
# prepare disk read packet
|
||
|
mov $disk_address_packet, %si
|
||
|
movb $0x10, 0x00(%si) # packet size
|
||
|
movb $0x00, 0x01(%si) # always 0
|
||
|
movw %cx, 0x02(%si) # lba count
|
||
|
movw %di, 0x04(%si) # offset
|
||
|
movw %ds, 0x06(%si) # segment
|
||
|
movl %eax, 0x08(%si) # 32 bit lower lba
|
||
|
movw %bx, 0x0C(%si) # 16 bit upper lba
|
||
|
movw $0, 0x0E(%si) # zero
|
||
|
|
||
|
# issue read command
|
||
|
mov $0x42, %ah
|
||
|
int $0x13
|
||
|
jc .read_from_disk_failed
|
||
|
|
||
|
popa
|
||
|
ret
|
||
|
|
||
|
.read_from_disk_failed:
|
||
|
mov $read_from_disk_msg, %si
|
||
|
jmp print_and_halt
|
||
|
|
||
|
|
||
|
# Reads GPT header into gpt_header buffer
|
||
|
# dl: drive number
|
||
|
# return:
|
||
|
# ax: 1 if has GPT header, 0 otherwise
|
||
|
.global read_gpt_header
|
||
|
read_gpt_header:
|
||
|
pushw %bx
|
||
|
pushw %cx
|
||
|
pushw %di
|
||
|
|
||
|
xorw %bx, %bx
|
||
|
movl $1, %eax
|
||
|
movw $1, %cx
|
||
|
movw $gpt_header, %di
|
||
|
call read_from_disk
|
||
|
|
||
|
xorw %bx, %bx
|
||
|
movw $1, %ax
|
||
|
|
||
|
# check if header starts with 'EFI PART'
|
||
|
cmpl $0x20494645, (gpt_header + 0)
|
||
|
cmovnew %bx, %ax
|
||
|
cmpl $0x54524150, (gpt_header + 4)
|
||
|
cmovnew %bx, %ax
|
||
|
|
||
|
popw %di
|
||
|
popw %cx
|
||
|
popw %bx
|
||
|
ret
|
||
|
|
||
|
|
||
|
# Find bios boot partition from boot drive
|
||
|
# returns:
|
||
|
# bx:eax: first lba
|
||
|
# cx: sector count
|
||
|
find_stage2_partition:
|
||
|
# read boot disk GPT header
|
||
|
movb (boot_disk_number), %dl
|
||
|
call read_gpt_header
|
||
|
|
||
|
testb %al, %al
|
||
|
jz .find_stage2_partition_not_gpt
|
||
|
|
||
|
# eax := entry_count
|
||
|
movl (gpt_header + 80), %eax
|
||
|
test %eax, %eax
|
||
|
jz .find_stage2_partition_not_found
|
||
|
|
||
|
# edx:eax := eax * entry_size
|
||
|
mull (gpt_header + 84)
|
||
|
test %edx, %edx
|
||
|
jnz .find_stage2_partition_too_big_entries
|
||
|
|
||
|
# FIXME: read one entry array section at a time
|
||
|
|
||
|
# sector count := (arr_size + SECTOR_SIZE - 1) / SECTOR_SIZE
|
||
|
movl %eax, %ecx
|
||
|
shrl $SECTOR_SIZE_SHIFT, %ecx
|
||
|
|
||
|
# start lba
|
||
|
movl (gpt_header + 72), %eax
|
||
|
movw (gpt_header + 76), %bx
|
||
|
|
||
|
movw $gpt_entry_data, %di
|
||
|
movw $bios_boot_guid, %si
|
||
|
movb (boot_disk_number), %dl
|
||
|
|
||
|
call read_from_disk
|
||
|
|
||
|
# NOTE: 'only' 0xFFFF partitions supported,
|
||
|
# although read will fail with more than 0x80
|
||
|
movw (gpt_header + 80), %cx
|
||
|
|
||
|
.find_stage2_partition_loop_gpt_entries:
|
||
|
pushw %cx
|
||
|
movw $16, %cx
|
||
|
call memcmp
|
||
|
popw %cx
|
||
|
|
||
|
testb %al, %al
|
||
|
jnz .find_stage2_partition_found
|
||
|
|
||
|
# add entry size to entry pointer
|
||
|
addw (gpt_header + 84), %di
|
||
|
|
||
|
loop .find_stage2_partition_loop_gpt_entries
|
||
|
|
||
|
# fall through to not found case
|
||
|
.find_stage2_partition_not_found:
|
||
|
movw $no_bios_boot_partition_msg, %si
|
||
|
jmp print_and_halt
|
||
|
|
||
|
.find_stage2_partition_not_gpt:
|
||
|
movw $not_gpt_partition_msg, %si
|
||
|
jmp print_and_halt
|
||
|
|
||
|
.find_stage2_partition_too_big_entries:
|
||
|
movw $too_gpt_big_entries_msg, %si
|
||
|
jmp print_and_halt
|
||
|
|
||
|
.find_stage2_partition_found:
|
||
|
# first lba
|
||
|
movl 32(%di), %eax
|
||
|
movw 36(%di), %bx
|
||
|
|
||
|
# count := last lba - first lba + 1
|
||
|
movl 40(%di), %ecx
|
||
|
subl %eax, %ecx
|
||
|
incl %ecx
|
||
|
|
||
|
ret
|
||
|
|
||
|
# reads stage2 into memory
|
||
|
# dl: boot drive number
|
||
|
# returns only on success
|
||
|
.global read_stage2_into_memory
|
||
|
read_stage2_into_memory:
|
||
|
movb %dl, (boot_disk_number)
|
||
|
|
||
|
# push stage2 sector count
|
||
|
movl $stage2_end, %eax
|
||
|
subl $stage2_start, %eax
|
||
|
addl $(SECTOR_SIZE - 1), %eax
|
||
|
movl $SECTOR_SIZE, %ecx
|
||
|
xorl %edx, %edx
|
||
|
divl %ecx
|
||
|
pushl %eax
|
||
|
|
||
|
call find_stage2_partition
|
||
|
|
||
|
movb (boot_disk_number), %dl
|
||
|
popl %ecx # FIXME: validate that partition has enough sectors
|
||
|
movw $stage2_start, %di
|
||
|
call read_from_disk
|
||
|
|
||
|
ret
|
||
|
|
||
|
# 21686148-6449-6E6F-744E-656564454649
|
||
|
.align 4
|
||
|
bios_boot_guid:
|
||
|
.long 0x21686148 # little endian
|
||
|
.word 0x6449 # little endian
|
||
|
.word 0x6E6F # little endian
|
||
|
.word 0x4E74 # big endian
|
||
|
.quad 0x494645646565 # big endian
|
||
|
|
||
|
boot_disk_number:
|
||
|
.skip 1
|
||
|
|
||
|
read_from_disk_msg:
|
||
|
.asciz "read error"
|
||
|
|
||
|
no_int13_ext_msg:
|
||
|
.asciz "no INT13 ext"
|
||
|
|
||
|
no_bios_boot_partition_msg:
|
||
|
.asciz "no bios boot"
|
||
|
|
||
|
too_gpt_big_entries_msg:
|
||
|
.asciz "too big GPT array"
|
||
|
|
||
|
not_gpt_partition_msg:
|
||
|
.asciz "not GPT"
|
||
|
|
||
|
|
||
|
.section .stage2
|
||
|
|
||
|
# check if drive exists
|
||
|
# dl: drive number
|
||
|
# return:
|
||
|
# al: 1 if disk is usable, 0 otherwise
|
||
|
drive_exists:
|
||
|
pusha
|
||
|
|
||
|
movb $0x48, %ah
|
||
|
movw $disk_drive_parameters, %si
|
||
|
movw $0x1A, (disk_drive_parameters) # set buffer size
|
||
|
|
||
|
int $0x13
|
||
|
jc .drive_exists_nope
|
||
|
|
||
|
popa
|
||
|
movb $1, %al
|
||
|
ret
|
||
|
|
||
|
.drive_exists_nope:
|
||
|
popa
|
||
|
movb $0, %al
|
||
|
ret
|
||
|
|
||
|
# find root disk and populate root_disk_drive_number field
|
||
|
# NO REGISTERS SAVED
|
||
|
.global find_root_disk
|
||
|
find_root_disk:
|
||
|
movb $0x80, %dl
|
||
|
|
||
|
.find_root_disk_loop:
|
||
|
call drive_exists
|
||
|
testb %al, %al
|
||
|
jz .find_root_disk_not_found
|
||
|
|
||
|
# read GPT header
|
||
|
xorw %bx, %bx
|
||
|
movl $1, %eax
|
||
|
movw $1, %cx
|
||
|
movw $gpt_header, %di
|
||
|
call read_from_disk
|
||
|
|
||
|
# confirm header (starts with 'EFI PART')
|
||
|
cmpl $0x20494645, (gpt_header + 0)
|
||
|
jne .find_root_disk_next_disk
|
||
|
cmpl $0x54524150, (gpt_header + 4)
|
||
|
jne .find_root_disk_next_disk
|
||
|
|
||
|
# compare disk GUID
|
||
|
movw $root_disk_guid, %si
|
||
|
movw $(gpt_header + 56), %di
|
||
|
movw $16, %cx
|
||
|
call memcmp
|
||
|
testb %al, %al
|
||
|
jz .find_root_disk_next_disk
|
||
|
|
||
|
movw $root_disk_found_msg, %si
|
||
|
call puts; call print_newline
|
||
|
|
||
|
movb %dl, (root_disk_drive_number)
|
||
|
ret
|
||
|
|
||
|
.find_root_disk_next_disk:
|
||
|
incb %dl
|
||
|
jmp .find_root_disk_loop
|
||
|
|
||
|
.find_root_disk_not_found:
|
||
|
movw $root_disk_not_found_msg, %si
|
||
|
jmp print_and_halt
|
||
|
|
||
|
|
||
|
# finds root partition from root disk
|
||
|
# fills root_partition_entry data structure
|
||
|
# NOTE: assumes GPT header is in `gpt_header`
|
||
|
# NO REGISTERS SAVED
|
||
|
.global find_root_partition
|
||
|
find_root_partition:
|
||
|
pushl %ebp
|
||
|
movl %esp, %ebp
|
||
|
subl $16, %esp
|
||
|
|
||
|
# esp + 0: 8 byte entry array lba
|
||
|
movl (gpt_header + 72), %eax
|
||
|
movl %eax, 0(%esp)
|
||
|
movl (gpt_header + 76), %eax
|
||
|
movl %eax, 4(%esp)
|
||
|
# FIXME: check that bits 48-63 are zero
|
||
|
|
||
|
# esp + 8: 4 byte entries per sector
|
||
|
xorl %edx, %edx
|
||
|
movl $SECTOR_SIZE, %eax
|
||
|
divl (gpt_header + 84)
|
||
|
movl %eax, 8(%esp)
|
||
|
|
||
|
# esp + 12: 4 byte entries remaining
|
||
|
movl (gpt_header + 80), %eax
|
||
|
testl %eax, %eax
|
||
|
jz .find_root_partition_not_found
|
||
|
movl %eax, 12(%esp)
|
||
|
|
||
|
.find_root_partition_read_entry_section:
|
||
|
movl 0(%esp), %eax
|
||
|
movl 4(%esp), %ebx
|
||
|
movw $1, %cx
|
||
|
movb (root_disk_drive_number), %dl
|
||
|
movw $sector_buffer, %di
|
||
|
call read_from_disk
|
||
|
|
||
|
# ecx: min(entries per section, entries remaining)
|
||
|
movl 8(%esp), %ecx
|
||
|
cmpl 12(%esp), %ecx
|
||
|
jae .find_root_partition_got_entry_count
|
||
|
movl 12(%esp), %ecx
|
||
|
|
||
|
.find_root_partition_got_entry_count:
|
||
|
# update entries remaining
|
||
|
subl %ecx, 12(%esp)
|
||
|
|
||
|
# si: entry pointer
|
||
|
movw $sector_buffer, %si
|
||
|
|
||
|
.find_root_partition_loop_entries:
|
||
|
# temporarily save cx in dx
|
||
|
movw %cx, %dx
|
||
|
|
||
|
# check that entry is used
|
||
|
movw $16, %cx
|
||
|
movw $zero_guid, %di
|
||
|
call memcmp
|
||
|
test %al, %al
|
||
|
jnz .find_root_partition_next_entry
|
||
|
|
||
|
# compare entry guid to root guid
|
||
|
movw $16, %cx
|
||
|
addw $16, %si
|
||
|
movw $root_partition_guid, %di
|
||
|
call memcmp
|
||
|
subw $16, %si
|
||
|
|
||
|
testb %al, %al
|
||
|
jnz .find_root_partition_found
|
||
|
|
||
|
.find_root_partition_next_entry:
|
||
|
|
||
|
# restore cx
|
||
|
movw %dx, %cx
|
||
|
|
||
|
# entry pointer += entry size
|
||
|
addw (gpt_header + 84), %si
|
||
|
loop .find_root_partition_loop_entries
|
||
|
|
||
|
# entry not found in this sector
|
||
|
|
||
|
# increment 8 byte entry array lba
|
||
|
incl 0(%esp)
|
||
|
jno .find_root_partition_no_overflow
|
||
|
incl 4(%esp)
|
||
|
|
||
|
.find_root_partition_no_overflow:
|
||
|
# loop to read next section if entries remaining
|
||
|
cmpl $0, 12(%esp)
|
||
|
jnz .find_root_partition_read_entry_section
|
||
|
|
||
|
.find_root_partition_not_found:
|
||
|
movw $root_partition_not_found_msg, %si
|
||
|
jmp print_and_halt
|
||
|
|
||
|
.find_root_partition_found:
|
||
|
# copy entry to buffer
|
||
|
movw $root_partition_entry, %di
|
||
|
movw $128, %cx
|
||
|
rep movsb
|
||
|
|
||
|
movw $root_partition_found_msg, %si
|
||
|
call puts; call print_newline
|
||
|
|
||
|
leavel
|
||
|
ret
|
||
|
|
||
|
|
||
|
# print information about root partition
|
||
|
.global print_root_partition_info
|
||
|
print_root_partition_info:
|
||
|
pushw %ax
|
||
|
pushw %bx
|
||
|
pushw %cx
|
||
|
pushw %si
|
||
|
|
||
|
movw $root_partition_info_start_msg, %si
|
||
|
call puts;
|
||
|
|
||
|
movw $16, %bx
|
||
|
movw $2, %cx
|
||
|
movw (root_partition_entry + 38), %ax; call print_number
|
||
|
movw (root_partition_entry + 36), %ax; call print_number
|
||
|
movw (root_partition_entry + 34), %ax; call print_number
|
||
|
movw (root_partition_entry + 32), %ax; call print_number
|
||
|
|
||
|
movb $'-', %al; call putc
|
||
|
movb $'>', %al; call putc
|
||
|
|
||
|
movw (root_partition_entry + 46), %ax; call print_number
|
||
|
movw (root_partition_entry + 44), %ax; call print_number
|
||
|
movw (root_partition_entry + 42), %ax; call print_number
|
||
|
movw (root_partition_entry + 40), %ax; call print_number
|
||
|
|
||
|
call print_newline
|
||
|
|
||
|
popw %si
|
||
|
popw %cx
|
||
|
popw %bx
|
||
|
popw %ax
|
||
|
ret
|
||
|
|
||
|
|
||
|
# These will be patched during bootloader installation
|
||
|
root_disk_guid:
|
||
|
.ascii "root disk guid "
|
||
|
root_partition_guid:
|
||
|
.ascii "root part guid "
|
||
|
zero_guid:
|
||
|
.skip 16, 0
|
||
|
|
||
|
root_disk_found_msg:
|
||
|
.asciz "Root disk found!"
|
||
|
root_disk_not_found_msg:
|
||
|
.asciz "Root disk not found"
|
||
|
|
||
|
root_partition_found_msg:
|
||
|
.asciz "Root partition found!"
|
||
|
root_partition_not_found_msg:
|
||
|
.asciz "Root partition not found"
|
||
|
|
||
|
root_partition_info_start_msg:
|
||
|
.asciz "Root partition: "
|
||
|
|
||
|
.section .bss
|
||
|
|
||
|
.align SECTOR_SIZE
|
||
|
gpt_header:
|
||
|
.skip SECTOR_SIZE
|
||
|
gpt_entry_data:
|
||
|
.skip SECTOR_SIZE
|
||
|
sector_buffer:
|
||
|
.skip SECTOR_SIZE
|
||
|
|
||
|
disk_address_packet:
|
||
|
.skip 16
|
||
|
|
||
|
disk_drive_parameters:
|
||
|
.skip 0x1A
|
||
|
.skip 2 # padding
|
||
|
|
||
|
root_disk_drive_number:
|
||
|
.skip 1
|
||
|
.skip 3 # padding
|
||
|
|
||
|
root_partition_entry:
|
||
|
.skip 128
|