cdboot.S revision 130943
1178479Sjb# 2178479Sjb# Copyright (c) 2001 John Baldwin <jhb@FreeBSD.org> 3178479Sjb# All rights reserved. 4178479Sjb# 5178479Sjb# Redistribution and use in source and binary forms, with or without 6178479Sjb# modification, are permitted provided that the following conditions 7178479Sjb# are met: 8178479Sjb# 1. Redistributions of source code must retain the above copyright 9178479Sjb# notice, this list of conditions and the following disclaimer. 10178479Sjb# 2. Redistributions in binary form must reproduce the above copyright 11178479Sjb# notice, this list of conditions and the following disclaimer in the 12178479Sjb# documentation and/or other materials provided with the distribution. 13178479Sjb# 3. Neither the name of the author nor the names of any co-contributors 14178479Sjb# may be used to endorse or promote products derived from this software 15178479Sjb# without specific prior written permission. 16178479Sjb# 17178479Sjb# THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 18178479Sjb# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 19178479Sjb# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 20178479Sjb# ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 21178566Sjb# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 22178479Sjb# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 23178566Sjb# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 24239536Spfg# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 25178479Sjb# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 26178479Sjb# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 27178479Sjb# SUCH DAMAGE. 28178479Sjb# 29178479Sjb 30178479Sjb# $FreeBSD: head/sys/boot/i386/cdboot/cdboot.s 130943 2004-06-22 21:55:22Z jhb $ 31178566Sjb 32178479Sjb# 33178566Sjb# This program is a freestanding boot program to load an a.out binary 34239536Spfg# from a CD-ROM booted with no emulation mode as described by the El 35178479Sjb# Torito standard. Due to broken BIOSen that do not load the desired 36178479Sjb# number of sectors, we try to fit this in as small a space as possible. 37178479Sjb# 38239536Spfg# Basically, we first create a set of boot arguments to pass to the loaded 39239536Spfg# binary. Then we attempt to load /boot/loader from the CD we were booted 40239536Spfg# off of. 41178479Sjb# 42178479Sjb 43178479Sjb# 44178479Sjb# Memory locations. 45178479Sjb# 46178479Sjb .set MEM_PAGE_SIZE,0x1000 # memory page size, 4k 47178479Sjb .set MEM_ARG,0x900 # Arguments at start 48178479Sjb .set MEM_ARG_BTX,0xa100 # Where we move them to so the 49178479Sjb # BTX client can see them 50178479Sjb .set MEM_ARG_SIZE,0x18 # Size of the arguments 51178479Sjb .set MEM_BTX_ADDRESS,0x9000 # where BTX lives 52178479Sjb .set MEM_BTX_ENTRY,0x9010 # where BTX starts to execute 53178479Sjb .set MEM_BTX_OFFSET,MEM_PAGE_SIZE # offset of BTX in the loader 54178479Sjb .set MEM_BTX_CLIENT,0xa000 # where BTX clients live 55178479Sjb# 56178479Sjb# a.out header fields 57178479Sjb# 58178479Sjb .set AOUT_TEXT,0x04 # text segment size 59178479Sjb .set AOUT_DATA,0x08 # data segment size 60178479Sjb .set AOUT_BSS,0x0c # zero'd BSS size 61178479Sjb .set AOUT_SYMBOLS,0x10 # symbol table 62178479Sjb .set AOUT_ENTRY,0x14 # entry point 63178479Sjb .set AOUT_HEADER,MEM_PAGE_SIZE # size of the a.out header 64178479Sjb# 65178479Sjb# Flags for kargs->bootflags 66178479Sjb# 67178479Sjb .set KARGS_FLAGS_CD,0x1 # flag to indicate booting from 68178479Sjb # CD loader 69178479Sjb# 70178479Sjb# Segment selectors. 71178479Sjb# 72178479Sjb .set SEL_SDATA,0x8 # Supervisor data 73178479Sjb .set SEL_RDATA,0x10 # Real mode data 74178479Sjb .set SEL_SCODE,0x18 # PM-32 code 75178479Sjb .set SEL_SCODE16,0x20 # PM-16 code 76178479Sjb# 77178479Sjb# BTX constants 78178479Sjb# 79178479Sjb .set INT_SYS,0x30 # BTX syscall interrupt 80178479Sjb# 81178479Sjb# Constants for reading from the CD. 82178479Sjb# 83178479Sjb .set ERROR_TIMEOUT,0x80 # BIOS timeout on read 84178479Sjb .set NUM_RETRIES,3 # Num times to retry 85178479Sjb .set SECTOR_SIZE,0x800 # size of a sector 86178479Sjb .set SECTOR_SHIFT,11 # number of place to shift 87178479Sjb .set BUFFER_LEN,0x100 # number of sectors in buffer 88178479Sjb .set MAX_READ,0x10000 # max we can read at a time 89178479Sjb .set MAX_READ_SEC,MAX_READ >> SECTOR_SHIFT 90178479Sjb .set MEM_READ_BUFFER,0x9000 # buffer to read from CD 91178479Sjb .set MEM_VOLDESC,MEM_READ_BUFFER # volume descriptor 92178479Sjb .set MEM_DIR,MEM_VOLDESC+SECTOR_SIZE # Lookup buffer 93178479Sjb .set VOLDESC_LBA,0x10 # LBA of vol descriptor 94178479Sjb .set VD_PRIMARY,1 # Primary VD 95178479Sjb .set VD_END,255 # VD Terminator 96178479Sjb .set VD_ROOTDIR,156 # Offset of Root Dir Record 97178479Sjb .set DIR_LEN,0 # Offset of Dir Record length 98178479Sjb .set DIR_EA_LEN,1 # Offset of EA length 99178479Sjb .set DIR_EXTENT,2 # Offset of 64-bit LBA 100178479Sjb .set DIR_SIZE,10 # Offset of 64-bit length 101178479Sjb .set DIR_NAMELEN,32 # Offset of 8-bit name len 102178479Sjb .set DIR_NAME,33 # Offset of dir name 103178479Sjb# 104178479Sjb# We expect to be loaded by the BIOS at 0x7c00 (standard boot loader entry 105178479Sjb# point) 106178479Sjb# 107178479Sjb .code16 108178479Sjb .globl start 109178479Sjb .org 0x0, 0x0 110178479Sjb# 111178479Sjb# Program start. 112178479Sjb# 113178479Sjbstart: cld # string ops inc 114178479Sjb xor %ax,%ax # zero %ax 115178479Sjb mov %ax,%ss # setup the 116178479Sjb mov $start,%sp # stack 117178479Sjb mov %ax,%ds # setup the 118178479Sjb mov %ax,%es # data segments 119178479Sjb mov %dl,drive # Save BIOS boot device 120178479Sjb mov $msg_welcome,%si # %ds:(%si) -> welcome message 121178479Sjb call putstr # display the welcome message 122178479Sjb# 123178479Sjb# Setup the arguments that the loader is expecting from boot[12] 124178479Sjb# 125178479Sjb mov $msg_bootinfo,%si # %ds:(%si) -> boot args message 126178479Sjb call putstr # display the message 127178479Sjb mov $MEM_ARG,%bx # %ds:(%bx) -> boot args 128178479Sjb mov %bx,%di # %es:(%di) -> boot args 129178479Sjb xor %eax,%eax # zero %eax 130178479Sjb mov $(MEM_ARG_SIZE/4),%cx # Size of arguments in 32-bit 131178479Sjb # dwords 132178479Sjb rep # Clear the arguments 133178479Sjb stosl # to zero 134178479Sjb mov drive,%dl # Store BIOS boot device 135178479Sjb mov %dl,0x4(%bx) # in kargs->bootdev 136178479Sjb or $KARGS_FLAGS_CD,0x8(%bx) # kargs->bootflags |= 137178479Sjb # KARGS_FLAGS_CD 138178479Sjb# 139178479Sjb# Load Volume Descriptor 140178479Sjb# 141178479Sjb mov $VOLDESC_LBA,%eax # Set LBA of first VD 142178479Sjbload_vd: push %eax # Save %eax 143178479Sjb mov $1,%dh # One sector 144178479Sjb mov $MEM_VOLDESC,%ebx # Destination 145178479Sjb call read # Read it in 146178479Sjb cmpb $VD_PRIMARY,(%bx) # Primary VD? 147178479Sjb je have_vd # Yes 148178479Sjb pop %eax # Prepare to 149178479Sjb inc %eax # try next 150178479Sjb cmpb $VD_END,(%bx) # Last VD? 151178479Sjb jne load_vd # No, read next 152178479Sjb mov $msg_novd,%si # No VD 153178479Sjb jmp error # Halt 154178479Sjbhave_vd: # Have Primary VD 155178479Sjb# 156178479Sjb# Try to look up the loader binary using the paths in the loader_paths 157178479Sjb# array. 158178479Sjb# 159178479Sjb mov $loader_paths,%si # Point to start of array 160178479Sjblookup_path: push %si # Save file name pointer 161178479Sjb call lookup # Try to find file 162178479Sjb pop %di # Restore file name pointer 163178479Sjb jnc lookup_found # Found this file 164178479Sjb xor %al,%al # Look for next 165178479Sjb mov $0xffff,%cx # path name by 166178479Sjb repnz # scanning for 167178479Sjb scasb # nul char 168178479Sjb mov %di,%si # Point %si at next path 169178479Sjb mov (%si),%al # Get first char of next path 170178479Sjb or %al,%al # Is it double nul? 171178479Sjb jnz lookup_path # No, try it. 172178479Sjb mov $msg_failed,%si # Failed message 173178479Sjb jmp error # Halt 174178479Sjblookup_found: # Found a loader file 175178479Sjb# 176178479Sjb# Load the binary into the buffer. Due to real mode addressing limitations 177178479Sjb# we have to read it in in 64k chunks. 178178479Sjb# 179178479Sjb mov DIR_SIZE(%bx),%eax # Read file length 180178479Sjb add $SECTOR_SIZE-1,%eax # Convert length to sectors 181178479Sjb shr $SECTOR_SHIFT,%eax 182178479Sjb cmp $BUFFER_LEN,%eax 183178479Sjb jbe load_sizeok 184178479Sjb mov $msg_load2big,%si # Error message 185178479Sjb call error 186178479Sjbload_sizeok: movzbw %al,%cx # Num sectors to read 187178479Sjb mov DIR_EXTENT(%bx),%eax # Load extent 188178479Sjb xor %edx,%edx 189178479Sjb mov DIR_EA_LEN(%bx),%dl 190178479Sjb add %edx,%eax # Skip extended 191178479Sjb mov $MEM_READ_BUFFER,%ebx # Read into the buffer 192178479Sjbload_loop: mov %cl,%dh 193178479Sjb cmp $MAX_READ_SEC,%cl # Truncate to max read size 194178479Sjb jbe load_notrunc 195178479Sjb mov $MAX_READ_SEC,%dh 196178479Sjbload_notrunc: sub %dh,%cl # Update count 197178479Sjb push %eax # Save 198178479Sjb call read # Read it in 199178479Sjb pop %eax # Restore 200178479Sjb add $MAX_READ_SEC,%eax # Update LBA 201178479Sjb add $MAX_READ,%ebx # Update dest addr 202178479Sjb jcxz load_done # Done? 203178479Sjb jmp load_loop # Keep going 204178479Sjbload_done: 205178479Sjb# 206178479Sjb# Turn on the A20 address line 207178479Sjb# 208239536Spfg call seta20 # Turn A20 on 209239536Spfg# 210239536Spfg# Relocate the loader and BTX using a very lazy protected mode 211239536Spfg# 212239536Spfg mov $msg_relocate,%si # Display the 213239536Spfg call putstr # relocation message 214239536Spfg mov MEM_READ_BUFFER+AOUT_ENTRY,%edi # %edi is the destination 215239536Spfg mov $(MEM_READ_BUFFER+AOUT_HEADER),%esi # %esi is 216239536Spfg # the start of the text 217239536Spfg # segment 218239536Spfg mov MEM_READ_BUFFER+AOUT_TEXT,%ecx # %ecx = length of the text 219239536Spfg # segment 220239536Spfg push %edi # Save entry point for later 221239536Spfg lgdt gdtdesc # setup our own gdt 222239536Spfg cli # turn off interrupts 223239536Spfg mov %cr0,%eax # Turn on 224239536Spfg or $0x1,%al # protected 225239536Spfg mov %eax,%cr0 # mode 226239536Spfg ljmp $SEL_SCODE,$pm_start # long jump to clear the 227239536Spfg # instruction pre-fetch queue 228239536Spfg .code32 229239536Spfgpm_start: mov $SEL_SDATA,%ax # Initialize 230239536Spfg mov %ax,%ds # %ds and 231178479Sjb mov %ax,%es # %es to a flat selector 232178479Sjb rep # Relocate the 233178479Sjb movsb # text segment 234178479Sjb add $(MEM_PAGE_SIZE - 1),%edi # pad %edi out to a new page 235178479Sjb and $~(MEM_PAGE_SIZE - 1),%edi # for the data segment 236178479Sjb mov MEM_READ_BUFFER+AOUT_DATA,%ecx # size of the data segment 237178479Sjb rep # Relocate the 238178479Sjb movsb # data segment 239178479Sjb mov MEM_READ_BUFFER+AOUT_BSS,%ecx # size of the bss 240178479Sjb xor %eax,%eax # zero %eax 241178479Sjb add $3,%cl # round %ecx up to 242178479Sjb shr $2,%ecx # a multiple of 4 243178566Sjb rep # zero the 244251857Sgnn stosl # bss 245251857Sgnn mov MEM_READ_BUFFER+AOUT_ENTRY,%esi # %esi -> relocated loader 246178479Sjb add $MEM_BTX_OFFSET,%esi # %esi -> BTX in the loader 247178479Sjb mov $MEM_BTX_ADDRESS,%edi # %edi -> where BTX needs to go 248178479Sjb movzwl 0xa(%esi),%ecx # %ecx -> length of BTX 249178479Sjb rep # Relocate 250178479Sjb movsb # BTX 251178479Sjb ljmp $SEL_SCODE16,$pm_16 # Jump to 16-bit PM 252178479Sjb .code16 253251857Sgnnpm_16: mov $SEL_RDATA,%ax # Initialize 254251857Sgnn mov %ax,%ds # %ds and 255251857Sgnn mov %ax,%es # %es to a real mode selector 256251857Sgnn mov %cr0,%eax # Turn off 257251857Sgnn and $~0x1,%al # protected 258251857Sgnn mov %eax,%cr0 # mode 259251857Sgnn ljmp $0,$pm_end # Long jump to clear the 260251857Sgnn # instruction pre-fetch queue 261251857Sgnnpm_end: sti # Turn interrupts back on now 262251857Sgnn# 263251857Sgnn# Copy the BTX client to MEM_BTX_CLIENT 264251857Sgnn# 265251857Sgnn xor %ax,%ax # zero %ax and set 266251857Sgnn mov %ax,%ds # %ds and %es 267251857Sgnn mov %ax,%es # to segment 0 268251857Sgnn mov $MEM_BTX_CLIENT,%di # Prepare to relocate 269251857Sgnn mov $btx_client,%si # the simple btx client 270251857Sgnn mov $(btx_client_end-btx_client),%cx # length of btx client 271251857Sgnn rep # Relocate the 272251857Sgnn movsb # simple BTX client 273251857Sgnn# 274251857Sgnn# Copy the boot[12] args to where the BTX client can see them 275251857Sgnn# 276251857Sgnn mov $MEM_ARG,%si # where the args are at now 277251857Sgnn mov $MEM_ARG_BTX,%di # where the args are moving to 278178479Sjb mov $(MEM_ARG_SIZE/4),%cx # size of the arguments in longs 279178479Sjb rep # Relocate 280178479Sjb movsl # the words 281178479Sjb# 282178566Sjb# Save the entry point so the client can get to it later on 283178479Sjb# 284239536Spfg pop %eax # Restore saved entry point 285239536Spfg stosl # and add it to the end of 286239536Spfg # the arguments 287178479Sjb# 288178479Sjb# Now we just start up BTX and let it do the rest 289178479Sjb# 290178479Sjb mov $msg_jump,%si # Display the 291178479Sjb call putstr # jump message 292178479Sjb ljmp $0,$MEM_BTX_ENTRY # Jump to the BTX entry point 293178479Sjb 294178566Sjb# 295178479Sjb# Lookup the file in the path at [SI] from the root directory. 296178479Sjb# 297178566Sjb# Trashes: All but BX 298178566Sjb# Returns: CF = 0 (success), BX = pointer to record 299178566Sjb# CF = 1 (not found) 300178566Sjb# 301178566Sjblookup: mov $VD_ROOTDIR+MEM_VOLDESC,%bx # Root directory record 302178566Sjb push %si 303178566Sjb mov $msg_lookup,%si # Display lookup message 304178566Sjb call putstr 305178566Sjb pop %si 306178566Sjb push %si 307178566Sjb call putstr 308178566Sjb mov $msg_lookup2,%si 309178566Sjb call putstr 310178566Sjb pop %si 311178566Sjblookup_dir: lodsb # Get first char of path 312239536Spfg cmp $0,%al # Are we done? 313239536Spfg je lookup_done # Yes 314178566Sjb cmp $'/',%al # Skip path separator. 315178566Sjb je lookup_dir 316178566Sjb dec %si # Undo lodsb side effect 317178566Sjb call find_file # Lookup first path item 318178566Sjb jnc lookup_dir # Try next component 319178566Sjb mov $msg_lookupfail,%si # Not found message 320178566Sjb call putstr 321178566Sjb stc # Set carry 322178479Sjb ret 323178566Sjb jmp error 324178566Sjblookup_done: mov $msg_lookupok,%si # Success message 325178479Sjb call putstr 326178479Sjb clc # Clear carry 327178479Sjb ret 328178479Sjb 329178479Sjb# 330178479Sjb# Lookup file at [SI] in directory whose record is at [BX]. 331178479Sjb# 332178479Sjb# Trashes: All but returns 333178479Sjb# Returns: CF = 0 (success), BX = pointer to record, SI = next path item 334178479Sjb# CF = 1 (not found), SI = preserved 335178479Sjb# 336178479Sjbfind_file: mov DIR_EXTENT(%bx),%eax # Load extent 337178479Sjb xor %edx,%edx 338178479Sjb mov DIR_EA_LEN(%bx),%dl 339178479Sjb add %edx,%eax # Skip extended attributes 340178479Sjb mov %eax,rec_lba # Save LBA 341178479Sjb mov DIR_SIZE(%bx),%eax # Save size 342178479Sjb mov %eax,rec_size 343178479Sjb xor %cl,%cl # Zero length 344178479Sjb push %si # Save 345178479Sjbff.namelen: inc %cl # Update length 346178479Sjb lodsb # Read char 347178479Sjb cmp $0,%al # Nul? 348178479Sjb je ff.namedone # Yes 349178479Sjb cmp $'/',%al # Path separator? 350178479Sjb jnz ff.namelen # No, keep going 351178479Sjbff.namedone: dec %cl # Adjust length and save 352178479Sjb mov %cl,name_len 353178479Sjb pop %si # Restore 354178479Sjbff.load: mov rec_lba,%eax # Load LBA 355178479Sjb mov $MEM_DIR,%ebx # Address buffer 356178479Sjb mov $1,%dh # One sector 357178479Sjb call read # Read directory block 358178479Sjb incl rec_lba # Update LBA to next block 359178479Sjbff.scan: mov %ebx,%edx # Check for EOF 360178479Sjb sub $MEM_DIR,%edx 361178479Sjb cmp %edx,rec_size 362178479Sjb ja ff.scan.1 363178479Sjb stc # EOF reached 364178479Sjb ret 365178479Sjbff.scan.1: cmpb $0,DIR_LEN(%bx) # Last record in block? 366178479Sjb je ff.nextblock 367178479Sjb push %si # Save 368178479Sjb movzbw DIR_NAMELEN(%bx),%si # Find end of string 369178479Sjbff.checkver: cmpb $'0',DIR_NAME-1(%bx,%si) # Less than '0'? 370178479Sjb jb ff.checkver.1 371178479Sjb cmpb $'9',DIR_NAME-1(%bx,%si) # Greater than '9'? 372178479Sjb ja ff.checkver.1 373178479Sjb dec %si # Next char 374178479Sjb jnz ff.checkver 375178479Sjb jmp ff.checklen # All numbers in name, so 376178479Sjb # no version 377178479Sjbff.checkver.1: movzbw DIR_NAMELEN(%bx),%cx 378178479Sjb cmp %cx,%si # Did we find any digits? 379178479Sjb je ff.checkdot # No 380178479Sjb cmpb $';',DIR_NAME-1(%bx,%si) # Check for semicolon 381178479Sjb jne ff.checkver.2 382178479Sjb dec %si # Skip semicolon 383178479Sjb mov %si,%cx 384178479Sjb mov %cl,DIR_NAMELEN(%bx) # Adjust length 385178479Sjb jmp ff.checkdot 386178479Sjbff.checkver.2: mov %cx,%si # Restore %si to end of string 387178479Sjbff.checkdot: cmpb $'.',DIR_NAME-1(%bx,%si) # Trailing dot? 388178479Sjb jne ff.checklen # No 389178479Sjb decb DIR_NAMELEN(%bx) # Adjust length 390178479Sjbff.checklen: pop %si # Restore 391178479Sjb movzbw name_len,%cx # Load length of name 392178479Sjb cmp %cl,DIR_NAMELEN(%bx) # Does length match? 393178479Sjb je ff.checkname # Yes, check name 394178479Sjbff.nextrec: add DIR_LEN(%bx),%bl # Next record 395178479Sjb adc $0,%bh 396178479Sjb jmp ff.scan 397178479Sjbff.nextblock: subl $SECTOR_SIZE,rec_size # Adjust size 398178479Sjb jnc ff.load # If subtract ok, keep going 399178479Sjb ret # End of file, so not found 400178479Sjbff.checkname: lea DIR_NAME(%bx),%di # Address name in record 401178479Sjb push %si # Save 402178479Sjb repe cmpsb # Compare name 403178479Sjb jcxz ff.match # We have a winner! 404178479Sjb pop %si # Restore 405178479Sjb jmp ff.nextrec # Keep looking. 406178479Sjbff.match: add $2,%sp # Discard saved %si 407178479Sjb clc # Clear carry 408178479Sjb ret 409178479Sjb 410178479Sjb# 411178479Sjb# Load DH sectors starting at LBA EAX into [EBX]. 412178479Sjb# 413178479Sjb# Trashes: EAX 414178479Sjb# 415178479Sjbread: push %si # Save 416178479Sjb push %cx # Save since some BIOSs trash 417178479Sjb mov %eax,edd_lba # LBA to read from 418178479Sjb mov %ebx,%eax # Convert address 419178479Sjb shr $4,%eax # to segment 420178479Sjb mov %ax,edd_addr+0x2 # and store 421178479Sjbread.retry: call twiddle # Entertain the user 422178479Sjb push %dx # Save 423178479Sjb mov $edd_packet,%si # Address Packet 424178479Sjb mov %dh,edd_len # Set length 425178479Sjb mov drive,%dl # BIOS Device 426178479Sjb mov $0x42,%ah # BIOS: Extended Read 427178479Sjb int $0x13 # Call BIOS 428178479Sjb pop %dx # Restore 429178479Sjb jc read.fail # Worked? 430178479Sjb pop %cx # Restore 431178479Sjb pop %si 432178479Sjb ret # Return 433178479Sjbread.fail: cmp $ERROR_TIMEOUT,%ah # Timeout? 434178479Sjb je read.retry # Yes, Retry. 435178479Sjbread.error: mov %ah,%al # Save error 436178479Sjb mov $hex_error,%di # Format it 437178479Sjb call hex8 # as hex 438178479Sjb mov $msg_badread,%si # Display Read error message 439178479Sjb 440178479Sjb# 441178479Sjb# Display error message at [SI] and halt. 442178479Sjb# 443178479Sjberror: call putstr # Display message 444178479Sjbhalt: hlt 445178479Sjb jmp halt # Spin 446178479Sjb 447178479Sjb# 448178479Sjb# Display a null-terminated string. 449178479Sjb# 450178479Sjb# Trashes: AX, SI 451178479Sjb# 452178479Sjbputstr: push %bx # Save 453178479Sjbputstr.load: lodsb # load %al from %ds:(%si) 454178479Sjb test %al,%al # stop at null 455178479Sjb jnz putstr.putc # if the char != null, output it 456178479Sjb pop %bx # Restore 457178479Sjb ret # return when null is hit 458178479Sjbputstr.putc: call putc # output char 459178479Sjb jmp putstr.load # next char 460178479Sjb 461178479Sjb# 462178479Sjb# Display a single char. 463178479Sjb# 464178479Sjbputc: mov $0x7,%bx # attribute for output 465178479Sjb mov $0xe,%ah # BIOS: put_char 466178479Sjb int $0x10 # call BIOS, print char in %al 467178479Sjb ret # Return to caller 468178479Sjb 469178479Sjb# 470178479Sjb# Output the "twiddle" 471178479Sjb# 472178479Sjbtwiddle: push %ax # Save 473178479Sjb push %bx # Save 474178479Sjb mov twiddle_index,%al # Load index 475178479Sjb mov twiddle_chars,%bx # Address table 476178479Sjb inc %al # Next 477178479Sjb and $3,%al # char 478178479Sjb mov %al,twiddle_index # Save index for next call 479178479Sjb xlat # Get char 480178479Sjb call putc # Output it 481178479Sjb mov $8,%al # Backspace 482178479Sjb call putc # Output it 483178479Sjb pop %bx # Restore 484178479Sjb pop %ax # Restore 485178479Sjb ret 486178479Sjb 487178479Sjb# 488178479Sjb# Enable A20 489178479Sjb# 490178479Sjbseta20: cli # Disable interrupts 491178479Sjbseta20.1: in $0x64,%al # Get status 492178479Sjb test $0x2,%al # Busy? 493178479Sjb jnz seta20.1 # Yes 494178479Sjb mov $0xd1,%al # Command: Write 495178479Sjb out %al,$0x64 # output port 496178479Sjbseta20.2: in $0x64,%al # Get status 497178479Sjb test $0x2,%al # Busy? 498178479Sjb jnz seta20.2 # Yes 499178479Sjb mov $0xdf,%al # Enable 500178479Sjb out %al,$0x60 # A20 501178479Sjb sti # Enable interrupts 502178479Sjb ret # To caller 503178479Sjb 504178479Sjb# 505178479Sjb# Convert AL to hex, saving the result to [EDI]. 506178479Sjb# 507178479Sjbhex8: pushl %eax # Save 508178479Sjb shrb $0x4,%al # Do upper 509178479Sjb call hex8.1 # 4 510178479Sjb popl %eax # Restore 511178479Sjbhex8.1: andb $0xf,%al # Get lower 4 512178479Sjb cmpb $0xa,%al # Convert 513178479Sjb sbbb $0x69,%al # to hex 514178479Sjb das # digit 515178479Sjb orb $0x20,%al # To lower case 516178479Sjb stosb # Save char 517178479Sjb ret # (Recursive) 518178479Sjb 519178479Sjb# 520178479Sjb# BTX client to start btxldr 521178479Sjb# 522178479Sjb .code32 523178479Sjbbtx_client: mov $(MEM_ARG_BTX-MEM_BTX_CLIENT+MEM_ARG_SIZE-4), %esi 524178479Sjb # %ds:(%esi) -> end 525178479Sjb # of boot[12] args 526178479Sjb mov $(MEM_ARG_SIZE/4),%ecx # Number of words to push 527178479Sjb std # Go backwards 528178479Sjbpush_arg: lodsl # Read argument 529178479Sjb push %eax # Push it onto the stack 530178479Sjb loop push_arg # Push all of the arguments 531178479Sjb cld # In case anyone depends on this 532178479Sjb pushl MEM_ARG_BTX-MEM_BTX_CLIENT+MEM_ARG_SIZE # Entry point of 533178479Sjb # the loader 534178479Sjb push %eax # Emulate a near call 535178479Sjb mov $0x1,%eax # 'exec' system call 536178479Sjb int $INT_SYS # BTX system call 537178479Sjbbtx_client_end: 538178479Sjb .code16 539178479Sjb 540178479Sjb .p2align 4 541178479Sjb# 542178479Sjb# Global descriptor table. 543178479Sjb# 544178479Sjbgdt: .word 0x0,0x0,0x0,0x0 # Null entry 545178479Sjb .word 0xffff,0x0,0x9200,0xcf # SEL_SDATA 546178479Sjb .word 0xffff,0x0,0x9200,0x0 # SEL_RDATA 547178479Sjb .word 0xffff,0x0,0x9a00,0xcf # SEL_SCODE (32-bit) 548178479Sjb .word 0xffff,0x0,0x9a00,0x8f # SEL_SCODE16 (16-bit) 549178479Sjbgdt.1: 550178479Sjb# 551178479Sjb# Pseudo-descriptors. 552178479Sjb# 553178479Sjbgdtdesc: .word gdt.1-gdt-1 # Limit 554178479Sjb .long gdt # Base 555178479Sjb# 556# EDD Packet 557# 558edd_packet: .byte 0x10 # Length 559 .byte 0 # Reserved 560edd_len: .byte 0x0 # Num to read 561 .byte 0 # Reserved 562edd_addr: .word 0x0,0x0 # Seg:Off 563edd_lba: .quad 0x0 # LBA 564 565drive: .byte 0 566 567# 568# State for searching dir 569# 570rec_lba: .long 0x0 # LBA (adjusted for EA) 571rec_size: .long 0x0 # File size 572name_len: .byte 0x0 # Length of current name 573 574twiddle_index: .byte 0x0 575 576msg_welcome: .asciz "CD Loader 1.2\r\n\n" 577msg_bootinfo: .asciz "Building the boot loader arguments\r\n" 578msg_relocate: .asciz "Relocating the loader and the BTX\r\n" 579msg_jump: .asciz "Starting the BTX loader\r\n" 580msg_badread: .ascii "Read Error: 0x" 581hex_error: .ascii "00\r\n" 582msg_novd: .asciz "Could not find Primary Volume Descriptor\r\n" 583msg_lookup: .asciz "Looking up " 584msg_lookup2: .asciz "... " 585msg_lookupok: .asciz "Found\r\n" 586msg_lookupfail: .asciz "File not found\r\n" 587msg_load2big: .asciz "File too big\r\n" 588msg_failed: .asciz "Boot failed\r\n" 589twiddle_chars: .ascii "|/-\\" 590loader_paths: .asciz "/BOOT/LOADER" 591 .asciz "/boot/loader" 592 .byte 0 593 594