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