Wednesday, June 13, 2012

# # Copyright (c) 2001 John Baldwin # All rights reserved. # # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions # are met: # 1. Redistributions of source code must retain the above copyright # notice, this list of conditions and the following disclaimer. # 2. Redistributions in binary form must reproduce the above copyright # notice, this list of conditions and the following disclaimer in the # documentation and/or other materials provided with the distribution. # 3. Neither the name of the author nor the names of any co-contributors # may be used to endorse or promote products derived from this software # without specific prior written permission. # # THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND # ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE # IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE # ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE # FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL # DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS # OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) # HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT # LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY # OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF # SUCH DAMAGE. # # $FreeBSD: release/9.0.0/sys/boot/i386/cdboot/cdboot.s 219126 2011-03-01 11:47:51Z brucec $ # # a.out header fields # .set AOUT_TEXT,0x04 # text segment size .set AOUT_DATA,0x08 # data segment size .set AOUT_BSS,0x0c # zero'd BSS size .set AOUT_SYMBOLS,0x10 # symbol table .set AOUT_ENTRY,0x14 # entry point .set AOUT_HEADER,MEM_PAGE_SIZE # size of the a.out header # # Constants for reading from the CD. # .set ERROR_TIMEOUT,0x80 # BIOS timeout on read .set NUM_RETRIES,3 # Num times to retry .set SECTOR_SIZE,0x800 # size of a sector .set SECTOR_SHIFT,11 # number of place to shift .set BUFFER_LEN,0x100 # number of sectors in buffer .set MAX_READ,0x10000 # max we can read at a time .set MAX_READ_SEC,MAX_READ >> SECTOR_SHIFT .set MEM_READ_BUFFER,0x9000 # buffer to read from CD .set MEM_VOLDESC,MEM_READ_BUFFER # volume descriptor .set MEM_DIR,MEM_VOLDESC+SECTOR_SIZE # Lookup buffer .set VOLDESC_LBA,0x10 # LBA of vol descriptor .set VD_PRIMARY,1 # Primary VD .set VD_END,255 # VD Terminator .set VD_ROOTDIR,156 # Offset of Root Dir Record .set DIR_LEN,0 # Offset of Dir Record length .set DIR_EA_LEN,1 # Offset of EA length .set DIR_EXTENT,2 # Offset of 64-bit LBA .set DIR_SIZE,10 # Offset of 64-bit length .set DIR_NAMELEN,32 # Offset of 8-bit name len .set DIR_NAME,33 # Offset of dir name # # We expect to be loaded by the BIOS at 0x7c00 (standard boot loader entry # point) # .code16 .globl start .org 0x0, 0x0 # # Program start. # start: cld # string ops inc xor %ax,%ax # zero %ax mov %ax,%ss # setup the mov $start,%sp # stack mov %ax,%ds # setup the mov %ax,%es # data segments mov %dl,drive # Save BIOS boot device # # Load Volume Descriptor # mov $VOLDESC_LBA,%eax # Set LBA of first VD load_vd: push %eax # Save %eax mov $1,%dh # One sector mov $MEM_VOLDESC,%ebx # Destination call read # Read it in cmpb $VD_PRIMARY,(%bx) # Primary VD? je have_vd # Yes pop %eax # Prepare to inc %eax # try next cmpb $VD_END,(%bx) # Last VD? jne load_vd # No, read next have_vd: # Have Primary VD # # Try to look up the loader binary using the paths in the loader_paths # array. # mov $loader_strings,%si # Point to start of array lookup_path: push %si # Save file name pointer call lookup # Try to find file pop %di # Restore file name pointer jnc lookup_found # Found this file xor %al,%al # Look for next mov $0xffff,%cx # path name by repnz # scanning for scasb # nul char mov %di,%si # Point %si at next path mov (%si),%al # Get first char of next path or %al,%al # Is it double nul? jnz lookup_path # No, try it. lookup_found: # Found a loader file # # Load the binary into the buffer. Due to real mode addressing limitations # we have to read it in 64k chunks. # mov DIR_SIZE(%bx),%eax # Read file length add $SECTOR_SIZE-1,%eax # Convert length to sectors shr $SECTOR_SHIFT,%eax cmp $BUFFER_LEN,%eax jbe load_sizeok mov $msg_load2big,%si # Error message call error load_sizeok: movzbw %al,%cx # Num sectors to read mov DIR_EXTENT(%bx),%eax # Load extent xor %edx,%edx mov DIR_EA_LEN(%bx),%dl add %edx,%eax # Skip extended mov $MEM_READ_BUFFER,%ebx # Read into the buffer load_loop: mov %cl,%dh cmp $MAX_READ_SEC,%cl # Truncate to max read size jbe load_notrunc mov $MAX_READ_SEC,%dh load_notrunc: sub %dh,%cl # Update count push %eax # Save call read # Read it in pop %eax # Restore add $MAX_READ_SEC,%eax # Update LBA add $MAX_READ,%ebx # Update dest addr jcxz load_done # Done? jmp load_loop # Keep going load_done: # # Lookup the file in the path at [SI] from the root directory. # lookup: mov $VD_ROOTDIR+MEM_VOLDESC,%bx # Root directory record mov DIR_EXTENT(%bx),rec_lba mov DIR_SIZE(%bx),rec_size mov $rec_size,%eax # Set LBA of root dir push %eax # Save %eax mov $1,%dh mov $MEM_VOLDESC,%ebx # Destination # # Load DH sectors starting at LBA EAX into [EBX]. # # Trashes: EAX # read: push %si # Save push %cx # Save since some BIOSs trash mov %eax,edd_lba # LBA to read from mov %ebx,%eax # Convert address shr $4,%eax # to segment mov %ax,edd_addr+0x2 # and store push %dx # Save mov $edd_packet,%si # Address Packet mov %dh,edd_len # Set length mov drive,%dl # BIOS Device mov $0x42,%ah # BIOS: Extended Read int $0x13 # Call BIOS pop %dx # Restore pop %cx # Restore pop %si ret # Return # # EDD Packet # edd_packet: .byte 0x10 # Length .byte 0 # Reserved edd_len: .byte 0x0 # Num to read .byte 0 # Reserved edd_addr: .word 0x0,0x0 # Seg:Off edd_lba: .quad 0x0 # LBA drive: .byte 0 # # State for searching dir # rec_lba: .long 0x0 # LBA (adjusted for EA) rec_size: .long 0x0 # File size loader_strings: .asciz "LOADER" .asciz "loader" .byte 0