156693Sjhb# 2130943Sjhb# Copyright (c) 2001 John Baldwin <jhb@FreeBSD.org> 356693Sjhb# All rights reserved. 456693Sjhb# 5130943Sjhb# Redistribution and use in source and binary forms, with or without 6130943Sjhb# modification, are permitted provided that the following conditions 7130943Sjhb# are met: 8130943Sjhb# 1. Redistributions of source code must retain the above copyright 9130943Sjhb# notice, this list of conditions and the following disclaimer. 10130943Sjhb# 2. Redistributions in binary form must reproduce the above copyright 11130943Sjhb# notice, this list of conditions and the following disclaimer in the 12130943Sjhb# documentation and/or other materials provided with the distribution. 1356693Sjhb# 14130943Sjhb# THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 15130943Sjhb# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 16130943Sjhb# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 17130943Sjhb# ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 18130943Sjhb# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 19130943Sjhb# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 20130943Sjhb# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 21130943Sjhb# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 22130943Sjhb# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 23130943Sjhb# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 24130943Sjhb# SUCH DAMAGE. 2556693Sjhb# 2656693Sjhb 2756693Sjhb# $FreeBSD$ 2856693Sjhb 2956693Sjhb# 3085998Sjhb# This program is a freestanding boot program to load an a.out binary 3185998Sjhb# from a CD-ROM booted with no emulation mode as described by the El 3285998Sjhb# Torito standard. Due to broken BIOSen that do not load the desired 3385998Sjhb# number of sectors, we try to fit this in as small a space as possible. 3456693Sjhb# 3585998Sjhb# Basically, we first create a set of boot arguments to pass to the loaded 3685998Sjhb# binary. Then we attempt to load /boot/loader from the CD we were booted 3785998Sjhb# off of. 3885998Sjhb# 3956693Sjhb 40235219Savg#include <bootargs.h> 41235219Savg 4256693Sjhb# 4356693Sjhb# Memory locations. 4456693Sjhb# 4558713Sjhb .set MEM_PAGE_SIZE,0x1000 # memory page size, 4k 4656693Sjhb .set MEM_ARG,0x900 # Arguments at start 4756693Sjhb .set MEM_ARG_BTX,0xa100 # Where we move them to so the 4856693Sjhb # BTX client can see them 4956693Sjhb .set MEM_ARG_SIZE,0x18 # Size of the arguments 5056693Sjhb .set MEM_BTX_ADDRESS,0x9000 # where BTX lives 5156693Sjhb .set MEM_BTX_ENTRY,0x9010 # where BTX starts to execute 5258713Sjhb .set MEM_BTX_OFFSET,MEM_PAGE_SIZE # offset of BTX in the loader 5356693Sjhb .set MEM_BTX_CLIENT,0xa000 # where BTX clients live 5456693Sjhb# 5558713Sjhb# a.out header fields 5658713Sjhb# 5758713Sjhb .set AOUT_TEXT,0x04 # text segment size 5858713Sjhb .set AOUT_DATA,0x08 # data segment size 5958713Sjhb .set AOUT_BSS,0x0c # zero'd BSS size 6058713Sjhb .set AOUT_SYMBOLS,0x10 # symbol table 6158713Sjhb .set AOUT_ENTRY,0x14 # entry point 6258713Sjhb .set AOUT_HEADER,MEM_PAGE_SIZE # size of the a.out header 6358713Sjhb# 6456693Sjhb# Segment selectors. 6556693Sjhb# 6656693Sjhb .set SEL_SDATA,0x8 # Supervisor data 6756693Sjhb .set SEL_RDATA,0x10 # Real mode data 6856693Sjhb .set SEL_SCODE,0x18 # PM-32 code 6956693Sjhb .set SEL_SCODE16,0x20 # PM-16 code 7056693Sjhb# 7156693Sjhb# BTX constants 7256693Sjhb# 7356693Sjhb .set INT_SYS,0x30 # BTX syscall interrupt 7456693Sjhb# 7585998Sjhb# Constants for reading from the CD. 7685998Sjhb# 7785998Sjhb .set ERROR_TIMEOUT,0x80 # BIOS timeout on read 7885998Sjhb .set NUM_RETRIES,3 # Num times to retry 7985998Sjhb .set SECTOR_SIZE,0x800 # size of a sector 8085998Sjhb .set SECTOR_SHIFT,11 # number of place to shift 8185998Sjhb .set BUFFER_LEN,0x100 # number of sectors in buffer 8285998Sjhb .set MAX_READ,0x10000 # max we can read at a time 8385998Sjhb .set MAX_READ_SEC,MAX_READ >> SECTOR_SHIFT 8485998Sjhb .set MEM_READ_BUFFER,0x9000 # buffer to read from CD 8585998Sjhb .set MEM_VOLDESC,MEM_READ_BUFFER # volume descriptor 8685998Sjhb .set MEM_DIR,MEM_VOLDESC+SECTOR_SIZE # Lookup buffer 8785998Sjhb .set VOLDESC_LBA,0x10 # LBA of vol descriptor 8885998Sjhb .set VD_PRIMARY,1 # Primary VD 8985998Sjhb .set VD_END,255 # VD Terminator 9085998Sjhb .set VD_ROOTDIR,156 # Offset of Root Dir Record 9185998Sjhb .set DIR_LEN,0 # Offset of Dir Record length 9285998Sjhb .set DIR_EA_LEN,1 # Offset of EA length 9385998Sjhb .set DIR_EXTENT,2 # Offset of 64-bit LBA 9485998Sjhb .set DIR_SIZE,10 # Offset of 64-bit length 9585998Sjhb .set DIR_NAMELEN,32 # Offset of 8-bit name len 9685998Sjhb .set DIR_NAME,33 # Offset of dir name 9785998Sjhb# 9858713Sjhb# We expect to be loaded by the BIOS at 0x7c00 (standard boot loader entry 9958713Sjhb# point) 10056693Sjhb# 10156693Sjhb .code16 10256693Sjhb .globl start 10356693Sjhb .org 0x0, 0x0 10456693Sjhb# 10585998Sjhb# Program start. 10656693Sjhb# 10758713Sjhbstart: cld # string ops inc 10885998Sjhb xor %ax,%ax # zero %ax 10985998Sjhb mov %ax,%ss # setup the 11085998Sjhb mov $start,%sp # stack 11185998Sjhb mov %ax,%ds # setup the 11285998Sjhb mov %ax,%es # data segments 11385998Sjhb mov %dl,drive # Save BIOS boot device 11485998Sjhb mov $msg_welcome,%si # %ds:(%si) -> welcome message 11585998Sjhb call putstr # display the welcome message 11656693Sjhb# 11756693Sjhb# Setup the arguments that the loader is expecting from boot[12] 11856693Sjhb# 11985998Sjhb mov $msg_bootinfo,%si # %ds:(%si) -> boot args message 12085998Sjhb call putstr # display the message 12185998Sjhb mov $MEM_ARG,%bx # %ds:(%bx) -> boot args 12285998Sjhb mov %bx,%di # %es:(%di) -> boot args 12385998Sjhb xor %eax,%eax # zero %eax 12485998Sjhb mov $(MEM_ARG_SIZE/4),%cx # Size of arguments in 32-bit 12556693Sjhb # dwords 12656693Sjhb rep # Clear the arguments 12756693Sjhb stosl # to zero 12885998Sjhb mov drive,%dl # Store BIOS boot device 12985998Sjhb mov %dl,0x4(%bx) # in kargs->bootdev 13085998Sjhb or $KARGS_FLAGS_CD,0x8(%bx) # kargs->bootflags |= 13158713Sjhb # KARGS_FLAGS_CD 13256693Sjhb# 13385998Sjhb# Load Volume Descriptor 13485998Sjhb# 13585998Sjhb mov $VOLDESC_LBA,%eax # Set LBA of first VD 13685998Sjhbload_vd: push %eax # Save %eax 13785998Sjhb mov $1,%dh # One sector 13885998Sjhb mov $MEM_VOLDESC,%ebx # Destination 13985998Sjhb call read # Read it in 14085998Sjhb cmpb $VD_PRIMARY,(%bx) # Primary VD? 14185998Sjhb je have_vd # Yes 14285998Sjhb pop %eax # Prepare to 14385998Sjhb inc %eax # try next 14485998Sjhb cmpb $VD_END,(%bx) # Last VD? 14585998Sjhb jne load_vd # No, read next 14685998Sjhb mov $msg_novd,%si # No VD 14785998Sjhb jmp error # Halt 14886164Sjhbhave_vd: # Have Primary VD 14985998Sjhb# 150123476Sjhb# Try to look up the loader binary using the paths in the loader_paths 151123476Sjhb# array. 15285998Sjhb# 153123476Sjhb mov $loader_paths,%si # Point to start of array 154123476Sjhblookup_path: push %si # Save file name pointer 155123476Sjhb call lookup # Try to find file 156123476Sjhb pop %di # Restore file name pointer 157123476Sjhb jnc lookup_found # Found this file 158123476Sjhb xor %al,%al # Look for next 159123476Sjhb mov $0xffff,%cx # path name by 160123476Sjhb repnz # scanning for 161123476Sjhb scasb # nul char 162123476Sjhb mov %di,%si # Point %si at next path 163123476Sjhb mov (%si),%al # Get first char of next path 164123476Sjhb or %al,%al # Is it double nul? 165123476Sjhb jnz lookup_path # No, try it. 166123476Sjhb mov $msg_failed,%si # Failed message 167123476Sjhb jmp error # Halt 168123476Sjhblookup_found: # Found a loader file 16985998Sjhb# 17085998Sjhb# Load the binary into the buffer. Due to real mode addressing limitations 171219126Sbrucec# we have to read it in 64k chunks. 17285998Sjhb# 17385998Sjhb mov DIR_SIZE(%bx),%eax # Read file length 17485998Sjhb add $SECTOR_SIZE-1,%eax # Convert length to sectors 175124445Sjhb shr $SECTOR_SHIFT,%eax 17685998Sjhb cmp $BUFFER_LEN,%eax 17785998Sjhb jbe load_sizeok 17885998Sjhb mov $msg_load2big,%si # Error message 17985998Sjhb call error 18085998Sjhbload_sizeok: movzbw %al,%cx # Num sectors to read 18185998Sjhb mov DIR_EXTENT(%bx),%eax # Load extent 18285998Sjhb xor %edx,%edx 18385998Sjhb mov DIR_EA_LEN(%bx),%dl 18485998Sjhb add %edx,%eax # Skip extended 18585998Sjhb mov $MEM_READ_BUFFER,%ebx # Read into the buffer 18685998Sjhbload_loop: mov %cl,%dh 18785998Sjhb cmp $MAX_READ_SEC,%cl # Truncate to max read size 18885998Sjhb jbe load_notrunc 18985998Sjhb mov $MAX_READ_SEC,%dh 19085998Sjhbload_notrunc: sub %dh,%cl # Update count 19185998Sjhb push %eax # Save 19285998Sjhb call read # Read it in 19385998Sjhb pop %eax # Restore 19485998Sjhb add $MAX_READ_SEC,%eax # Update LBA 19585998Sjhb add $MAX_READ,%ebx # Update dest addr 19685998Sjhb jcxz load_done # Done? 19785998Sjhb jmp load_loop # Keep going 19885998Sjhbload_done: 19985998Sjhb# 20056693Sjhb# Turn on the A20 address line 20156693Sjhb# 20285998Sjhb call seta20 # Turn A20 on 20356693Sjhb# 20456693Sjhb# Relocate the loader and BTX using a very lazy protected mode 20556693Sjhb# 20685998Sjhb mov $msg_relocate,%si # Display the 20785998Sjhb call putstr # relocation message 20885998Sjhb mov MEM_READ_BUFFER+AOUT_ENTRY,%edi # %edi is the destination 20985998Sjhb mov $(MEM_READ_BUFFER+AOUT_HEADER),%esi # %esi is 21058713Sjhb # the start of the text 21158713Sjhb # segment 21285998Sjhb mov MEM_READ_BUFFER+AOUT_TEXT,%ecx # %ecx = length of the text 21358713Sjhb # segment 21485998Sjhb push %edi # Save entry point for later 21556693Sjhb lgdt gdtdesc # setup our own gdt 21656693Sjhb cli # turn off interrupts 21785998Sjhb mov %cr0,%eax # Turn on 21885998Sjhb or $0x1,%al # protected 21985998Sjhb mov %eax,%cr0 # mode 22060821Sjhb ljmp $SEL_SCODE,$pm_start # long jump to clear the 22160821Sjhb # instruction pre-fetch queue 22256693Sjhb .code32 22385998Sjhbpm_start: mov $SEL_SDATA,%ax # Initialize 22485998Sjhb mov %ax,%ds # %ds and 22585998Sjhb mov %ax,%es # %es to a flat selector 22658713Sjhb rep # Relocate the 22758713Sjhb movsb # text segment 22885998Sjhb add $(MEM_PAGE_SIZE - 1),%edi # pad %edi out to a new page 22985998Sjhb and $~(MEM_PAGE_SIZE - 1),%edi # for the data segment 23085998Sjhb mov MEM_READ_BUFFER+AOUT_DATA,%ecx # size of the data segment 23158713Sjhb rep # Relocate the 23258713Sjhb movsb # data segment 23385998Sjhb mov MEM_READ_BUFFER+AOUT_BSS,%ecx # size of the bss 23485998Sjhb xor %eax,%eax # zero %eax 23585998Sjhb add $3,%cl # round %ecx up to 23685998Sjhb shr $2,%ecx # a multiple of 4 23758713Sjhb rep # zero the 23858713Sjhb stosl # bss 23985998Sjhb mov MEM_READ_BUFFER+AOUT_ENTRY,%esi # %esi -> relocated loader 24085998Sjhb add $MEM_BTX_OFFSET,%esi # %esi -> BTX in the loader 24185998Sjhb mov $MEM_BTX_ADDRESS,%edi # %edi -> where BTX needs to go 24285998Sjhb movzwl 0xa(%esi),%ecx # %ecx -> length of BTX 24356693Sjhb rep # Relocate 24456693Sjhb movsb # BTX 24558713Sjhb ljmp $SEL_SCODE16,$pm_16 # Jump to 16-bit PM 24656693Sjhb .code16 24785998Sjhbpm_16: mov $SEL_RDATA,%ax # Initialize 24885998Sjhb mov %ax,%ds # %ds and 24985998Sjhb mov %ax,%es # %es to a real mode selector 25085998Sjhb mov %cr0,%eax # Turn off 25185998Sjhb and $~0x1,%al # protected 25285998Sjhb mov %eax,%cr0 # mode 25360821Sjhb ljmp $0,$pm_end # Long jump to clear the 25460821Sjhb # instruction pre-fetch queue 25556693Sjhbpm_end: sti # Turn interrupts back on now 25656693Sjhb# 25756693Sjhb# Copy the BTX client to MEM_BTX_CLIENT 25856693Sjhb# 25985998Sjhb xor %ax,%ax # zero %ax and set 26085998Sjhb mov %ax,%ds # %ds and %es 26185998Sjhb mov %ax,%es # to segment 0 26285998Sjhb mov $MEM_BTX_CLIENT,%di # Prepare to relocate 26385998Sjhb mov $btx_client,%si # the simple btx client 26485998Sjhb mov $(btx_client_end-btx_client),%cx # length of btx client 26556693Sjhb rep # Relocate the 26656693Sjhb movsb # simple BTX client 26756693Sjhb# 26856693Sjhb# Copy the boot[12] args to where the BTX client can see them 26956693Sjhb# 27085998Sjhb mov $MEM_ARG,%si # where the args are at now 27185998Sjhb mov $MEM_ARG_BTX,%di # where the args are moving to 27285998Sjhb mov $(MEM_ARG_SIZE/4),%cx # size of the arguments in longs 27356693Sjhb rep # Relocate 27456693Sjhb movsl # the words 27556693Sjhb# 27658713Sjhb# Save the entry point so the client can get to it later on 27758713Sjhb# 27885998Sjhb pop %eax # Restore saved entry point 27985998Sjhb stosl # and add it to the end of 28085998Sjhb # the arguments 28158713Sjhb# 28256693Sjhb# Now we just start up BTX and let it do the rest 28356693Sjhb# 28485998Sjhb mov $msg_jump,%si # Display the 28585998Sjhb call putstr # jump message 28660821Sjhb ljmp $0,$MEM_BTX_ENTRY # Jump to the BTX entry point 28756693Sjhb 28856693Sjhb# 28985998Sjhb# Lookup the file in the path at [SI] from the root directory. 29056693Sjhb# 29185998Sjhb# Trashes: All but BX 292123476Sjhb# Returns: CF = 0 (success), BX = pointer to record 293123476Sjhb# CF = 1 (not found) 29485998Sjhb# 29585998Sjhblookup: mov $VD_ROOTDIR+MEM_VOLDESC,%bx # Root directory record 29685998Sjhb push %si 29785998Sjhb mov $msg_lookup,%si # Display lookup message 29885998Sjhb call putstr 29985998Sjhb pop %si 30085998Sjhb push %si 30185998Sjhb call putstr 30285998Sjhb mov $msg_lookup2,%si 30385998Sjhb call putstr 30485998Sjhb pop %si 30585998Sjhblookup_dir: lodsb # Get first char of path 30685998Sjhb cmp $0,%al # Are we done? 30785998Sjhb je lookup_done # Yes 30885998Sjhb cmp $'/',%al # Skip path separator. 30985998Sjhb je lookup_dir 31085998Sjhb dec %si # Undo lodsb side effect 31185998Sjhb call find_file # Lookup first path item 31285998Sjhb jnc lookup_dir # Try next component 313123476Sjhb mov $msg_lookupfail,%si # Not found message 314123476Sjhb call putstr 315123476Sjhb stc # Set carry 316123476Sjhb ret 31785998Sjhb jmp error 31885998Sjhblookup_done: mov $msg_lookupok,%si # Success message 31985998Sjhb call putstr 320123476Sjhb clc # Clear carry 32185998Sjhb ret 32285998Sjhb 32385998Sjhb# 32485998Sjhb# Lookup file at [SI] in directory whose record is at [BX]. 32585998Sjhb# 32685998Sjhb# Trashes: All but returns 327123476Sjhb# Returns: CF = 0 (success), BX = pointer to record, SI = next path item 32885998Sjhb# CF = 1 (not found), SI = preserved 32985998Sjhb# 33086001Sjhbfind_file: mov DIR_EXTENT(%bx),%eax # Load extent 33185998Sjhb xor %edx,%edx 33285998Sjhb mov DIR_EA_LEN(%bx),%dl 33385998Sjhb add %edx,%eax # Skip extended attributes 33485998Sjhb mov %eax,rec_lba # Save LBA 33585998Sjhb mov DIR_SIZE(%bx),%eax # Save size 33685998Sjhb mov %eax,rec_size 33785998Sjhb xor %cl,%cl # Zero length 33885998Sjhb push %si # Save 33985998Sjhbff.namelen: inc %cl # Update length 34085998Sjhb lodsb # Read char 34185998Sjhb cmp $0,%al # Nul? 34285998Sjhb je ff.namedone # Yes 34385998Sjhb cmp $'/',%al # Path separator? 34485998Sjhb jnz ff.namelen # No, keep going 34585998Sjhbff.namedone: dec %cl # Adjust length and save 34685998Sjhb mov %cl,name_len 34785998Sjhb pop %si # Restore 34885998Sjhbff.load: mov rec_lba,%eax # Load LBA 34985998Sjhb mov $MEM_DIR,%ebx # Address buffer 35085998Sjhb mov $1,%dh # One sector 35185998Sjhb call read # Read directory block 35285998Sjhb incl rec_lba # Update LBA to next block 35385998Sjhbff.scan: mov %ebx,%edx # Check for EOF 35485998Sjhb sub $MEM_DIR,%edx 35585998Sjhb cmp %edx,rec_size 35685998Sjhb ja ff.scan.1 35785998Sjhb stc # EOF reached 35885998Sjhb ret 35985998Sjhbff.scan.1: cmpb $0,DIR_LEN(%bx) # Last record in block? 36085998Sjhb je ff.nextblock 36185998Sjhb push %si # Save 36285998Sjhb movzbw DIR_NAMELEN(%bx),%si # Find end of string 36386001Sjhbff.checkver: cmpb $'0',DIR_NAME-1(%bx,%si) # Less than '0'? 36485998Sjhb jb ff.checkver.1 36585998Sjhb cmpb $'9',DIR_NAME-1(%bx,%si) # Greater than '9'? 36685998Sjhb ja ff.checkver.1 36785998Sjhb dec %si # Next char 36885998Sjhb jnz ff.checkver 36985998Sjhb jmp ff.checklen # All numbers in name, so 37085998Sjhb # no version 37185998Sjhbff.checkver.1: movzbw DIR_NAMELEN(%bx),%cx 37285998Sjhb cmp %cx,%si # Did we find any digits? 37385998Sjhb je ff.checkdot # No 37485998Sjhb cmpb $';',DIR_NAME-1(%bx,%si) # Check for semicolon 37585998Sjhb jne ff.checkver.2 37685998Sjhb dec %si # Skip semicolon 37785998Sjhb mov %si,%cx 37885998Sjhb mov %cl,DIR_NAMELEN(%bx) # Adjust length 37985998Sjhb jmp ff.checkdot 38085998Sjhbff.checkver.2: mov %cx,%si # Restore %si to end of string 38185998Sjhbff.checkdot: cmpb $'.',DIR_NAME-1(%bx,%si) # Trailing dot? 38285998Sjhb jne ff.checklen # No 38385998Sjhb decb DIR_NAMELEN(%bx) # Adjust length 38485998Sjhbff.checklen: pop %si # Restore 38585998Sjhb movzbw name_len,%cx # Load length of name 38685998Sjhb cmp %cl,DIR_NAMELEN(%bx) # Does length match? 38785998Sjhb je ff.checkname # Yes, check name 38885998Sjhbff.nextrec: add DIR_LEN(%bx),%bl # Next record 38985998Sjhb adc $0,%bh 39085998Sjhb jmp ff.scan 39185998Sjhbff.nextblock: subl $SECTOR_SIZE,rec_size # Adjust size 39285998Sjhb jnc ff.load # If subtract ok, keep going 39385998Sjhb ret # End of file, so not found 39486001Sjhbff.checkname: lea DIR_NAME(%bx),%di # Address name in record 39585998Sjhb push %si # Save 39685998Sjhb repe cmpsb # Compare name 397157663Sjhb je ff.match # We have a winner! 39885998Sjhb pop %si # Restore 39985998Sjhb jmp ff.nextrec # Keep looking. 40085998Sjhbff.match: add $2,%sp # Discard saved %si 40185998Sjhb clc # Clear carry 40285998Sjhb ret 40385998Sjhb 40485998Sjhb# 40585998Sjhb# Load DH sectors starting at LBA EAX into [EBX]. 40685998Sjhb# 40785998Sjhb# Trashes: EAX 40885998Sjhb# 40985998Sjhbread: push %si # Save 410124445Sjhb push %cx # Save since some BIOSs trash 41185998Sjhb mov %eax,edd_lba # LBA to read from 41285998Sjhb mov %ebx,%eax # Convert address 41385998Sjhb shr $4,%eax # to segment 41485998Sjhb mov %ax,edd_addr+0x2 # and store 41586164Sjhbread.retry: call twiddle # Entertain the user 41685998Sjhb push %dx # Save 41785998Sjhb mov $edd_packet,%si # Address Packet 41885998Sjhb mov %dh,edd_len # Set length 41985998Sjhb mov drive,%dl # BIOS Device 42085998Sjhb mov $0x42,%ah # BIOS: Extended Read 42185998Sjhb int $0x13 # Call BIOS 42285998Sjhb pop %dx # Restore 42385998Sjhb jc read.fail # Worked? 424124445Sjhb pop %cx # Restore 425124445Sjhb pop %si 42685998Sjhb ret # Return 42785998Sjhbread.fail: cmp $ERROR_TIMEOUT,%ah # Timeout? 42885998Sjhb je read.retry # Yes, Retry. 42985998Sjhbread.error: mov %ah,%al # Save error 43085998Sjhb mov $hex_error,%di # Format it 43185998Sjhb call hex8 # as hex 43285998Sjhb mov $msg_badread,%si # Display Read error message 43385998Sjhb 43485998Sjhb# 43585998Sjhb# Display error message at [SI] and halt. 43685998Sjhb# 43785998Sjhberror: call putstr # Display message 43885998Sjhbhalt: hlt 43985998Sjhb jmp halt # Spin 44085998Sjhb 44185998Sjhb# 44285998Sjhb# Display a null-terminated string. 44385998Sjhb# 44485998Sjhb# Trashes: AX, SI 44585998Sjhb# 44685998Sjhbputstr: push %bx # Save 44785998Sjhbputstr.load: lodsb # load %al from %ds:(%si) 44885998Sjhb test %al,%al # stop at null 44985998Sjhb jnz putstr.putc # if the char != null, output it 45085998Sjhb pop %bx # Restore 45185998Sjhb ret # return when null is hit 45285998Sjhbputstr.putc: call putc # output char 45385998Sjhb jmp putstr.load # next char 45485998Sjhb 45585998Sjhb# 45685998Sjhb# Display a single char. 45785998Sjhb# 45886164Sjhbputc: mov $0x7,%bx # attribute for output 45985998Sjhb mov $0xe,%ah # BIOS: put_char 46056693Sjhb int $0x10 # call BIOS, print char in %al 46185998Sjhb ret # Return to caller 46256693Sjhb 46356693Sjhb# 46485998Sjhb# Output the "twiddle" 46585998Sjhb# 46685998Sjhbtwiddle: push %ax # Save 46785998Sjhb push %bx # Save 46885998Sjhb mov twiddle_index,%al # Load index 469166926Sremko mov $twiddle_chars,%bx # Address table 47085998Sjhb inc %al # Next 47185998Sjhb and $3,%al # char 472124445Sjhb mov %al,twiddle_index # Save index for next call 47385998Sjhb xlat # Get char 47485998Sjhb call putc # Output it 47585998Sjhb mov $8,%al # Backspace 47685998Sjhb call putc # Output it 47785998Sjhb pop %bx # Restore 47885998Sjhb pop %ax # Restore 47985998Sjhb ret 48085998Sjhb 48185998Sjhb# 482157664Sjhb# Enable A20. Put an upper limit on the amount of time we wait for the 483157652Ssobomax# keyboard controller to get ready (65K x ISA access time). If 484157664Sjhb# we wait more than that amount, the hardware is probably 485157664Sjhb# legacy-free and simply doesn't have a keyboard controller. 486157664Sjhb# Thus, the A20 line is already enabled. 48756693Sjhb# 48856693Sjhbseta20: cli # Disable interrupts 489157652Ssobomax xor %cx,%cx # Clear 490157652Ssobomaxseta20.1: inc %cx # Increment, overflow? 491157652Ssobomax jz seta20.3 # Yes 492157652Ssobomax in $0x64,%al # Get status 49385998Sjhb test $0x2,%al # Busy? 49456693Sjhb jnz seta20.1 # Yes 49585998Sjhb mov $0xd1,%al # Command: Write 49685998Sjhb out %al,$0x64 # output port 49785998Sjhbseta20.2: in $0x64,%al # Get status 49885998Sjhb test $0x2,%al # Busy? 49956693Sjhb jnz seta20.2 # Yes 50085998Sjhb mov $0xdf,%al # Enable 50185998Sjhb out %al,$0x60 # A20 502157652Ssobomaxseta20.3: sti # Enable interrupts 50385998Sjhb ret # To caller 50456693Sjhb 50556693Sjhb# 50686001Sjhb# Convert AL to hex, saving the result to [EDI]. 50785998Sjhb# 50885998Sjhbhex8: pushl %eax # Save 50985998Sjhb shrb $0x4,%al # Do upper 51085998Sjhb call hex8.1 # 4 51185998Sjhb popl %eax # Restore 51285998Sjhbhex8.1: andb $0xf,%al # Get lower 4 51385998Sjhb cmpb $0xa,%al # Convert 51485998Sjhb sbbb $0x69,%al # to hex 51585998Sjhb das # digit 51685998Sjhb orb $0x20,%al # To lower case 51785998Sjhb stosb # Save char 51885998Sjhb ret # (Recursive) 51985998Sjhb 52085998Sjhb# 52158713Sjhb# BTX client to start btxldr 52256693Sjhb# 52356693Sjhb .code32 52485998Sjhbbtx_client: mov $(MEM_ARG_BTX-MEM_BTX_CLIENT+MEM_ARG_SIZE-4), %esi 52556693Sjhb # %ds:(%esi) -> end 52656693Sjhb # of boot[12] args 52785998Sjhb mov $(MEM_ARG_SIZE/4),%ecx # Number of words to push 52856693Sjhb std # Go backwards 52956693Sjhbpush_arg: lodsl # Read argument 53085998Sjhb push %eax # Push it onto the stack 53156693Sjhb loop push_arg # Push all of the arguments 53256693Sjhb cld # In case anyone depends on this 53358713Sjhb pushl MEM_ARG_BTX-MEM_BTX_CLIENT+MEM_ARG_SIZE # Entry point of 53458713Sjhb # the loader 53585998Sjhb push %eax # Emulate a near call 53685998Sjhb mov $0x1,%eax # 'exec' system call 53756693Sjhb int $INT_SYS # BTX system call 53856693Sjhbbtx_client_end: 53956693Sjhb .code16 54081363Sjhb 54156693Sjhb .p2align 4 54256693Sjhb# 54356693Sjhb# Global descriptor table. 54456693Sjhb# 54556693Sjhbgdt: .word 0x0,0x0,0x0,0x0 # Null entry 54656693Sjhb .word 0xffff,0x0,0x9200,0xcf # SEL_SDATA 54756693Sjhb .word 0xffff,0x0,0x9200,0x0 # SEL_RDATA 54856693Sjhb .word 0xffff,0x0,0x9a00,0xcf # SEL_SCODE (32-bit) 54981363Sjhb .word 0xffff,0x0,0x9a00,0x8f # SEL_SCODE16 (16-bit) 55056693Sjhbgdt.1: 55156693Sjhb# 55256693Sjhb# Pseudo-descriptors. 55356693Sjhb# 55456693Sjhbgdtdesc: .word gdt.1-gdt-1 # Limit 55558713Sjhb .long gdt # Base 55685998Sjhb# 55785998Sjhb# EDD Packet 55885998Sjhb# 55985998Sjhbedd_packet: .byte 0x10 # Length 56085998Sjhb .byte 0 # Reserved 56185998Sjhbedd_len: .byte 0x0 # Num to read 56285998Sjhb .byte 0 # Reserved 56385998Sjhbedd_addr: .word 0x0,0x0 # Seg:Off 56485998Sjhbedd_lba: .quad 0x0 # LBA 56581363Sjhb 56685998Sjhbdrive: .byte 0 56781363Sjhb 56885998Sjhb# 56985998Sjhb# State for searching dir 57085998Sjhb# 57185998Sjhbrec_lba: .long 0x0 # LBA (adjusted for EA) 57285998Sjhbrec_size: .long 0x0 # File size 57385998Sjhbname_len: .byte 0x0 # Length of current name 57485998Sjhb 57585998Sjhbtwiddle_index: .byte 0x0 57685998Sjhb 577123476Sjhbmsg_welcome: .asciz "CD Loader 1.2\r\n\n" 57885998Sjhbmsg_bootinfo: .asciz "Building the boot loader arguments\r\n" 57985998Sjhbmsg_relocate: .asciz "Relocating the loader and the BTX\r\n" 58085998Sjhbmsg_jump: .asciz "Starting the BTX loader\r\n" 58185998Sjhbmsg_badread: .ascii "Read Error: 0x" 582173697Sjhbhex_error: .asciz "00\r\n" 58385998Sjhbmsg_novd: .asciz "Could not find Primary Volume Descriptor\r\n" 58485998Sjhbmsg_lookup: .asciz "Looking up " 58585998Sjhbmsg_lookup2: .asciz "... " 58685998Sjhbmsg_lookupok: .asciz "Found\r\n" 58785998Sjhbmsg_lookupfail: .asciz "File not found\r\n" 58885998Sjhbmsg_load2big: .asciz "File too big\r\n" 589123476Sjhbmsg_failed: .asciz "Boot failed\r\n" 59085998Sjhbtwiddle_chars: .ascii "|/-\\" 591123476Sjhbloader_paths: .asciz "/BOOT/LOADER" 592123476Sjhb .asciz "/boot/loader" 593123476Sjhb .byte 0 59485998Sjhb 595