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. 13130943Sjhb# 3. Neither the name of the author nor the names of any co-contributors 14130943Sjhb# may be used to endorse or promote products derived from this software 15130943Sjhb# without specific prior written permission. 1656693Sjhb# 17130943Sjhb# THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 18130943Sjhb# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 19130943Sjhb# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 20130943Sjhb# ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 21130943Sjhb# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 22130943Sjhb# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 23130943Sjhb# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 24130943Sjhb# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 25130943Sjhb# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 26130943Sjhb# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 27130943Sjhb# SUCH DAMAGE. 2856693Sjhb# 2956693Sjhb 3056693Sjhb# $FreeBSD$ 3156693Sjhb 3256693Sjhb# 3385998Sjhb# This program is a freestanding boot program to load an a.out binary 3485998Sjhb# from a CD-ROM booted with no emulation mode as described by the El 3585998Sjhb# Torito standard. Due to broken BIOSen that do not load the desired 3685998Sjhb# number of sectors, we try to fit this in as small a space as possible. 3756693Sjhb# 3885998Sjhb# Basically, we first create a set of boot arguments to pass to the loaded 3985998Sjhb# binary. Then we attempt to load /boot/loader from the CD we were booted 4085998Sjhb# off of. 4185998Sjhb# 4256693Sjhb 43235219Savg#include <bootargs.h> 44235219Savg 4556693Sjhb# 4656693Sjhb# Memory locations. 4756693Sjhb# 4858713Sjhb .set MEM_PAGE_SIZE,0x1000 # memory page size, 4k 4956693Sjhb .set MEM_ARG,0x900 # Arguments at start 5056693Sjhb .set MEM_ARG_BTX,0xa100 # Where we move them to so the 5156693Sjhb # BTX client can see them 5256693Sjhb .set MEM_ARG_SIZE,0x18 # Size of the arguments 5356693Sjhb .set MEM_BTX_ADDRESS,0x9000 # where BTX lives 5456693Sjhb .set MEM_BTX_ENTRY,0x9010 # where BTX starts to execute 5558713Sjhb .set MEM_BTX_OFFSET,MEM_PAGE_SIZE # offset of BTX in the loader 5656693Sjhb .set MEM_BTX_CLIENT,0xa000 # where BTX clients live 5756693Sjhb# 5858713Sjhb# a.out header fields 5958713Sjhb# 6058713Sjhb .set AOUT_TEXT,0x04 # text segment size 6158713Sjhb .set AOUT_DATA,0x08 # data segment size 6258713Sjhb .set AOUT_BSS,0x0c # zero'd BSS size 6358713Sjhb .set AOUT_SYMBOLS,0x10 # symbol table 6458713Sjhb .set AOUT_ENTRY,0x14 # entry point 6558713Sjhb .set AOUT_HEADER,MEM_PAGE_SIZE # size of the a.out header 6658713Sjhb# 6756693Sjhb# Segment selectors. 6856693Sjhb# 6956693Sjhb .set SEL_SDATA,0x8 # Supervisor data 7056693Sjhb .set SEL_RDATA,0x10 # Real mode data 7156693Sjhb .set SEL_SCODE,0x18 # PM-32 code 7256693Sjhb .set SEL_SCODE16,0x20 # PM-16 code 7356693Sjhb# 7456693Sjhb# BTX constants 7556693Sjhb# 7656693Sjhb .set INT_SYS,0x30 # BTX syscall interrupt 7756693Sjhb# 7885998Sjhb# Constants for reading from the CD. 7985998Sjhb# 8085998Sjhb .set ERROR_TIMEOUT,0x80 # BIOS timeout on read 8185998Sjhb .set NUM_RETRIES,3 # Num times to retry 8285998Sjhb .set SECTOR_SIZE,0x800 # size of a sector 8385998Sjhb .set SECTOR_SHIFT,11 # number of place to shift 8485998Sjhb .set BUFFER_LEN,0x100 # number of sectors in buffer 8585998Sjhb .set MAX_READ,0x10000 # max we can read at a time 8685998Sjhb .set MAX_READ_SEC,MAX_READ >> SECTOR_SHIFT 8785998Sjhb .set MEM_READ_BUFFER,0x9000 # buffer to read from CD 8885998Sjhb .set MEM_VOLDESC,MEM_READ_BUFFER # volume descriptor 8985998Sjhb .set MEM_DIR,MEM_VOLDESC+SECTOR_SIZE # Lookup buffer 9085998Sjhb .set VOLDESC_LBA,0x10 # LBA of vol descriptor 9185998Sjhb .set VD_PRIMARY,1 # Primary VD 9285998Sjhb .set VD_END,255 # VD Terminator 9385998Sjhb .set VD_ROOTDIR,156 # Offset of Root Dir Record 9485998Sjhb .set DIR_LEN,0 # Offset of Dir Record length 9585998Sjhb .set DIR_EA_LEN,1 # Offset of EA length 9685998Sjhb .set DIR_EXTENT,2 # Offset of 64-bit LBA 9785998Sjhb .set DIR_SIZE,10 # Offset of 64-bit length 9885998Sjhb .set DIR_NAMELEN,32 # Offset of 8-bit name len 9985998Sjhb .set DIR_NAME,33 # Offset of dir name 10085998Sjhb# 10158713Sjhb# We expect to be loaded by the BIOS at 0x7c00 (standard boot loader entry 10258713Sjhb# point) 10356693Sjhb# 10456693Sjhb .code16 10556693Sjhb .globl start 10656693Sjhb .org 0x0, 0x0 10756693Sjhb# 10885998Sjhb# Program start. 10956693Sjhb# 11058713Sjhbstart: cld # string ops inc 11185998Sjhb xor %ax,%ax # zero %ax 11285998Sjhb mov %ax,%ss # setup the 11385998Sjhb mov $start,%sp # stack 11485998Sjhb mov %ax,%ds # setup the 11585998Sjhb mov %ax,%es # data segments 11685998Sjhb mov %dl,drive # Save BIOS boot device 11785998Sjhb mov $msg_welcome,%si # %ds:(%si) -> welcome message 11885998Sjhb call putstr # display the welcome message 11956693Sjhb# 12056693Sjhb# Setup the arguments that the loader is expecting from boot[12] 12156693Sjhb# 12285998Sjhb mov $msg_bootinfo,%si # %ds:(%si) -> boot args message 12385998Sjhb call putstr # display the message 12485998Sjhb mov $MEM_ARG,%bx # %ds:(%bx) -> boot args 12585998Sjhb mov %bx,%di # %es:(%di) -> boot args 12685998Sjhb xor %eax,%eax # zero %eax 12785998Sjhb mov $(MEM_ARG_SIZE/4),%cx # Size of arguments in 32-bit 12856693Sjhb # dwords 12956693Sjhb rep # Clear the arguments 13056693Sjhb stosl # to zero 13185998Sjhb mov drive,%dl # Store BIOS boot device 13285998Sjhb mov %dl,0x4(%bx) # in kargs->bootdev 13385998Sjhb or $KARGS_FLAGS_CD,0x8(%bx) # kargs->bootflags |= 13458713Sjhb # KARGS_FLAGS_CD 13556693Sjhb# 13685998Sjhb# Load Volume Descriptor 13785998Sjhb# 13885998Sjhb mov $VOLDESC_LBA,%eax # Set LBA of first VD 13985998Sjhbload_vd: push %eax # Save %eax 14085998Sjhb mov $1,%dh # One sector 14185998Sjhb mov $MEM_VOLDESC,%ebx # Destination 14285998Sjhb call read # Read it in 14385998Sjhb cmpb $VD_PRIMARY,(%bx) # Primary VD? 14485998Sjhb je have_vd # Yes 14585998Sjhb pop %eax # Prepare to 14685998Sjhb inc %eax # try next 14785998Sjhb cmpb $VD_END,(%bx) # Last VD? 14885998Sjhb jne load_vd # No, read next 14985998Sjhb mov $msg_novd,%si # No VD 15085998Sjhb jmp error # Halt 15186164Sjhbhave_vd: # Have Primary VD 15285998Sjhb# 153123476Sjhb# Try to look up the loader binary using the paths in the loader_paths 154123476Sjhb# array. 15585998Sjhb# 156123476Sjhb mov $loader_paths,%si # Point to start of array 157123476Sjhblookup_path: push %si # Save file name pointer 158123476Sjhb call lookup # Try to find file 159123476Sjhb pop %di # Restore file name pointer 160123476Sjhb jnc lookup_found # Found this file 161123476Sjhb xor %al,%al # Look for next 162123476Sjhb mov $0xffff,%cx # path name by 163123476Sjhb repnz # scanning for 164123476Sjhb scasb # nul char 165123476Sjhb mov %di,%si # Point %si at next path 166123476Sjhb mov (%si),%al # Get first char of next path 167123476Sjhb or %al,%al # Is it double nul? 168123476Sjhb jnz lookup_path # No, try it. 169123476Sjhb mov $msg_failed,%si # Failed message 170123476Sjhb jmp error # Halt 171123476Sjhblookup_found: # Found a loader file 17285998Sjhb# 17385998Sjhb# Load the binary into the buffer. Due to real mode addressing limitations 174219126Sbrucec# we have to read it in 64k chunks. 17585998Sjhb# 17685998Sjhb mov DIR_SIZE(%bx),%eax # Read file length 17785998Sjhb add $SECTOR_SIZE-1,%eax # Convert length to sectors 178124445Sjhb shr $SECTOR_SHIFT,%eax 17985998Sjhb cmp $BUFFER_LEN,%eax 18085998Sjhb jbe load_sizeok 18185998Sjhb mov $msg_load2big,%si # Error message 18285998Sjhb call error 18385998Sjhbload_sizeok: movzbw %al,%cx # Num sectors to read 18485998Sjhb mov DIR_EXTENT(%bx),%eax # Load extent 18585998Sjhb xor %edx,%edx 18685998Sjhb mov DIR_EA_LEN(%bx),%dl 18785998Sjhb add %edx,%eax # Skip extended 18885998Sjhb mov $MEM_READ_BUFFER,%ebx # Read into the buffer 18985998Sjhbload_loop: mov %cl,%dh 19085998Sjhb cmp $MAX_READ_SEC,%cl # Truncate to max read size 19185998Sjhb jbe load_notrunc 19285998Sjhb mov $MAX_READ_SEC,%dh 19385998Sjhbload_notrunc: sub %dh,%cl # Update count 19485998Sjhb push %eax # Save 19585998Sjhb call read # Read it in 19685998Sjhb pop %eax # Restore 19785998Sjhb add $MAX_READ_SEC,%eax # Update LBA 19885998Sjhb add $MAX_READ,%ebx # Update dest addr 19985998Sjhb jcxz load_done # Done? 20085998Sjhb jmp load_loop # Keep going 20185998Sjhbload_done: 20285998Sjhb# 20356693Sjhb# Turn on the A20 address line 20456693Sjhb# 20585998Sjhb call seta20 # Turn A20 on 20656693Sjhb# 20756693Sjhb# Relocate the loader and BTX using a very lazy protected mode 20856693Sjhb# 20985998Sjhb mov $msg_relocate,%si # Display the 21085998Sjhb call putstr # relocation message 21185998Sjhb mov MEM_READ_BUFFER+AOUT_ENTRY,%edi # %edi is the destination 21285998Sjhb mov $(MEM_READ_BUFFER+AOUT_HEADER),%esi # %esi is 21358713Sjhb # the start of the text 21458713Sjhb # segment 21585998Sjhb mov MEM_READ_BUFFER+AOUT_TEXT,%ecx # %ecx = length of the text 21658713Sjhb # segment 21785998Sjhb push %edi # Save entry point for later 21856693Sjhb lgdt gdtdesc # setup our own gdt 21956693Sjhb cli # turn off interrupts 22085998Sjhb mov %cr0,%eax # Turn on 22185998Sjhb or $0x1,%al # protected 22285998Sjhb mov %eax,%cr0 # mode 22360821Sjhb ljmp $SEL_SCODE,$pm_start # long jump to clear the 22460821Sjhb # instruction pre-fetch queue 22556693Sjhb .code32 22685998Sjhbpm_start: mov $SEL_SDATA,%ax # Initialize 22785998Sjhb mov %ax,%ds # %ds and 22885998Sjhb mov %ax,%es # %es to a flat selector 22958713Sjhb rep # Relocate the 23058713Sjhb movsb # text segment 23185998Sjhb add $(MEM_PAGE_SIZE - 1),%edi # pad %edi out to a new page 23285998Sjhb and $~(MEM_PAGE_SIZE - 1),%edi # for the data segment 23385998Sjhb mov MEM_READ_BUFFER+AOUT_DATA,%ecx # size of the data segment 23458713Sjhb rep # Relocate the 23558713Sjhb movsb # data segment 23685998Sjhb mov MEM_READ_BUFFER+AOUT_BSS,%ecx # size of the bss 23785998Sjhb xor %eax,%eax # zero %eax 23885998Sjhb add $3,%cl # round %ecx up to 23985998Sjhb shr $2,%ecx # a multiple of 4 24058713Sjhb rep # zero the 24158713Sjhb stosl # bss 24285998Sjhb mov MEM_READ_BUFFER+AOUT_ENTRY,%esi # %esi -> relocated loader 24385998Sjhb add $MEM_BTX_OFFSET,%esi # %esi -> BTX in the loader 24485998Sjhb mov $MEM_BTX_ADDRESS,%edi # %edi -> where BTX needs to go 24585998Sjhb movzwl 0xa(%esi),%ecx # %ecx -> length of BTX 24656693Sjhb rep # Relocate 24756693Sjhb movsb # BTX 24858713Sjhb ljmp $SEL_SCODE16,$pm_16 # Jump to 16-bit PM 24956693Sjhb .code16 25085998Sjhbpm_16: mov $SEL_RDATA,%ax # Initialize 25185998Sjhb mov %ax,%ds # %ds and 25285998Sjhb mov %ax,%es # %es to a real mode selector 25385998Sjhb mov %cr0,%eax # Turn off 25485998Sjhb and $~0x1,%al # protected 25585998Sjhb mov %eax,%cr0 # mode 25660821Sjhb ljmp $0,$pm_end # Long jump to clear the 25760821Sjhb # instruction pre-fetch queue 25856693Sjhbpm_end: sti # Turn interrupts back on now 25956693Sjhb# 26056693Sjhb# Copy the BTX client to MEM_BTX_CLIENT 26156693Sjhb# 26285998Sjhb xor %ax,%ax # zero %ax and set 26385998Sjhb mov %ax,%ds # %ds and %es 26485998Sjhb mov %ax,%es # to segment 0 26585998Sjhb mov $MEM_BTX_CLIENT,%di # Prepare to relocate 26685998Sjhb mov $btx_client,%si # the simple btx client 26785998Sjhb mov $(btx_client_end-btx_client),%cx # length of btx client 26856693Sjhb rep # Relocate the 26956693Sjhb movsb # simple BTX client 27056693Sjhb# 27156693Sjhb# Copy the boot[12] args to where the BTX client can see them 27256693Sjhb# 27385998Sjhb mov $MEM_ARG,%si # where the args are at now 27485998Sjhb mov $MEM_ARG_BTX,%di # where the args are moving to 27585998Sjhb mov $(MEM_ARG_SIZE/4),%cx # size of the arguments in longs 27656693Sjhb rep # Relocate 27756693Sjhb movsl # the words 27856693Sjhb# 27958713Sjhb# Save the entry point so the client can get to it later on 28058713Sjhb# 28185998Sjhb pop %eax # Restore saved entry point 28285998Sjhb stosl # and add it to the end of 28385998Sjhb # the arguments 28458713Sjhb# 28556693Sjhb# Now we just start up BTX and let it do the rest 28656693Sjhb# 28785998Sjhb mov $msg_jump,%si # Display the 28885998Sjhb call putstr # jump message 28960821Sjhb ljmp $0,$MEM_BTX_ENTRY # Jump to the BTX entry point 29056693Sjhb 29156693Sjhb# 29285998Sjhb# Lookup the file in the path at [SI] from the root directory. 29356693Sjhb# 29485998Sjhb# Trashes: All but BX 295123476Sjhb# Returns: CF = 0 (success), BX = pointer to record 296123476Sjhb# CF = 1 (not found) 29785998Sjhb# 29885998Sjhblookup: mov $VD_ROOTDIR+MEM_VOLDESC,%bx # Root directory record 29985998Sjhb push %si 30085998Sjhb mov $msg_lookup,%si # Display lookup message 30185998Sjhb call putstr 30285998Sjhb pop %si 30385998Sjhb push %si 30485998Sjhb call putstr 30585998Sjhb mov $msg_lookup2,%si 30685998Sjhb call putstr 30785998Sjhb pop %si 30885998Sjhblookup_dir: lodsb # Get first char of path 30985998Sjhb cmp $0,%al # Are we done? 31085998Sjhb je lookup_done # Yes 31185998Sjhb cmp $'/',%al # Skip path separator. 31285998Sjhb je lookup_dir 31385998Sjhb dec %si # Undo lodsb side effect 31485998Sjhb call find_file # Lookup first path item 31585998Sjhb jnc lookup_dir # Try next component 316123476Sjhb mov $msg_lookupfail,%si # Not found message 317123476Sjhb call putstr 318123476Sjhb stc # Set carry 319123476Sjhb ret 32085998Sjhb jmp error 32185998Sjhblookup_done: mov $msg_lookupok,%si # Success message 32285998Sjhb call putstr 323123476Sjhb clc # Clear carry 32485998Sjhb ret 32585998Sjhb 32685998Sjhb# 32785998Sjhb# Lookup file at [SI] in directory whose record is at [BX]. 32885998Sjhb# 32985998Sjhb# Trashes: All but returns 330123476Sjhb# Returns: CF = 0 (success), BX = pointer to record, SI = next path item 33185998Sjhb# CF = 1 (not found), SI = preserved 33285998Sjhb# 33386001Sjhbfind_file: mov DIR_EXTENT(%bx),%eax # Load extent 33485998Sjhb xor %edx,%edx 33585998Sjhb mov DIR_EA_LEN(%bx),%dl 33685998Sjhb add %edx,%eax # Skip extended attributes 33785998Sjhb mov %eax,rec_lba # Save LBA 33885998Sjhb mov DIR_SIZE(%bx),%eax # Save size 33985998Sjhb mov %eax,rec_size 34085998Sjhb xor %cl,%cl # Zero length 34185998Sjhb push %si # Save 34285998Sjhbff.namelen: inc %cl # Update length 34385998Sjhb lodsb # Read char 34485998Sjhb cmp $0,%al # Nul? 34585998Sjhb je ff.namedone # Yes 34685998Sjhb cmp $'/',%al # Path separator? 34785998Sjhb jnz ff.namelen # No, keep going 34885998Sjhbff.namedone: dec %cl # Adjust length and save 34985998Sjhb mov %cl,name_len 35085998Sjhb pop %si # Restore 35185998Sjhbff.load: mov rec_lba,%eax # Load LBA 35285998Sjhb mov $MEM_DIR,%ebx # Address buffer 35385998Sjhb mov $1,%dh # One sector 35485998Sjhb call read # Read directory block 35585998Sjhb incl rec_lba # Update LBA to next block 35685998Sjhbff.scan: mov %ebx,%edx # Check for EOF 35785998Sjhb sub $MEM_DIR,%edx 35885998Sjhb cmp %edx,rec_size 35985998Sjhb ja ff.scan.1 36085998Sjhb stc # EOF reached 36185998Sjhb ret 36285998Sjhbff.scan.1: cmpb $0,DIR_LEN(%bx) # Last record in block? 36385998Sjhb je ff.nextblock 36485998Sjhb push %si # Save 36585998Sjhb movzbw DIR_NAMELEN(%bx),%si # Find end of string 36686001Sjhbff.checkver: cmpb $'0',DIR_NAME-1(%bx,%si) # Less than '0'? 36785998Sjhb jb ff.checkver.1 36885998Sjhb cmpb $'9',DIR_NAME-1(%bx,%si) # Greater than '9'? 36985998Sjhb ja ff.checkver.1 37085998Sjhb dec %si # Next char 37185998Sjhb jnz ff.checkver 37285998Sjhb jmp ff.checklen # All numbers in name, so 37385998Sjhb # no version 37485998Sjhbff.checkver.1: movzbw DIR_NAMELEN(%bx),%cx 37585998Sjhb cmp %cx,%si # Did we find any digits? 37685998Sjhb je ff.checkdot # No 37785998Sjhb cmpb $';',DIR_NAME-1(%bx,%si) # Check for semicolon 37885998Sjhb jne ff.checkver.2 37985998Sjhb dec %si # Skip semicolon 38085998Sjhb mov %si,%cx 38185998Sjhb mov %cl,DIR_NAMELEN(%bx) # Adjust length 38285998Sjhb jmp ff.checkdot 38385998Sjhbff.checkver.2: mov %cx,%si # Restore %si to end of string 38485998Sjhbff.checkdot: cmpb $'.',DIR_NAME-1(%bx,%si) # Trailing dot? 38585998Sjhb jne ff.checklen # No 38685998Sjhb decb DIR_NAMELEN(%bx) # Adjust length 38785998Sjhbff.checklen: pop %si # Restore 38885998Sjhb movzbw name_len,%cx # Load length of name 38985998Sjhb cmp %cl,DIR_NAMELEN(%bx) # Does length match? 39085998Sjhb je ff.checkname # Yes, check name 39185998Sjhbff.nextrec: add DIR_LEN(%bx),%bl # Next record 39285998Sjhb adc $0,%bh 39385998Sjhb jmp ff.scan 39485998Sjhbff.nextblock: subl $SECTOR_SIZE,rec_size # Adjust size 39585998Sjhb jnc ff.load # If subtract ok, keep going 39685998Sjhb ret # End of file, so not found 39786001Sjhbff.checkname: lea DIR_NAME(%bx),%di # Address name in record 39885998Sjhb push %si # Save 39985998Sjhb repe cmpsb # Compare name 400157663Sjhb je ff.match # We have a winner! 40185998Sjhb pop %si # Restore 40285998Sjhb jmp ff.nextrec # Keep looking. 40385998Sjhbff.match: add $2,%sp # Discard saved %si 40485998Sjhb clc # Clear carry 40585998Sjhb ret 40685998Sjhb 40785998Sjhb# 40885998Sjhb# Load DH sectors starting at LBA EAX into [EBX]. 40985998Sjhb# 41085998Sjhb# Trashes: EAX 41185998Sjhb# 41285998Sjhbread: push %si # Save 413124445Sjhb push %cx # Save since some BIOSs trash 41485998Sjhb mov %eax,edd_lba # LBA to read from 41585998Sjhb mov %ebx,%eax # Convert address 41685998Sjhb shr $4,%eax # to segment 41785998Sjhb mov %ax,edd_addr+0x2 # and store 41886164Sjhbread.retry: call twiddle # Entertain the user 41985998Sjhb push %dx # Save 42085998Sjhb mov $edd_packet,%si # Address Packet 42185998Sjhb mov %dh,edd_len # Set length 42285998Sjhb mov drive,%dl # BIOS Device 42385998Sjhb mov $0x42,%ah # BIOS: Extended Read 42485998Sjhb int $0x13 # Call BIOS 42585998Sjhb pop %dx # Restore 42685998Sjhb jc read.fail # Worked? 427124445Sjhb pop %cx # Restore 428124445Sjhb pop %si 42985998Sjhb ret # Return 43085998Sjhbread.fail: cmp $ERROR_TIMEOUT,%ah # Timeout? 43185998Sjhb je read.retry # Yes, Retry. 43285998Sjhbread.error: mov %ah,%al # Save error 43385998Sjhb mov $hex_error,%di # Format it 43485998Sjhb call hex8 # as hex 43585998Sjhb mov $msg_badread,%si # Display Read error message 43685998Sjhb 43785998Sjhb# 43885998Sjhb# Display error message at [SI] and halt. 43985998Sjhb# 44085998Sjhberror: call putstr # Display message 44185998Sjhbhalt: hlt 44285998Sjhb jmp halt # Spin 44385998Sjhb 44485998Sjhb# 44585998Sjhb# Display a null-terminated string. 44685998Sjhb# 44785998Sjhb# Trashes: AX, SI 44885998Sjhb# 44985998Sjhbputstr: push %bx # Save 45085998Sjhbputstr.load: lodsb # load %al from %ds:(%si) 45185998Sjhb test %al,%al # stop at null 45285998Sjhb jnz putstr.putc # if the char != null, output it 45385998Sjhb pop %bx # Restore 45485998Sjhb ret # return when null is hit 45585998Sjhbputstr.putc: call putc # output char 45685998Sjhb jmp putstr.load # next char 45785998Sjhb 45885998Sjhb# 45985998Sjhb# Display a single char. 46085998Sjhb# 46186164Sjhbputc: mov $0x7,%bx # attribute for output 46285998Sjhb mov $0xe,%ah # BIOS: put_char 46356693Sjhb int $0x10 # call BIOS, print char in %al 46485998Sjhb ret # Return to caller 46556693Sjhb 46656693Sjhb# 46785998Sjhb# Output the "twiddle" 46885998Sjhb# 46985998Sjhbtwiddle: push %ax # Save 47085998Sjhb push %bx # Save 47185998Sjhb mov twiddle_index,%al # Load index 472166926Sremko mov $twiddle_chars,%bx # Address table 47385998Sjhb inc %al # Next 47485998Sjhb and $3,%al # char 475124445Sjhb mov %al,twiddle_index # Save index for next call 47685998Sjhb xlat # Get char 47785998Sjhb call putc # Output it 47885998Sjhb mov $8,%al # Backspace 47985998Sjhb call putc # Output it 48085998Sjhb pop %bx # Restore 48185998Sjhb pop %ax # Restore 48285998Sjhb ret 48385998Sjhb 48485998Sjhb# 485157664Sjhb# Enable A20. Put an upper limit on the amount of time we wait for the 486157652Ssobomax# keyboard controller to get ready (65K x ISA access time). If 487157664Sjhb# we wait more than that amount, the hardware is probably 488157664Sjhb# legacy-free and simply doesn't have a keyboard controller. 489157664Sjhb# Thus, the A20 line is already enabled. 49056693Sjhb# 49156693Sjhbseta20: cli # Disable interrupts 492157652Ssobomax xor %cx,%cx # Clear 493157652Ssobomaxseta20.1: inc %cx # Increment, overflow? 494157652Ssobomax jz seta20.3 # Yes 495157652Ssobomax in $0x64,%al # Get status 49685998Sjhb test $0x2,%al # Busy? 49756693Sjhb jnz seta20.1 # Yes 49885998Sjhb mov $0xd1,%al # Command: Write 49985998Sjhb out %al,$0x64 # output port 50085998Sjhbseta20.2: in $0x64,%al # Get status 50185998Sjhb test $0x2,%al # Busy? 50256693Sjhb jnz seta20.2 # Yes 50385998Sjhb mov $0xdf,%al # Enable 50485998Sjhb out %al,$0x60 # A20 505157652Ssobomaxseta20.3: sti # Enable interrupts 50685998Sjhb ret # To caller 50756693Sjhb 50856693Sjhb# 50986001Sjhb# Convert AL to hex, saving the result to [EDI]. 51085998Sjhb# 51185998Sjhbhex8: pushl %eax # Save 51285998Sjhb shrb $0x4,%al # Do upper 51385998Sjhb call hex8.1 # 4 51485998Sjhb popl %eax # Restore 51585998Sjhbhex8.1: andb $0xf,%al # Get lower 4 51685998Sjhb cmpb $0xa,%al # Convert 51785998Sjhb sbbb $0x69,%al # to hex 51885998Sjhb das # digit 51985998Sjhb orb $0x20,%al # To lower case 52085998Sjhb stosb # Save char 52185998Sjhb ret # (Recursive) 52285998Sjhb 52385998Sjhb# 52458713Sjhb# BTX client to start btxldr 52556693Sjhb# 52656693Sjhb .code32 52785998Sjhbbtx_client: mov $(MEM_ARG_BTX-MEM_BTX_CLIENT+MEM_ARG_SIZE-4), %esi 52856693Sjhb # %ds:(%esi) -> end 52956693Sjhb # of boot[12] args 53085998Sjhb mov $(MEM_ARG_SIZE/4),%ecx # Number of words to push 53156693Sjhb std # Go backwards 53256693Sjhbpush_arg: lodsl # Read argument 53385998Sjhb push %eax # Push it onto the stack 53456693Sjhb loop push_arg # Push all of the arguments 53556693Sjhb cld # In case anyone depends on this 53658713Sjhb pushl MEM_ARG_BTX-MEM_BTX_CLIENT+MEM_ARG_SIZE # Entry point of 53758713Sjhb # the loader 53885998Sjhb push %eax # Emulate a near call 53985998Sjhb mov $0x1,%eax # 'exec' system call 54056693Sjhb int $INT_SYS # BTX system call 54156693Sjhbbtx_client_end: 54256693Sjhb .code16 54381363Sjhb 54456693Sjhb .p2align 4 54556693Sjhb# 54656693Sjhb# Global descriptor table. 54756693Sjhb# 54856693Sjhbgdt: .word 0x0,0x0,0x0,0x0 # Null entry 54956693Sjhb .word 0xffff,0x0,0x9200,0xcf # SEL_SDATA 55056693Sjhb .word 0xffff,0x0,0x9200,0x0 # SEL_RDATA 55156693Sjhb .word 0xffff,0x0,0x9a00,0xcf # SEL_SCODE (32-bit) 55281363Sjhb .word 0xffff,0x0,0x9a00,0x8f # SEL_SCODE16 (16-bit) 55356693Sjhbgdt.1: 55456693Sjhb# 55556693Sjhb# Pseudo-descriptors. 55656693Sjhb# 55756693Sjhbgdtdesc: .word gdt.1-gdt-1 # Limit 55858713Sjhb .long gdt # Base 55985998Sjhb# 56085998Sjhb# EDD Packet 56185998Sjhb# 56285998Sjhbedd_packet: .byte 0x10 # Length 56385998Sjhb .byte 0 # Reserved 56485998Sjhbedd_len: .byte 0x0 # Num to read 56585998Sjhb .byte 0 # Reserved 56685998Sjhbedd_addr: .word 0x0,0x0 # Seg:Off 56785998Sjhbedd_lba: .quad 0x0 # LBA 56881363Sjhb 56985998Sjhbdrive: .byte 0 57081363Sjhb 57185998Sjhb# 57285998Sjhb# State for searching dir 57385998Sjhb# 57485998Sjhbrec_lba: .long 0x0 # LBA (adjusted for EA) 57585998Sjhbrec_size: .long 0x0 # File size 57685998Sjhbname_len: .byte 0x0 # Length of current name 57785998Sjhb 57885998Sjhbtwiddle_index: .byte 0x0 57985998Sjhb 580123476Sjhbmsg_welcome: .asciz "CD Loader 1.2\r\n\n" 58185998Sjhbmsg_bootinfo: .asciz "Building the boot loader arguments\r\n" 58285998Sjhbmsg_relocate: .asciz "Relocating the loader and the BTX\r\n" 58385998Sjhbmsg_jump: .asciz "Starting the BTX loader\r\n" 58485998Sjhbmsg_badread: .ascii "Read Error: 0x" 585173697Sjhbhex_error: .asciz "00\r\n" 58685998Sjhbmsg_novd: .asciz "Could not find Primary Volume Descriptor\r\n" 58785998Sjhbmsg_lookup: .asciz "Looking up " 58885998Sjhbmsg_lookup2: .asciz "... " 58985998Sjhbmsg_lookupok: .asciz "Found\r\n" 59085998Sjhbmsg_lookupfail: .asciz "File not found\r\n" 59185998Sjhbmsg_load2big: .asciz "File too big\r\n" 592123476Sjhbmsg_failed: .asciz "Boot failed\r\n" 59385998Sjhbtwiddle_chars: .ascii "|/-\\" 594123476Sjhbloader_paths: .asciz "/BOOT/LOADER" 595123476Sjhb .asciz "/boot/loader" 596123476Sjhb .byte 0 59785998Sjhb 598