root/stand/boot/pc32/mbr/mbr.S
/*
 * Copyright (c) 1999 Robert Nordier
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms are freely
 * permitted provided that the above copyright notice and this
 * paragraph and the following disclaimer are duplicated in all
 * such forms.
 *
 * This software is provided "AS IS" and without any express or
 * implied warranties, including, without limitation, the implied
 * warranties of merchantability and fitness for a particular
 * purpose.
 *
 *
 * $FreeBSD: src/sys/boot/i386/mbr/mbr.s,v 1.6 2000/06/27 20:04:10 jhb Exp $
 * $DragonFly: src/sys/boot/pc32/mbr/mbr.s,v 1.3 2003/11/10 06:08:36 dillon Exp $
 */

/*
 * A 512 byte MBR boot manager that simply boots the active partition.
 */

                .set LOAD,0x7c00                 # Load address
                .set EXEC,0x600                  # Execution address
                .set PT_OFF,0x1be                # Partition table
                .set MAGIC,0xaa55                # Magic: bootable

                .set NHRDRV,0x475                # Number of hard drives

                .globl start                     # Entry point
                .code16

/*
 * Setup the segment registers for flat addressing and setup the stack.
 */
start:          cld                              # String ops inc
                xorw %ax,%ax                     # Zero
                movw %ax,%es                     # Address
                movw %ax,%ds                     #  data
                movw %ax,%ss                     # Set up
                movw $LOAD,%sp                   #  stack
/*
 * Relocate ourself to a lower address so that we are out of the way when
 * we load in the bootstrap from the partition to boot.
 */
                movw $main-EXEC+LOAD,%si         # Source
                movw $main,%di                   # Destination
                movw $0x200-(main-start),%cx     # Byte count
                rep                              # Relocate
                movsb                            #  code
/*
 * Jump to the relocated code.
 */
                jmp main-LOAD+EXEC               # To relocated code
/*
 * Scan the partition table looking for an active entry.  Note that %ch is
 * zero from the repeated string instruction above.  We save the offset of
 * the active partition in %si and scan the entire table to ensure that only
 * one partition is marked active.
 */
main:           xorw %si,%si                     # No active partition
                movw $partbl,%bx                 # Partition table
                movb $0x4,%cl                    # Number of entries
main.1:         cmpb %ch,(%bx)                   # Null entry?
                je main.2                        # Yes
                jg err_pt                        # If 0x1..0x7f
                testw %si,%si                    # Active already found?
                jnz err_pt                       # Yes
                movw %bx,%si                     # Point to active
main.2:         addb $0x10,%bl                   # Till
                loop main.1                      #  done
                testw %si,%si                    # Active found?
                jnz main.3                       # Yes
                int $0x18                        # BIOS: Diskless boot
/*
 * Ok, we've found a possible active partition.  Check to see that the drive
 * is a valid hard drive number.
 */
main.3:         cmpb $0x80,%dl                   # Drive valid?
                jb main.4                        # No
                movb NHRDRV,%dh                  # Calculate the highest
                addb $0x80,%dh                   #  drive number available
                cmpb %dh,%dl                     # Within range?
                jb main.5                        # Yes
main.4:         movb (%si),%dl                   # Load drive
/*
 * Ok, now that we have a valid drive and partition entry, load the CHS from
 * the partition entry and read the sector from the disk.
 */
main.5:         movw %sp,%di                     # Save stack pointer
                movb 0x1(%si),%dh                # Load head
                movw 0x2(%si),%cx                # Load cylinder:sector
                movw $LOAD,%bx                   # Transfer buffer
#ifdef AVOID_PACKET_MODE
                cmpb $0xff,%dh                   # Might we need to use LBA?
                jnz main.7                       # No.
                cmpw $0xffff,%cx                 # Do we need to use LBA?
                jnz main.7                       # No.
#endif
                pushw %cx                        # Save %cx
                pushw %bx                        # Save %bx
                movw $0x55aa,%bx                 # Magic
                movb $0x41,%ah                   # BIOS:        EDD extensions
                int $0x13                        #  present?
                jc main.6                        # No.
                cmpw $0xaa55,%bx                 # Magic ok?
                jne main.6                       # No.
                testb $0x1,%cl                   # Packet mode present?
                jz main.6                        # No.
                popw %bx                         # Restore %bx
                pushl $0x0                       # Set the LBA
                pushl 0x8(%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
                jmp main.8                       # Skip the CHS setup
main.6:         popw %bx                         # Restore %bx
                popw %cx                         # Restore %cx
main.7:         movw $0x201,%ax                  # BIOS: Read from disk
main.8:         int $0x13                        # Call the BIOS
                movw %di,%sp                     # Restore stack
                jc err_rd                        # If error
/*
 * Now that we've loaded the bootstrap, check for the 0xaa55 signature.  If it
 * is present, execute the bootstrap we just loaded.
 */
                cmpw $MAGIC,0x1fe(%bx)           # Bootable?
                jne err_os                       # No
                jmp *%bx                         # Invoke bootstrap
/*
 * Various error message entry points.
 */
err_pt:         movw $msg_pt,%si                 # "Invalid partition
                jmp putstr                       #  table"

err_rd:         movw $msg_rd,%si                 # "Error loading
                jmp putstr                       #  operating system"

err_os:         movw $msg_os,%si                 # "Missing operating
                jmp putstr                       #  system"
/*
 * Output an ASCIZ string to the console via the BIOS.
 */
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
putstr.1:       jmp putstr.1                     # Await reset

msg_pt:         .asciz "Invalid partition table"
msg_rd:         .asciz "Error loading operating system"
msg_os:         .asciz "Missing operating system"

                .org PT_OFF

partbl:         .fill 0x10,0x4,0x0               # Partition table
                .word MAGIC                      # Magic number