# 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 movw $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 # return: # dl: drive number # ecx: sector count (capped at 0xFFFFFFFF) # bx:eax: first sector .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) adcl $0, 4(%esp) # 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 # 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 sbbl $0, %ebx # 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 # 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 .section .data # 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