1ca987d46SWarner Losh# 2ca987d46SWarner Losh# Copyright (c) 2001 John Baldwin <jhb@FreeBSD.org> 3ca987d46SWarner Losh# 4ca987d46SWarner Losh# Redistribution and use in source and binary forms, with or without 5ca987d46SWarner Losh# modification, are permitted provided that the following conditions 6ca987d46SWarner Losh# are met: 7ca987d46SWarner Losh# 1. Redistributions of source code must retain the above copyright 8ca987d46SWarner Losh# notice, this list of conditions and the following disclaimer. 9ca987d46SWarner Losh# 2. Redistributions in binary form must reproduce the above copyright 10ca987d46SWarner Losh# notice, this list of conditions and the following disclaimer in the 11ca987d46SWarner Losh# documentation and/or other materials provided with the distribution. 12ca987d46SWarner Losh# 13ca987d46SWarner Losh# THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 14ca987d46SWarner Losh# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 15ca987d46SWarner Losh# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 16ca987d46SWarner Losh# ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 17ca987d46SWarner Losh# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 18ca987d46SWarner Losh# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 19ca987d46SWarner Losh# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 20ca987d46SWarner Losh# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 21ca987d46SWarner Losh# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 22ca987d46SWarner Losh# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 23ca987d46SWarner Losh# SUCH DAMAGE. 24ca987d46SWarner Losh# 25ca987d46SWarner Losh 26ca987d46SWarner Losh# 27ca987d46SWarner Losh# This program is a freestanding boot program to load an a.out binary 28ca987d46SWarner Losh# from a CD-ROM booted with no emulation mode as described by the El 29ca987d46SWarner Losh# Torito standard. Due to broken BIOSen that do not load the desired 30ca987d46SWarner Losh# number of sectors, we try to fit this in as small a space as possible. 31ca987d46SWarner Losh# 32ca987d46SWarner Losh# Basically, we first create a set of boot arguments to pass to the loaded 33ca987d46SWarner Losh# binary. Then we attempt to load /boot/loader from the CD we were booted 34ca987d46SWarner Losh# off of. 35ca987d46SWarner Losh# 36ca987d46SWarner Losh 37ca987d46SWarner Losh#include <bootargs.h> 38ca987d46SWarner Losh 39ca987d46SWarner Losh# 40ca987d46SWarner Losh# Memory locations. 41ca987d46SWarner Losh# 42ca987d46SWarner Losh .set MEM_PAGE_SIZE,0x1000 # memory page size, 4k 43ca987d46SWarner Losh .set MEM_ARG,0x900 # Arguments at start 44ca987d46SWarner Losh .set MEM_ARG_BTX,0xa100 # Where we move them to so the 45ca987d46SWarner Losh # BTX client can see them 46ca987d46SWarner Losh .set MEM_ARG_SIZE,0x18 # Size of the arguments 47ca987d46SWarner Losh .set MEM_BTX_ADDRESS,0x9000 # where BTX lives 48ca987d46SWarner Losh .set MEM_BTX_ENTRY,0x9010 # where BTX starts to execute 49ca987d46SWarner Losh .set MEM_BTX_OFFSET,MEM_PAGE_SIZE # offset of BTX in the loader 50ca987d46SWarner Losh .set MEM_BTX_CLIENT,0xa000 # where BTX clients live 51ca987d46SWarner Losh# 52ca987d46SWarner Losh# a.out header fields 53ca987d46SWarner Losh# 54ca987d46SWarner Losh .set AOUT_TEXT,0x04 # text segment size 55ca987d46SWarner Losh .set AOUT_DATA,0x08 # data segment size 56ca987d46SWarner Losh .set AOUT_BSS,0x0c # zero'd BSS size 57ca987d46SWarner Losh .set AOUT_SYMBOLS,0x10 # symbol table 58ca987d46SWarner Losh .set AOUT_ENTRY,0x14 # entry point 59ca987d46SWarner Losh .set AOUT_HEADER,MEM_PAGE_SIZE # size of the a.out header 60ca987d46SWarner Losh# 61ca987d46SWarner Losh# Segment selectors. 62ca987d46SWarner Losh# 63ca987d46SWarner Losh .set SEL_SDATA,0x8 # Supervisor data 64ca987d46SWarner Losh .set SEL_RDATA,0x10 # Real mode data 65ca987d46SWarner Losh .set SEL_SCODE,0x18 # PM-32 code 66ca987d46SWarner Losh .set SEL_SCODE16,0x20 # PM-16 code 67ca987d46SWarner Losh# 68ca987d46SWarner Losh# BTX constants 69ca987d46SWarner Losh# 70ca987d46SWarner Losh .set INT_SYS,0x30 # BTX syscall interrupt 71ca987d46SWarner Losh# 72ca987d46SWarner Losh# Constants for reading from the CD. 73ca987d46SWarner Losh# 74ca987d46SWarner Losh .set ERROR_TIMEOUT,0x80 # BIOS timeout on read 75ca987d46SWarner Losh .set NUM_RETRIES,3 # Num times to retry 76ca987d46SWarner Losh .set SECTOR_SIZE,0x800 # size of a sector 77ca987d46SWarner Losh .set SECTOR_SHIFT,11 # number of place to shift 78ca987d46SWarner Losh .set BUFFER_LEN,0x100 # number of sectors in buffer 79ca987d46SWarner Losh .set MAX_READ,0x10000 # max we can read at a time 80ca987d46SWarner Losh .set MAX_READ_SEC,MAX_READ >> SECTOR_SHIFT 81ca987d46SWarner Losh .set MEM_READ_BUFFER,0x9000 # buffer to read from CD 82ca987d46SWarner Losh .set MEM_VOLDESC,MEM_READ_BUFFER # volume descriptor 83ca987d46SWarner Losh .set MEM_DIR,MEM_VOLDESC+SECTOR_SIZE # Lookup buffer 84ca987d46SWarner Losh .set VOLDESC_LBA,0x10 # LBA of vol descriptor 85ca987d46SWarner Losh .set VD_PRIMARY,1 # Primary VD 86ca987d46SWarner Losh .set VD_END,255 # VD Terminator 87ca987d46SWarner Losh .set VD_ROOTDIR,156 # Offset of Root Dir Record 88ca987d46SWarner Losh .set DIR_LEN,0 # Offset of Dir Record length 89ca987d46SWarner Losh .set DIR_EA_LEN,1 # Offset of EA length 90ca987d46SWarner Losh .set DIR_EXTENT,2 # Offset of 64-bit LBA 91ca987d46SWarner Losh .set DIR_SIZE,10 # Offset of 64-bit length 92ca987d46SWarner Losh .set DIR_NAMELEN,32 # Offset of 8-bit name len 93ca987d46SWarner Losh .set DIR_NAME,33 # Offset of dir name 94ca987d46SWarner Losh# 95ca987d46SWarner Losh# We expect to be loaded by the BIOS at 0x7c00 (standard boot loader entry 96ca987d46SWarner Losh# point) 97ca987d46SWarner Losh# 98ca987d46SWarner Losh .code16 99ca987d46SWarner Losh .globl start 100ca987d46SWarner Losh .org 0x0, 0x0 101ca987d46SWarner Losh# 102ca987d46SWarner Losh# Program start. 103ca987d46SWarner Losh# 104ca987d46SWarner Loshstart: cld # string ops inc 105ca987d46SWarner Losh xor %ax,%ax # zero %ax 106ca987d46SWarner Losh mov %ax,%ss # setup the 107ca987d46SWarner Losh mov $start,%sp # stack 108ca987d46SWarner Losh mov %ax,%ds # setup the 109ca987d46SWarner Losh mov %ax,%es # data segments 110ca987d46SWarner Losh mov %dl,drive # Save BIOS boot device 111ca987d46SWarner Losh mov $msg_welcome,%si # %ds:(%si) -> welcome message 112ca987d46SWarner Losh call putstr # display the welcome message 113ca987d46SWarner Losh# 114ca987d46SWarner Losh# Setup the arguments that the loader is expecting from boot[12] 115ca987d46SWarner Losh# 116ca987d46SWarner Losh mov $msg_bootinfo,%si # %ds:(%si) -> boot args message 117ca987d46SWarner Losh call putstr # display the message 118ca987d46SWarner Losh mov $MEM_ARG,%bx # %ds:(%bx) -> boot args 119ca987d46SWarner Losh mov %bx,%di # %es:(%di) -> boot args 120ca987d46SWarner Losh xor %eax,%eax # zero %eax 121ca987d46SWarner Losh mov $(MEM_ARG_SIZE/4),%cx # Size of arguments in 32-bit 122ca987d46SWarner Losh # dwords 123ca987d46SWarner Losh rep # Clear the arguments 124ca987d46SWarner Losh stosl # to zero 125ca987d46SWarner Losh mov drive,%dl # Store BIOS boot device 126ca987d46SWarner Losh mov %dl,0x4(%bx) # in kargs->bootdev 127ca987d46SWarner Losh orb $KARGS_FLAGS_CD,0x8(%bx) # kargs->bootflags |= 128ca987d46SWarner Losh # KARGS_FLAGS_CD 129ca987d46SWarner Losh# 130ca987d46SWarner Losh# Load Volume Descriptor 131ca987d46SWarner Losh# 132ca987d46SWarner Losh mov $VOLDESC_LBA,%eax # Set LBA of first VD 133ca987d46SWarner Loshload_vd: push %eax # Save %eax 134ca987d46SWarner Losh mov $1,%dh # One sector 135ca987d46SWarner Losh mov $MEM_VOLDESC,%ebx # Destination 136ca987d46SWarner Losh call read # Read it in 137ca987d46SWarner Losh cmpb $VD_PRIMARY,(%bx) # Primary VD? 138ca987d46SWarner Losh je have_vd # Yes 139ca987d46SWarner Losh pop %eax # Prepare to 140ca987d46SWarner Losh inc %eax # try next 141ca987d46SWarner Losh cmpb $VD_END,(%bx) # Last VD? 142ca987d46SWarner Losh jne load_vd # No, read next 143ca987d46SWarner Losh mov $msg_novd,%si # No VD 144ca987d46SWarner Losh jmp error # Halt 145ca987d46SWarner Loshhave_vd: # Have Primary VD 146ca987d46SWarner Losh# 147ca987d46SWarner Losh# Try to look up the loader binary using the paths in the loader_paths 148ca987d46SWarner Losh# array. 149ca987d46SWarner Losh# 150ca987d46SWarner Losh mov $loader_paths,%si # Point to start of array 151ca987d46SWarner Loshlookup_path: push %si # Save file name pointer 152ca987d46SWarner Losh call lookup # Try to find file 153ca987d46SWarner Losh pop %di # Restore file name pointer 154ca987d46SWarner Losh jnc lookup_found # Found this file 155ca987d46SWarner Losh xor %al,%al # Look for next 156ca987d46SWarner Losh mov $0xffff,%cx # path name by 157ca987d46SWarner Losh repnz # scanning for 158ca987d46SWarner Losh scasb # nul char 159ca987d46SWarner Losh mov %di,%si # Point %si at next path 160ca987d46SWarner Losh mov (%si),%al # Get first char of next path 161ca987d46SWarner Losh or %al,%al # Is it double nul? 162ca987d46SWarner Losh jnz lookup_path # No, try it. 163ca987d46SWarner Losh mov $msg_failed,%si # Failed message 164ca987d46SWarner Losh jmp error # Halt 165ca987d46SWarner Loshlookup_found: # Found a loader file 166ca987d46SWarner Losh# 167ca987d46SWarner Losh# Load the binary into the buffer. Due to real mode addressing limitations 168ca987d46SWarner Losh# we have to read it in 64k chunks. 169ca987d46SWarner Losh# 170ca987d46SWarner Losh mov DIR_SIZE(%bx),%eax # Read file length 171ca987d46SWarner Losh add $SECTOR_SIZE-1,%eax # Convert length to sectors 172ca987d46SWarner Losh shr $SECTOR_SHIFT,%eax 173ca987d46SWarner Losh cmp $BUFFER_LEN,%eax 174ca987d46SWarner Losh jbe load_sizeok 175ca987d46SWarner Losh mov $msg_load2big,%si # Error message 176ca987d46SWarner Losh call error 177ca987d46SWarner Loshload_sizeok: movzbw %al,%cx # Num sectors to read 178ca987d46SWarner Losh mov DIR_EXTENT(%bx),%eax # Load extent 179ca987d46SWarner Losh xor %edx,%edx 180ca987d46SWarner Losh mov DIR_EA_LEN(%bx),%dl 181ca987d46SWarner Losh add %edx,%eax # Skip extended 182ca987d46SWarner Losh mov $MEM_READ_BUFFER,%ebx # Read into the buffer 183ca987d46SWarner Loshload_loop: mov %cl,%dh 184ca987d46SWarner Losh cmp $MAX_READ_SEC,%cl # Truncate to max read size 185ca987d46SWarner Losh jbe load_notrunc 186ca987d46SWarner Losh mov $MAX_READ_SEC,%dh 187ca987d46SWarner Loshload_notrunc: sub %dh,%cl # Update count 188ca987d46SWarner Losh push %eax # Save 189ca987d46SWarner Losh call read # Read it in 190ca987d46SWarner Losh pop %eax # Restore 191ca987d46SWarner Losh add $MAX_READ_SEC,%eax # Update LBA 192ca987d46SWarner Losh add $MAX_READ,%ebx # Update dest addr 193ca987d46SWarner Losh jcxz load_done # Done? 194ca987d46SWarner Losh jmp load_loop # Keep going 195ca987d46SWarner Loshload_done: 196ca987d46SWarner Losh# 197ca987d46SWarner Losh# Turn on the A20 address line 198ca987d46SWarner Losh# 199ca987d46SWarner Losh call seta20 # Turn A20 on 200ca987d46SWarner Losh# 201ca987d46SWarner Losh# Relocate the loader and BTX using a very lazy protected mode 202ca987d46SWarner Losh# 203ca987d46SWarner Losh mov $msg_relocate,%si # Display the 204ca987d46SWarner Losh call putstr # relocation message 205ca987d46SWarner Losh mov MEM_READ_BUFFER+AOUT_ENTRY,%edi # %edi is the destination 206ca987d46SWarner Losh mov $(MEM_READ_BUFFER+AOUT_HEADER),%esi # %esi is 207ca987d46SWarner Losh # the start of the text 208ca987d46SWarner Losh # segment 209ca987d46SWarner Losh mov MEM_READ_BUFFER+AOUT_TEXT,%ecx # %ecx = length of the text 210ca987d46SWarner Losh # segment 211ca987d46SWarner Losh push %edi # Save entry point for later 212ca987d46SWarner Losh lgdt gdtdesc # setup our own gdt 213ca987d46SWarner Losh cli # turn off interrupts 214ca987d46SWarner Losh mov %cr0,%eax # Turn on 215ca987d46SWarner Losh or $0x1,%al # protected 216ca987d46SWarner Losh mov %eax,%cr0 # mode 217ca987d46SWarner Losh ljmp $SEL_SCODE,$pm_start # long jump to clear the 218ca987d46SWarner Losh # instruction pre-fetch queue 219ca987d46SWarner Losh .code32 220ca987d46SWarner Loshpm_start: mov $SEL_SDATA,%ax # Initialize 221ca987d46SWarner Losh mov %ax,%ds # %ds and 222ca987d46SWarner Losh mov %ax,%es # %es to a flat selector 223ca987d46SWarner Losh rep # Relocate the 224ca987d46SWarner Losh movsb # text segment 225ca987d46SWarner Losh add $(MEM_PAGE_SIZE - 1),%edi # pad %edi out to a new page 226ca987d46SWarner Losh and $~(MEM_PAGE_SIZE - 1),%edi # for the data segment 227ca987d46SWarner Losh mov MEM_READ_BUFFER+AOUT_DATA,%ecx # size of the data segment 228ca987d46SWarner Losh rep # Relocate the 229ca987d46SWarner Losh movsb # data segment 230ca987d46SWarner Losh mov MEM_READ_BUFFER+AOUT_BSS,%ecx # size of the bss 231ca987d46SWarner Losh xor %eax,%eax # zero %eax 232ca987d46SWarner Losh add $3,%cl # round %ecx up to 233ca987d46SWarner Losh shr $2,%ecx # a multiple of 4 234ca987d46SWarner Losh rep # zero the 235ca987d46SWarner Losh stosl # bss 236ca987d46SWarner Losh mov MEM_READ_BUFFER+AOUT_ENTRY,%esi # %esi -> relocated loader 237ca987d46SWarner Losh add $MEM_BTX_OFFSET,%esi # %esi -> BTX in the loader 238ca987d46SWarner Losh mov $MEM_BTX_ADDRESS,%edi # %edi -> where BTX needs to go 239ca987d46SWarner Losh movzwl 0xa(%esi),%ecx # %ecx -> length of BTX 240ca987d46SWarner Losh rep # Relocate 241ca987d46SWarner Losh movsb # BTX 242ca987d46SWarner Losh ljmp $SEL_SCODE16,$pm_16 # Jump to 16-bit PM 243ca987d46SWarner Losh .code16 244ca987d46SWarner Loshpm_16: mov $SEL_RDATA,%ax # Initialize 245ca987d46SWarner Losh mov %ax,%ds # %ds and 246ca987d46SWarner Losh mov %ax,%es # %es to a real mode selector 247ca987d46SWarner Losh mov %cr0,%eax # Turn off 248ca987d46SWarner Losh and $~0x1,%al # protected 249ca987d46SWarner Losh mov %eax,%cr0 # mode 250ca987d46SWarner Losh ljmp $0,$pm_end # Long jump to clear the 251ca987d46SWarner Losh # instruction pre-fetch queue 252ca987d46SWarner Loshpm_end: sti # Turn interrupts back on now 253ca987d46SWarner Losh# 254ca987d46SWarner Losh# Copy the BTX client to MEM_BTX_CLIENT 255ca987d46SWarner Losh# 256ca987d46SWarner Losh xor %ax,%ax # zero %ax and set 257ca987d46SWarner Losh mov %ax,%ds # %ds and %es 258ca987d46SWarner Losh mov %ax,%es # to segment 0 259ca987d46SWarner Losh mov $MEM_BTX_CLIENT,%di # Prepare to relocate 260ca987d46SWarner Losh mov $btx_client,%si # the simple btx client 261ca987d46SWarner Losh mov $(btx_client_end-btx_client),%cx # length of btx client 262ca987d46SWarner Losh rep # Relocate the 263ca987d46SWarner Losh movsb # simple BTX client 264ca987d46SWarner Losh# 265ca987d46SWarner Losh# Copy the boot[12] args to where the BTX client can see them 266ca987d46SWarner Losh# 267ca987d46SWarner Losh mov $MEM_ARG,%si # where the args are at now 268ca987d46SWarner Losh mov $MEM_ARG_BTX,%di # where the args are moving to 269ca987d46SWarner Losh mov $(MEM_ARG_SIZE/4),%cx # size of the arguments in longs 270ca987d46SWarner Losh rep # Relocate 271ca987d46SWarner Losh movsl # the words 272ca987d46SWarner Losh# 273ca987d46SWarner Losh# Save the entry point so the client can get to it later on 274ca987d46SWarner Losh# 275ca987d46SWarner Losh pop %eax # Restore saved entry point 276ca987d46SWarner Losh stosl # and add it to the end of 277ca987d46SWarner Losh # the arguments 278ca987d46SWarner Losh# 279ca987d46SWarner Losh# Now we just start up BTX and let it do the rest 280ca987d46SWarner Losh# 281ca987d46SWarner Losh mov $msg_jump,%si # Display the 282ca987d46SWarner Losh call putstr # jump message 283ca987d46SWarner Losh ljmp $0,$MEM_BTX_ENTRY # Jump to the BTX entry point 284ca987d46SWarner Losh 285ca987d46SWarner Losh# 286ca987d46SWarner Losh# Lookup the file in the path at [SI] from the root directory. 287ca987d46SWarner Losh# 288ca987d46SWarner Losh# Trashes: All but BX 289ca987d46SWarner Losh# Returns: CF = 0 (success), BX = pointer to record 290ca987d46SWarner Losh# CF = 1 (not found) 291ca987d46SWarner Losh# 292ca987d46SWarner Loshlookup: mov $VD_ROOTDIR+MEM_VOLDESC,%bx # Root directory record 293ca987d46SWarner Losh push %si 294ca987d46SWarner Losh mov $msg_lookup,%si # Display lookup message 295ca987d46SWarner Losh call putstr 296ca987d46SWarner Losh pop %si 297ca987d46SWarner Losh push %si 298ca987d46SWarner Losh call putstr 299ca987d46SWarner Losh mov $msg_lookup2,%si 300ca987d46SWarner Losh call putstr 301ca987d46SWarner Losh pop %si 302ca987d46SWarner Loshlookup_dir: lodsb # Get first char of path 303ca987d46SWarner Losh cmp $0,%al # Are we done? 304ca987d46SWarner Losh je lookup_done # Yes 305ca987d46SWarner Losh cmp $'/',%al # Skip path separator. 306ca987d46SWarner Losh je lookup_dir 307ca987d46SWarner Losh dec %si # Undo lodsb side effect 308ca987d46SWarner Losh call find_file # Lookup first path item 309ca987d46SWarner Losh jnc lookup_dir # Try next component 310ca987d46SWarner Losh mov $msg_lookupfail,%si # Not found message 311ca987d46SWarner Losh call putstr 312ca987d46SWarner Losh stc # Set carry 313ca987d46SWarner Losh ret 314ca987d46SWarner Losh jmp error 315ca987d46SWarner Loshlookup_done: mov $msg_lookupok,%si # Success message 316ca987d46SWarner Losh call putstr 317ca987d46SWarner Losh clc # Clear carry 318ca987d46SWarner Losh ret 319ca987d46SWarner Losh 320ca987d46SWarner Losh# 321ca987d46SWarner Losh# Lookup file at [SI] in directory whose record is at [BX]. 322ca987d46SWarner Losh# 323ca987d46SWarner Losh# Trashes: All but returns 324ca987d46SWarner Losh# Returns: CF = 0 (success), BX = pointer to record, SI = next path item 325ca987d46SWarner Losh# CF = 1 (not found), SI = preserved 326ca987d46SWarner Losh# 327ca987d46SWarner Loshfind_file: mov DIR_EXTENT(%bx),%eax # Load extent 328ca987d46SWarner Losh xor %edx,%edx 329ca987d46SWarner Losh mov DIR_EA_LEN(%bx),%dl 330ca987d46SWarner Losh add %edx,%eax # Skip extended attributes 331ca987d46SWarner Losh mov %eax,rec_lba # Save LBA 332ca987d46SWarner Losh mov DIR_SIZE(%bx),%eax # Save size 333ca987d46SWarner Losh mov %eax,rec_size 334ca987d46SWarner Losh xor %cl,%cl # Zero length 335ca987d46SWarner Losh push %si # Save 336ca987d46SWarner Loshff.namelen: inc %cl # Update length 337ca987d46SWarner Losh lodsb # Read char 338ca987d46SWarner Losh cmp $0,%al # Nul? 339ca987d46SWarner Losh je ff.namedone # Yes 340ca987d46SWarner Losh cmp $'/',%al # Path separator? 341ca987d46SWarner Losh jnz ff.namelen # No, keep going 342ca987d46SWarner Loshff.namedone: dec %cl # Adjust length and save 343ca987d46SWarner Losh mov %cl,name_len 344ca987d46SWarner Losh pop %si # Restore 345ca987d46SWarner Loshff.load: mov rec_lba,%eax # Load LBA 346ca987d46SWarner Losh mov $MEM_DIR,%ebx # Address buffer 347ca987d46SWarner Losh mov $1,%dh # One sector 348ca987d46SWarner Losh call read # Read directory block 349ca987d46SWarner Losh incl rec_lba # Update LBA to next block 350ca987d46SWarner Loshff.scan: mov %ebx,%edx # Check for EOF 351ca987d46SWarner Losh sub $MEM_DIR,%edx 352ca987d46SWarner Losh cmp %edx,rec_size 353ca987d46SWarner Losh ja ff.scan.1 354ca987d46SWarner Losh stc # EOF reached 355ca987d46SWarner Losh ret 356ca987d46SWarner Loshff.scan.1: cmpb $0,DIR_LEN(%bx) # Last record in block? 357ca987d46SWarner Losh je ff.nextblock 358ca987d46SWarner Losh push %si # Save 359ca987d46SWarner Losh movzbw DIR_NAMELEN(%bx),%si # Find end of string 360ca987d46SWarner Loshff.checkver: cmpb $'0',DIR_NAME-1(%bx,%si) # Less than '0'? 361ca987d46SWarner Losh jb ff.checkver.1 362ca987d46SWarner Losh cmpb $'9',DIR_NAME-1(%bx,%si) # Greater than '9'? 363ca987d46SWarner Losh ja ff.checkver.1 364ca987d46SWarner Losh dec %si # Next char 365ca987d46SWarner Losh jnz ff.checkver 366ca987d46SWarner Losh jmp ff.checklen # All numbers in name, so 367ca987d46SWarner Losh # no version 368ca987d46SWarner Loshff.checkver.1: movzbw DIR_NAMELEN(%bx),%cx 369ca987d46SWarner Losh cmp %cx,%si # Did we find any digits? 370ca987d46SWarner Losh je ff.checkdot # No 371ca987d46SWarner Losh cmpb $';',DIR_NAME-1(%bx,%si) # Check for semicolon 372ca987d46SWarner Losh jne ff.checkver.2 373ca987d46SWarner Losh dec %si # Skip semicolon 374ca987d46SWarner Losh mov %si,%cx 375ca987d46SWarner Losh mov %cl,DIR_NAMELEN(%bx) # Adjust length 376ca987d46SWarner Losh jmp ff.checkdot 377ca987d46SWarner Loshff.checkver.2: mov %cx,%si # Restore %si to end of string 378ca987d46SWarner Loshff.checkdot: cmpb $'.',DIR_NAME-1(%bx,%si) # Trailing dot? 379ca987d46SWarner Losh jne ff.checklen # No 380ca987d46SWarner Losh decb DIR_NAMELEN(%bx) # Adjust length 381ca987d46SWarner Loshff.checklen: pop %si # Restore 382ca987d46SWarner Losh movzbw name_len,%cx # Load length of name 383ca987d46SWarner Losh cmp %cl,DIR_NAMELEN(%bx) # Does length match? 384ca987d46SWarner Losh je ff.checkname # Yes, check name 385ca987d46SWarner Loshff.nextrec: add DIR_LEN(%bx),%bl # Next record 386ca987d46SWarner Losh adc $0,%bh 387ca987d46SWarner Losh jmp ff.scan 388ca987d46SWarner Loshff.nextblock: subl $SECTOR_SIZE,rec_size # Adjust size 389ca987d46SWarner Losh jnc ff.load # If subtract ok, keep going 390ca987d46SWarner Losh ret # End of file, so not found 391ca987d46SWarner Loshff.checkname: lea DIR_NAME(%bx),%di # Address name in record 392ca987d46SWarner Losh push %si # Save 393ca987d46SWarner Losh repe cmpsb # Compare name 394ca987d46SWarner Losh je ff.match # We have a winner! 395ca987d46SWarner Losh pop %si # Restore 396ca987d46SWarner Losh jmp ff.nextrec # Keep looking. 397ca987d46SWarner Loshff.match: add $2,%sp # Discard saved %si 398ca987d46SWarner Losh clc # Clear carry 399ca987d46SWarner Losh ret 400ca987d46SWarner Losh 401ca987d46SWarner Losh# 402ca987d46SWarner Losh# Load DH sectors starting at LBA EAX into [EBX]. 403ca987d46SWarner Losh# 404ca987d46SWarner Losh# Trashes: EAX 405ca987d46SWarner Losh# 406ca987d46SWarner Loshread: push %si # Save 407ca987d46SWarner Losh push %cx # Save since some BIOSs trash 408ca987d46SWarner Losh mov %eax,edd_lba # LBA to read from 409ca987d46SWarner Losh mov %ebx,%eax # Convert address 410ca987d46SWarner Losh shr $4,%eax # to segment 411ca987d46SWarner Losh mov %ax,edd_addr+0x2 # and store 412ca987d46SWarner Loshread.retry: call twiddle # Entertain the user 413ca987d46SWarner Losh push %dx # Save 414ca987d46SWarner Losh mov $edd_packet,%si # Address Packet 415ca987d46SWarner Losh mov %dh,edd_len # Set length 416ca987d46SWarner Losh mov drive,%dl # BIOS Device 417ca987d46SWarner Losh mov $0x42,%ah # BIOS: Extended Read 418ca987d46SWarner Losh int $0x13 # Call BIOS 419ca987d46SWarner Losh pop %dx # Restore 420ca987d46SWarner Losh jc read.fail # Worked? 421ca987d46SWarner Losh pop %cx # Restore 422ca987d46SWarner Losh pop %si 423ca987d46SWarner Losh ret # Return 424ca987d46SWarner Loshread.fail: cmp $ERROR_TIMEOUT,%ah # Timeout? 425ca987d46SWarner Losh je read.retry # Yes, Retry. 426ca987d46SWarner Loshread.error: mov %ah,%al # Save error 427ca987d46SWarner Losh mov $hex_error,%di # Format it 428ca987d46SWarner Losh call hex8 # as hex 429ca987d46SWarner Losh mov $msg_badread,%si # Display Read error message 430ca987d46SWarner Losh 431ca987d46SWarner Losh# 432ca987d46SWarner Losh# Display error message at [SI] and halt. 433ca987d46SWarner Losh# 434ca987d46SWarner Losherror: call putstr # Display message 435ca987d46SWarner Loshhalt: hlt 436ca987d46SWarner Losh jmp halt # Spin 437ca987d46SWarner Losh 438ca987d46SWarner Losh# 439ca987d46SWarner Losh# Display a null-terminated string. 440ca987d46SWarner Losh# 441ca987d46SWarner Losh# Trashes: AX, SI 442ca987d46SWarner Losh# 443ca987d46SWarner Loshputstr: push %bx # Save 444ca987d46SWarner Loshputstr.load: lodsb # load %al from %ds:(%si) 445ca987d46SWarner Losh test %al,%al # stop at null 446ca987d46SWarner Losh jnz putstr.putc # if the char != null, output it 447ca987d46SWarner Losh pop %bx # Restore 448ca987d46SWarner Losh ret # return when null is hit 449ca987d46SWarner Loshputstr.putc: call putc # output char 450ca987d46SWarner Losh jmp putstr.load # next char 451ca987d46SWarner Losh 452ca987d46SWarner Losh# 453ca987d46SWarner Losh# Display a single char. 454ca987d46SWarner Losh# 455ca987d46SWarner Loshputc: mov $0x7,%bx # attribute for output 456ca987d46SWarner Losh mov $0xe,%ah # BIOS: put_char 457ca987d46SWarner Losh int $0x10 # call BIOS, print char in %al 458ca987d46SWarner Losh ret # Return to caller 459ca987d46SWarner Losh 460ca987d46SWarner Losh# 461ca987d46SWarner Losh# Output the "twiddle" 462ca987d46SWarner Losh# 463ca987d46SWarner Loshtwiddle: push %ax # Save 464ca987d46SWarner Losh push %bx # Save 465ca987d46SWarner Losh mov twiddle_index,%al # Load index 466ca987d46SWarner Losh mov $twiddle_chars,%bx # Address table 467ca987d46SWarner Losh inc %al # Next 468ca987d46SWarner Losh and $3,%al # char 469ca987d46SWarner Losh mov %al,twiddle_index # Save index for next call 470ca987d46SWarner Losh xlat # Get char 471ca987d46SWarner Losh call putc # Output it 472ca987d46SWarner Losh mov $8,%al # Backspace 473ca987d46SWarner Losh call putc # Output it 474ca987d46SWarner Losh pop %bx # Restore 475ca987d46SWarner Losh pop %ax # Restore 476ca987d46SWarner Losh ret 477ca987d46SWarner Losh 478ca987d46SWarner Losh# 479ca987d46SWarner Losh# Enable A20. Put an upper limit on the amount of time we wait for the 480ca987d46SWarner Losh# keyboard controller to get ready (65K x ISA access time). If 481ca987d46SWarner Losh# we wait more than that amount, the hardware is probably 482ca987d46SWarner Losh# legacy-free and simply doesn't have a keyboard controller. 483ca987d46SWarner Losh# Thus, the A20 line is already enabled. 484ca987d46SWarner Losh# 485ca987d46SWarner Loshseta20: cli # Disable interrupts 486ca987d46SWarner Losh xor %cx,%cx # Clear 487ca987d46SWarner Loshseta20.1: inc %cx # Increment, overflow? 488ca987d46SWarner Losh jz seta20.3 # Yes 489ca987d46SWarner Losh in $0x64,%al # Get status 490ca987d46SWarner Losh test $0x2,%al # Busy? 491ca987d46SWarner Losh jnz seta20.1 # Yes 492ca987d46SWarner Losh mov $0xd1,%al # Command: Write 493ca987d46SWarner Losh out %al,$0x64 # output port 494ca987d46SWarner Loshseta20.2: in $0x64,%al # Get status 495ca987d46SWarner Losh test $0x2,%al # Busy? 496ca987d46SWarner Losh jnz seta20.2 # Yes 497ca987d46SWarner Losh mov $0xdf,%al # Enable 498ca987d46SWarner Losh out %al,$0x60 # A20 499ca987d46SWarner Loshseta20.3: sti # Enable interrupts 500ca987d46SWarner Losh ret # To caller 501ca987d46SWarner Losh 502ca987d46SWarner Losh# 503ca987d46SWarner Losh# Convert AL to hex, saving the result to [EDI]. 504ca987d46SWarner Losh# 505ca987d46SWarner Loshhex8: pushl %eax # Save 506ca987d46SWarner Losh shrb $0x4,%al # Do upper 507ca987d46SWarner Losh call hex8.1 # 4 508ca987d46SWarner Losh popl %eax # Restore 509ca987d46SWarner Loshhex8.1: andb $0xf,%al # Get lower 4 510ca987d46SWarner Losh cmpb $0xa,%al # Convert 511ca987d46SWarner Losh sbbb $0x69,%al # to hex 512ca987d46SWarner Losh das # digit 513ca987d46SWarner Losh orb $0x20,%al # To lower case 514ca987d46SWarner Losh stosb # Save char 515ca987d46SWarner Losh ret # (Recursive) 516ca987d46SWarner Losh 517ca987d46SWarner Losh# 518ca987d46SWarner Losh# BTX client to start btxldr 519ca987d46SWarner Losh# 520ca987d46SWarner Losh .code32 521ca987d46SWarner Loshbtx_client: mov $(MEM_ARG_BTX-MEM_BTX_CLIENT+MEM_ARG_SIZE-4), %esi 522ca987d46SWarner Losh # %ds:(%esi) -> end 523ca987d46SWarner Losh # of boot[12] args 524ca987d46SWarner Losh mov $(MEM_ARG_SIZE/4),%ecx # Number of words to push 525ca987d46SWarner Losh std # Go backwards 526ca987d46SWarner Loshpush_arg: lodsl # Read argument 527ca987d46SWarner Losh push %eax # Push it onto the stack 528ca987d46SWarner Losh loop push_arg # Push all of the arguments 529ca987d46SWarner Losh cld # In case anyone depends on this 530ca987d46SWarner Losh pushl MEM_ARG_BTX-MEM_BTX_CLIENT+MEM_ARG_SIZE # Entry point of 531ca987d46SWarner Losh # the loader 532ca987d46SWarner Losh push %eax # Emulate a near call 533ca987d46SWarner Losh mov $0x1,%eax # 'exec' system call 534ca987d46SWarner Losh int $INT_SYS # BTX system call 535ca987d46SWarner Loshbtx_client_end: 536ca987d46SWarner Losh .code16 537ca987d46SWarner Losh 538ca987d46SWarner Losh .p2align 4 539ca987d46SWarner Losh# 540ca987d46SWarner Losh# Global descriptor table. 541ca987d46SWarner Losh# 542ca987d46SWarner Loshgdt: .word 0x0,0x0,0x0,0x0 # Null entry 543ca987d46SWarner Losh .word 0xffff,0x0,0x9200,0xcf # SEL_SDATA 544ca987d46SWarner Losh .word 0xffff,0x0,0x9200,0x0 # SEL_RDATA 545ca987d46SWarner Losh .word 0xffff,0x0,0x9a00,0xcf # SEL_SCODE (32-bit) 546ca987d46SWarner Losh .word 0xffff,0x0,0x9a00,0x8f # SEL_SCODE16 (16-bit) 547ca987d46SWarner Loshgdt.1: 548ca987d46SWarner Losh# 549ca987d46SWarner Losh# Pseudo-descriptors. 550ca987d46SWarner Losh# 551ca987d46SWarner Loshgdtdesc: .word gdt.1-gdt-1 # Limit 552ca987d46SWarner Losh .long gdt # Base 553ca987d46SWarner Losh# 554ca987d46SWarner Losh# EDD Packet 555ca987d46SWarner Losh# 556ca987d46SWarner Loshedd_packet: .byte 0x10 # Length 557ca987d46SWarner Losh .byte 0 # Reserved 558ca987d46SWarner Loshedd_len: .byte 0x0 # Num to read 559ca987d46SWarner Losh .byte 0 # Reserved 560ca987d46SWarner Loshedd_addr: .word 0x0,0x0 # Seg:Off 561ca987d46SWarner Loshedd_lba: .quad 0x0 # LBA 562ca987d46SWarner Losh 563ca987d46SWarner Loshdrive: .byte 0 564ca987d46SWarner Losh 565ca987d46SWarner Losh# 566ca987d46SWarner Losh# State for searching dir 567ca987d46SWarner Losh# 568ca987d46SWarner Loshrec_lba: .long 0x0 # LBA (adjusted for EA) 569ca987d46SWarner Loshrec_size: .long 0x0 # File size 570ca987d46SWarner Loshname_len: .byte 0x0 # Length of current name 571ca987d46SWarner Losh 572ca987d46SWarner Loshtwiddle_index: .byte 0x0 573ca987d46SWarner Losh 574ca987d46SWarner Loshmsg_welcome: .asciz "CD Loader 1.2\r\n\n" 575ca987d46SWarner Loshmsg_bootinfo: .asciz "Building the boot loader arguments\r\n" 576ca987d46SWarner Loshmsg_relocate: .asciz "Relocating the loader and the BTX\r\n" 577ca987d46SWarner Loshmsg_jump: .asciz "Starting the BTX loader\r\n" 578ca987d46SWarner Loshmsg_badread: .ascii "Read Error: 0x" 579ca987d46SWarner Loshhex_error: .asciz "00\r\n" 580ca987d46SWarner Loshmsg_novd: .asciz "Could not find Primary Volume Descriptor\r\n" 581ca987d46SWarner Loshmsg_lookup: .asciz "Looking up " 582ca987d46SWarner Loshmsg_lookup2: .asciz "... " 583ca987d46SWarner Loshmsg_lookupok: .asciz "Found\r\n" 584ca987d46SWarner Loshmsg_lookupfail: .asciz "File not found\r\n" 585ca987d46SWarner Loshmsg_load2big: .asciz "File too big\r\n" 586ca987d46SWarner Loshmsg_failed: .asciz "Boot failed\r\n" 587ca987d46SWarner Loshtwiddle_chars: .ascii "|/-\\" 588ca987d46SWarner Loshloader_paths: .asciz "/BOOT/LOADER" 589ca987d46SWarner Losh .asciz "/boot/loader" 590ca987d46SWarner Losh .byte 0 591ca987d46SWarner Losh 592