#-
#
# 1. Redistributions of source code must retain the above copyright
# 2. Redistributions in binary form must reproduce the above copyright
# 3. Neither the name of the author nor the names of any co-contributors
#
#
#
.set LOAD,0x7c00 # Load address
.set EXEC,0x600 # Execution address
.set MAGIC,0xaa55 # Magic: bootable
.set SECSIZE,0x200 # Size of a single disk sector
.set DISKSIG,440 # Disk signature offset
.set STACK,EXEC+SECSIZE*4 # Stack address
.set GPT_ADDR,STACK # GPT header address
.set GPT_SIG,0
.set GPT_SIG_0,0x20494645 # "EFI "
.set GPT_SIG_1,0x54524150 # "PART"
.set GPT_MYLBA,24
.set GPT_PART_LBA,72
.set GPT_NPART,80
.set GPT_PART_SIZE,84
.set PART_ADDR,GPT_ADDR+SECSIZE # GPT partition array address
.set PART_TYPE,0
.set PART_START_LBA,32
.set PART_END_LBA,40
.set DPBUF,PART_ADDR+SECSIZE
.set DPBUF_SEC,0x10 # Number of sectors
.set NHRDRV,0x475 # Number of hard drives
.globl start # Entry point
.code16
#
#
start: cld # String ops inc
xorw %ax,%ax # Zero
movw %ax,%es # Address
movw %ax,%ds # data
movw %ax,%ss # Set up
movw $STACK,%sp # stack
#
#
movw $main-EXEC+LOAD,%si # Source
movw $main,%di # Destination
movw $SECSIZE-(main-start),%cx # Byte count
rep # Relocate
movsb # code
#
#
jmp main-LOAD+EXEC # To relocated code
#
#
main: cmpb $0x80,%dl # Drive valid?
jb main.1 # No
movb NHRDRV,%dh # Calculate the highest
addb $0x80,%dh # drive number available
cmpb %dh,%dl # Within range?
jb main.2 # Yes
main.1: movb $0x80,%dl # Assume drive 0x80
#
#
main.2: call getdrvparams # Read drive parameters
movb $1,%dh # %dh := 1 (reading primary)
main.2a: movw $GPT_ADDR,%bx
movw $lba,%si
call read # Read header and check GPT sig
cmpl $GPT_SIG_0,GPT_ADDR+GPT_SIG
jnz main.2b
cmpl $GPT_SIG_1,GPT_ADDR+GPT_SIG+4
jnz main.2b
jmp load_part
main.2b: cmpb $1,%dh # Reading primary?
jne err_pt # If no - invalid table found
#
#
main.3: movb $0,%dh # %dh := 0 (reading backup)
movw $DPBUF+DPBUF_SEC,%si # %si = last sector + 1
movw $lba,%di # %di = $lba
main.3a: subl $1, (%si) # 0x0(%si) = last sec (0-31)
sbbl $0, 4(%si)
movw $4,%cx
rep
movsw # $lastsec--, copy it to $lba
jmp main.2a # Read the next sector
#
#
load_part: movw $GPT_ADDR+GPT_PART_LBA,%si
movw $PART_ADDR,%bx
call read
scan: movw %bx,%si # Compare partition UUID
movw $boot_uuid,%di # with FreeBSD boot UUID
movw $0x10,%cx
repe cmpsb
jnz next_part # Didn't match, next partition
#
#
movw %bx,%di # Save partition pointer in %di
leaw PART_START_LBA(%di),%si
movw $LOAD/16,%bx
movw %bx,%es
xorw %bx,%bx
load_boot: push %si # Save %si
call read
pop %si # Restore
movl PART_END_LBA(%di),%eax # See if this was the last LBA
cmpl (%si),%eax
jnz next_boot
movl PART_END_LBA+4(%di),%eax
cmpl 4(%si),%eax
jnz next_boot
mov %bx,%es # Reset %es to zero
jmp LOAD # Jump to boot code
next_boot: addl $1,(%si) # Next LBA
adcl $0,4(%si)
mov %es,%ax # Adjust segment for next
addw $SECSIZE/16,%ax # sector
cmp $0x9000,%ax # Don't load past 0x90000,
jb sz_ok # 545k should be enough for
call err_big # any boot code, but warn
mov $0x9000-SECSIZE/16,%ax # and truncate
sz_ok: mov %ax,%es
jmp load_boot
#
#
#
next_part: decl GPT_ADDR+GPT_NPART # Was this the last partition?
jz err_noboot
movw GPT_ADDR+GPT_PART_SIZE,%ax
addw %ax,%bx # Next partition
cmpw $PART_ADDR+0x200,%bx # Still in sector?
jb scan
addl $1, GPT_ADDR+GPT_PART_LBA # Next sector
adcl $0,GPT_ADDR+GPT_PART_LBA+4
jmp load_part
#
#
read: pushl 0x4(%si) # Set the LBA
pushl 0x0(%si) # address
pushw %es # Set the address of
pushw %bx # the transfer buffer
pushw $0x1 # Read 1 sector
pushw $0x10 # Packet length
movw %sp,%si # Packer pointer
movw $0x4200,%ax # BIOS: LBA Read from disk
int $0x13 # Call the BIOS
add $0x10,%sp # Restore stack
jc err_rd # If error
ret
#
#
getdrvparams:
movw $DPBUF,%si # Set the address of result buf
movw $0x001e,(%si) # len
movw $0x4800,%ax # BIOS: Read Drive Parameters
int $0x13 # Call the BIOS
jc err_rd # "I/O error" if error
ret
#
#
err_big: movw $msg_big,%si # "Truncated
call putstr # to 545k"
ret
err_pt: movw $msg_pt,%si # "Invalid partition
call putstr # table"
err_pt.1: jmp err_pt.1 # Await reset
err_rd: movw $msg_rd,%si # "I/O error loading
call putstr # boot loader"
jmp err_pt.1
err_noboot: movw $msg_noboot,%si # "Missing boot
call putstr # loader"
jmp err_pt.1
#
#
putstr.0: movw $0x7,%bx # Page:attribute
movb $0xe,%ah # BIOS: Display
int $0x10 # character
putstr: lodsb # Get character
testb %al,%al # End of string?
jnz putstr.0 # No
ret
msg_big: .asciz "Loaded only 545k"
msg_pt: .asciz "Invalid partition table"
msg_rd: .asciz "I/O error loading boot loader"
msg_noboot: .asciz "Missing boot loader"
lba: .quad 1 # LBA of GPT header
boot_uuid: .long 0x83bd6b9d
.word 0x7f41
.word 0x11dc
.byte 0xbe
.byte 0x0b
.byte 0x00
.byte 0x15
.byte 0x60
.byte 0xb8
.byte 0x4f
.byte 0x0f
.org DISKSIG,0x90
sig: .long 0 # OS Disk Signature
.word 0 # "Unknown" in PMBR
partbl: .fill 0x10,0x4,0x0 # Partition table
.word MAGIC # Magic number