cdboot.S revision 124445
10SN/A# 29330SN/A# Copyright (c) 2001 John Baldwin 30SN/A# All rights reserved. 40SN/A# 50SN/A# Redistribution and use in source and binary forms are freely 60SN/A# permitted provided that the above copyright notice and this 72362SN/A# paragraph and the following disclaimer are duplicated in all 80SN/A# such forms. 92362SN/A# 100SN/A# This software is provided "AS IS" and without any express or 110SN/A# implied warranties, including, without limitation, the implied 120SN/A# warranties of merchantability and fitness for a particular 130SN/A# purpose. 140SN/A# 150SN/A 160SN/A# $FreeBSD: head/sys/boot/i386/cdboot/cdboot.s 124445 2004-01-12 20:34:42Z jhb $ 170SN/A 180SN/A# 190SN/A# This program is a freestanding boot program to load an a.out binary 200SN/A# from a CD-ROM booted with no emulation mode as described by the El 212362SN/A# Torito standard. Due to broken BIOSen that do not load the desired 222362SN/A# number of sectors, we try to fit this in as small a space as possible. 232362SN/A# 240SN/A# Basically, we first create a set of boot arguments to pass to the loaded 250SN/A# binary. Then we attempt to load /boot/loader from the CD we were booted 260SN/A# off of. 270SN/A# 28430SN/A 290SN/A# 300SN/A# Memory locations. 310SN/A# 320SN/A .set MEM_PAGE_SIZE,0x1000 # memory page size, 4k 330SN/A .set MEM_ARG,0x900 # Arguments at start 340SN/A .set MEM_ARG_BTX,0xa100 # Where we move them to so the 350SN/A # BTX client can see them 360SN/A .set MEM_ARG_SIZE,0x18 # Size of the arguments 37430SN/A .set MEM_BTX_ADDRESS,0x9000 # where BTX lives 380SN/A .set MEM_BTX_ENTRY,0x9010 # where BTX starts to execute 390SN/A .set MEM_BTX_OFFSET,MEM_PAGE_SIZE # offset of BTX in the loader 400SN/A .set MEM_BTX_CLIENT,0xa000 # where BTX clients live 410SN/A# 420SN/A# a.out header fields 430SN/A# 440SN/A .set AOUT_TEXT,0x04 # text segment size 450SN/A .set AOUT_DATA,0x08 # data segment size 460SN/A .set AOUT_BSS,0x0c # zero'd BSS size 470SN/A .set AOUT_SYMBOLS,0x10 # symbol table 480SN/A .set AOUT_ENTRY,0x14 # entry point 490SN/A .set AOUT_HEADER,MEM_PAGE_SIZE # size of the a.out header 500SN/A# 510SN/A# Flags for kargs->bootflags 520SN/A# 530SN/A .set KARGS_FLAGS_CD,0x1 # flag to indicate booting from 540SN/A # CD loader 550SN/A# 56430SN/A# Segment selectors. 570SN/A# 58430SN/A .set SEL_SDATA,0x8 # Supervisor data 59430SN/A .set SEL_RDATA,0x10 # Real mode data 600SN/A .set SEL_SCODE,0x18 # PM-32 code 610SN/A .set SEL_SCODE16,0x20 # PM-16 code 620SN/A# 630SN/A# BTX constants 640SN/A# 650SN/A .set INT_SYS,0x30 # BTX syscall interrupt 660SN/A# 670SN/A# Constants for reading from the CD. 680SN/A# 690SN/A .set ERROR_TIMEOUT,0x80 # BIOS timeout on read 700SN/A .set NUM_RETRIES,3 # Num times to retry 710SN/A .set SECTOR_SIZE,0x800 # size of a sector 720SN/A .set SECTOR_SHIFT,11 # number of place to shift 730SN/A .set BUFFER_LEN,0x100 # number of sectors in buffer 74430SN/A .set MAX_READ,0x10000 # max we can read at a time 75430SN/A .set MAX_READ_SEC,MAX_READ >> SECTOR_SHIFT 76430SN/A .set MEM_READ_BUFFER,0x9000 # buffer to read from CD 77430SN/A .set MEM_VOLDESC,MEM_READ_BUFFER # volume descriptor 780SN/A .set MEM_DIR,MEM_VOLDESC+SECTOR_SIZE # Lookup buffer 790SN/A .set VOLDESC_LBA,0x10 # LBA of vol descriptor 800SN/A .set VD_PRIMARY,1 # Primary VD 810SN/A .set VD_END,255 # VD Terminator 820SN/A .set VD_ROOTDIR,156 # Offset of Root Dir Record 830SN/A .set DIR_LEN,0 # Offset of Dir Record length 84430SN/A .set DIR_EA_LEN,1 # Offset of EA length 850SN/A .set DIR_EXTENT,2 # Offset of 64-bit LBA 860SN/A .set DIR_SIZE,10 # Offset of 64-bit length 870SN/A .set DIR_NAMELEN,32 # Offset of 8-bit name len 880SN/A .set DIR_NAME,33 # Offset of dir name 890SN/A# 900SN/A# We expect to be loaded by the BIOS at 0x7c00 (standard boot loader entry 910SN/A# point) 920SN/A# 930SN/A .code16 940SN/A .globl start 950SN/A .org 0x0, 0x0 960SN/A# 970SN/A# Program start. 980SN/A# 990SN/Astart: cld # string ops inc 1000SN/A xor %ax,%ax # zero %ax 1010SN/A mov %ax,%ss # setup the 1020SN/A mov $start,%sp # stack 1030SN/A mov %ax,%ds # setup the 1040SN/A mov %ax,%es # data segments 1050SN/A mov %dl,drive # Save BIOS boot device 1060SN/A mov $msg_welcome,%si # %ds:(%si) -> welcome message 1070SN/A call putstr # display the welcome message 1080SN/A# 1090SN/A# Setup the arguments that the loader is expecting from boot[12] 1100SN/A# 1110SN/A mov $msg_bootinfo,%si # %ds:(%si) -> boot args message 1120SN/A call putstr # display the message 1130SN/A mov $MEM_ARG,%bx # %ds:(%bx) -> boot args 1140SN/A mov %bx,%di # %es:(%di) -> boot args 1150SN/A xor %eax,%eax # zero %eax 1160SN/A mov $(MEM_ARG_SIZE/4),%cx # Size of arguments in 32-bit 1170SN/A # dwords 1180SN/A rep # Clear the arguments 1190SN/A stosl # to zero 1200SN/A mov drive,%dl # Store BIOS boot device 1210SN/A mov %dl,0x4(%bx) # in kargs->bootdev 1220SN/A or $KARGS_FLAGS_CD,0x8(%bx) # kargs->bootflags |= 1230SN/A # KARGS_FLAGS_CD 1240SN/A# 1250SN/A# Load Volume Descriptor 1260SN/A# 1270SN/A mov $VOLDESC_LBA,%eax # Set LBA of first VD 1280SN/Aload_vd: push %eax # Save %eax 1290SN/A mov $1,%dh # One sector 1300SN/A mov $MEM_VOLDESC,%ebx # Destination 1310SN/A call read # Read it in 1320SN/A cmpb $VD_PRIMARY,(%bx) # Primary VD? 1330SN/A je have_vd # Yes 1340SN/A pop %eax # Prepare to 1350SN/A inc %eax # try next 1360SN/A cmpb $VD_END,(%bx) # Last VD? 1370SN/A jne load_vd # No, read next 1380SN/A mov $msg_novd,%si # No VD 1390SN/A jmp error # Halt 1400SN/Ahave_vd: # Have Primary VD 1410SN/A# 1420SN/A# Try to look up the loader binary using the paths in the loader_paths 1430SN/A# array. 1440SN/A# 1450SN/A mov $loader_paths,%si # Point to start of array 1460SN/Alookup_path: push %si # Save file name pointer 1470SN/A call lookup # Try to find file 1480SN/A pop %di # Restore file name pointer 1490SN/A jnc lookup_found # Found this file 1500SN/A xor %al,%al # Look for next 1510SN/A mov $0xffff,%cx # path name by 1520SN/A repnz # scanning for 1530SN/A scasb # nul char 1540SN/A inc %di # Skip nul 1550SN/A mov %di,%si # Point %si at next path 1560SN/A mov (%si),%al # Get first char of next path 1570SN/A or %al,%al # Is it double nul? 1580SN/A jnz lookup_path # No, try it. 1590SN/A mov $msg_failed,%si # Failed message 1600SN/A jmp error # Halt 1610SN/Alookup_found: # Found a loader file 1620SN/A# 1630SN/A# Load the binary into the buffer. Due to real mode addressing limitations 1640SN/A# we have to read it in in 64k chunks. 1650SN/A# 1660SN/A mov DIR_SIZE(%bx),%eax # Read file length 1670SN/A add $SECTOR_SIZE-1,%eax # Convert length to sectors 1680SN/A shr $SECTOR_SHIFT,%eax 1690SN/A cmp $BUFFER_LEN,%eax 1700SN/A jbe load_sizeok 1710SN/A mov $msg_load2big,%si # Error message 1720SN/A call error 1730SN/Aload_sizeok: movzbw %al,%cx # Num sectors to read 1740SN/A mov DIR_EXTENT(%bx),%eax # Load extent 1750SN/A xor %edx,%edx 1760SN/A mov DIR_EA_LEN(%bx),%dl 1770SN/A add %edx,%eax # Skip extended 1780SN/A mov $MEM_READ_BUFFER,%ebx # Read into the buffer 1790SN/Aload_loop: mov %cl,%dh 1800SN/A cmp $MAX_READ_SEC,%cl # Truncate to max read size 1810SN/A jbe load_notrunc 1820SN/A mov $MAX_READ_SEC,%dh 1830SN/Aload_notrunc: sub %dh,%cl # Update count 1840SN/A push %eax # Save 1850SN/A call read # Read it in 1860SN/A pop %eax # Restore 1870SN/A add $MAX_READ_SEC,%eax # Update LBA 1880SN/A add $MAX_READ,%ebx # Update dest addr 1890SN/A jcxz load_done # Done? 1900SN/A jmp load_loop # Keep going 1910SN/Aload_done: 1920SN/A# 1930SN/A# Turn on the A20 address line 1940SN/A# 1950SN/A call seta20 # Turn A20 on 1960SN/A# 1970SN/A# Relocate the loader and BTX using a very lazy protected mode 1980SN/A# 1990SN/A mov $msg_relocate,%si # Display the 2000SN/A call putstr # relocation message 2010SN/A mov MEM_READ_BUFFER+AOUT_ENTRY,%edi # %edi is the destination 2020SN/A mov $(MEM_READ_BUFFER+AOUT_HEADER),%esi # %esi is 2030SN/A # the start of the text 2040SN/A # segment 2050SN/A mov MEM_READ_BUFFER+AOUT_TEXT,%ecx # %ecx = length of the text 2060SN/A # segment 2070SN/A push %edi # Save entry point for later 2080SN/A lgdt gdtdesc # setup our own gdt 2090SN/A cli # turn off interrupts 2100SN/A mov %cr0,%eax # Turn on 2110SN/A or $0x1,%al # protected 2120SN/A mov %eax,%cr0 # mode 2130SN/A ljmp $SEL_SCODE,$pm_start # long jump to clear the 2140SN/A # instruction pre-fetch queue 2150SN/A .code32 2160SN/Apm_start: mov $SEL_SDATA,%ax # Initialize 2170SN/A mov %ax,%ds # %ds and 2180SN/A mov %ax,%es # %es to a flat selector 2190SN/A rep # Relocate the 2200SN/A movsb # text segment 2210SN/A add $(MEM_PAGE_SIZE - 1),%edi # pad %edi out to a new page 2220SN/A and $~(MEM_PAGE_SIZE - 1),%edi # for the data segment 2230SN/A mov MEM_READ_BUFFER+AOUT_DATA,%ecx # size of the data segment 2240SN/A rep # Relocate the 2250SN/A movsb # data segment 2260SN/A mov MEM_READ_BUFFER+AOUT_BSS,%ecx # size of the bss 2278565SN/A xor %eax,%eax # zero %eax 2280SN/A add $3,%cl # round %ecx up to 2290SN/A shr $2,%ecx # a multiple of 4 2300SN/A rep # zero the 2310SN/A stosl # bss 2320SN/A mov MEM_READ_BUFFER+AOUT_ENTRY,%esi # %esi -> relocated loader 2330SN/A add $MEM_BTX_OFFSET,%esi # %esi -> BTX in the loader 2340SN/A mov $MEM_BTX_ADDRESS,%edi # %edi -> where BTX needs to go 2350SN/A movzwl 0xa(%esi),%ecx # %ecx -> length of BTX 2360SN/A rep # Relocate 2370SN/A movsb # BTX 2380SN/A ljmp $SEL_SCODE16,$pm_16 # Jump to 16-bit PM 2390SN/A .code16 2400SN/Apm_16: mov $SEL_RDATA,%ax # Initialize 2410SN/A mov %ax,%ds # %ds and 2420SN/A mov %ax,%es # %es to a real mode selector 2430SN/A mov %cr0,%eax # Turn off 2440SN/A and $~0x1,%al # protected 2450SN/A mov %eax,%cr0 # mode 2460SN/A ljmp $0,$pm_end # Long jump to clear the 2470SN/A # instruction pre-fetch queue 2480SN/Apm_end: sti # Turn interrupts back on now 2490SN/A# 2500SN/A# Copy the BTX client to MEM_BTX_CLIENT 2510SN/A# 2520SN/A xor %ax,%ax # zero %ax and set 2530SN/A mov %ax,%ds # %ds and %es 2540SN/A mov %ax,%es # to segment 0 2550SN/A mov $MEM_BTX_CLIENT,%di # Prepare to relocate 2560SN/A mov $btx_client,%si # the simple btx client 2570SN/A mov $(btx_client_end-btx_client),%cx # length of btx client 2580SN/A rep # Relocate the 2590SN/A movsb # simple BTX client 2600SN/A# 2610SN/A# Copy the boot[12] args to where the BTX client can see them 2620SN/A# 2630SN/A mov $MEM_ARG,%si # where the args are at now 2640SN/A mov $MEM_ARG_BTX,%di # where the args are moving to 2650SN/A mov $(MEM_ARG_SIZE/4),%cx # size of the arguments in longs 2660SN/A rep # Relocate 2670SN/A movsl # the words 2680SN/A# 2690SN/A# Save the entry point so the client can get to it later on 2700SN/A# 2710SN/A pop %eax # Restore saved entry point 2720SN/A stosl # and add it to the end of 2730SN/A # the arguments 2740SN/A# 2750SN/A# Now we just start up BTX and let it do the rest 2760SN/A# 2770SN/A mov $msg_jump,%si # Display the 2780SN/A call putstr # jump message 2790SN/A ljmp $0,$MEM_BTX_ENTRY # Jump to the BTX entry point 2800SN/A 2810SN/A# 2820SN/A# Lookup the file in the path at [SI] from the root directory. 2830SN/A# 2840SN/A# Trashes: All but BX 2850SN/A# Returns: CF = 0 (success), BX = pointer to record 2860SN/A# CF = 1 (not found) 2870SN/A# 2880SN/Alookup: mov $VD_ROOTDIR+MEM_VOLDESC,%bx # Root directory record 2890SN/A push %si 2900SN/A mov $msg_lookup,%si # Display lookup message 2910SN/A call putstr 2920SN/A pop %si 2930SN/A push %si 2940SN/A call putstr 2950SN/A mov $msg_lookup2,%si 2960SN/A call putstr 2970SN/A pop %si 2980SN/Alookup_dir: lodsb # Get first char of path 2990SN/A cmp $0,%al # Are we done? 3000SN/A je lookup_done # Yes 3010SN/A cmp $'/',%al # Skip path separator. 3020SN/A je lookup_dir 3030SN/A dec %si # Undo lodsb side effect 3040SN/A call find_file # Lookup first path item 3050SN/A jnc lookup_dir # Try next component 3060SN/A mov $msg_lookupfail,%si # Not found message 3070SN/A call putstr 3080SN/A stc # Set carry 3090SN/A ret 3100SN/A jmp error 3110SN/Alookup_done: mov $msg_lookupok,%si # Success message 3120SN/A call putstr 3130SN/A clc # Clear carry 3140SN/A ret 3150SN/A 3160SN/A# 3170SN/A# Lookup file at [SI] in directory whose record is at [BX]. 3180SN/A# 3190SN/A# Trashes: All but returns 3200SN/A# Returns: CF = 0 (success), BX = pointer to record, SI = next path item 3210SN/A# CF = 1 (not found), SI = preserved 3220SN/A# 3230SN/Afind_file: mov DIR_EXTENT(%bx),%eax # Load extent 3240SN/A xor %edx,%edx 3250SN/A mov DIR_EA_LEN(%bx),%dl 3260SN/A add %edx,%eax # Skip extended attributes 3270SN/A mov %eax,rec_lba # Save LBA 3280SN/A mov DIR_SIZE(%bx),%eax # Save size 3290SN/A mov %eax,rec_size 3300SN/A xor %cl,%cl # Zero length 3310SN/A push %si # Save 3320SN/Aff.namelen: inc %cl # Update length 3330SN/A lodsb # Read char 3340SN/A cmp $0,%al # Nul? 3350SN/A je ff.namedone # Yes 3360SN/A cmp $'/',%al # Path separator? 3370SN/A jnz ff.namelen # No, keep going 3380SN/Aff.namedone: dec %cl # Adjust length and save 3390SN/A mov %cl,name_len 3400SN/A pop %si # Restore 3410SN/Aff.load: mov rec_lba,%eax # Load LBA 3420SN/A mov $MEM_DIR,%ebx # Address buffer 3430SN/A mov $1,%dh # One sector 3440SN/A call read # Read directory block 3450SN/A incl rec_lba # Update LBA to next block 3460SN/Aff.scan: mov %ebx,%edx # Check for EOF 3470SN/A sub $MEM_DIR,%edx 3480SN/A cmp %edx,rec_size 3490SN/A ja ff.scan.1 3500SN/A stc # EOF reached 3510SN/A ret 3520SN/Aff.scan.1: cmpb $0,DIR_LEN(%bx) # Last record in block? 3530SN/A je ff.nextblock 3540SN/A push %si # Save 3550SN/A movzbw DIR_NAMELEN(%bx),%si # Find end of string 3560SN/Aff.checkver: cmpb $'0',DIR_NAME-1(%bx,%si) # Less than '0'? 3570SN/A jb ff.checkver.1 3580SN/A cmpb $'9',DIR_NAME-1(%bx,%si) # Greater than '9'? 3590SN/A ja ff.checkver.1 3600SN/A dec %si # Next char 3610SN/A jnz ff.checkver 3620SN/A jmp ff.checklen # All numbers in name, so 3630SN/A # no version 3640SN/Aff.checkver.1: movzbw DIR_NAMELEN(%bx),%cx 3650SN/A cmp %cx,%si # Did we find any digits? 3660SN/A je ff.checkdot # No 3670SN/A cmpb $';',DIR_NAME-1(%bx,%si) # Check for semicolon 3680SN/A jne ff.checkver.2 3690SN/A dec %si # Skip semicolon 3700SN/A mov %si,%cx 3710SN/A mov %cl,DIR_NAMELEN(%bx) # Adjust length 3720SN/A jmp ff.checkdot 3730SN/Aff.checkver.2: mov %cx,%si # Restore %si to end of string 3740SN/Aff.checkdot: cmpb $'.',DIR_NAME-1(%bx,%si) # Trailing dot? 3750SN/A jne ff.checklen # No 3760SN/A decb DIR_NAMELEN(%bx) # Adjust length 3770SN/Aff.checklen: pop %si # Restore 3780SN/A movzbw name_len,%cx # Load length of name 3790SN/A cmp %cl,DIR_NAMELEN(%bx) # Does length match? 3800SN/A je ff.checkname # Yes, check name 3810SN/Aff.nextrec: add DIR_LEN(%bx),%bl # Next record 3820SN/A adc $0,%bh 3830SN/A jmp ff.scan 3840SN/Aff.nextblock: subl $SECTOR_SIZE,rec_size # Adjust size 3850SN/A jnc ff.load # If subtract ok, keep going 3860SN/A ret # End of file, so not found 3870SN/Aff.checkname: lea DIR_NAME(%bx),%di # Address name in record 3880SN/A push %si # Save 3890SN/A repe cmpsb # Compare name 3900SN/A jcxz ff.match # We have a winner! 3910SN/A pop %si # Restore 3920SN/A jmp ff.nextrec # Keep looking. 3930SN/Aff.match: add $2,%sp # Discard saved %si 3940SN/A clc # Clear carry 3950SN/A ret 3960SN/A 3970SN/A# 3980SN/A# Load DH sectors starting at LBA EAX into [EBX]. 3990SN/A# 4000SN/A# Trashes: EAX 4010SN/A# 4020SN/Aread: push %si # Save 4030SN/A push %cx # Save since some BIOSs trash 4040SN/A mov %eax,edd_lba # LBA to read from 4050SN/A mov %ebx,%eax # Convert address 4060SN/A shr $4,%eax # to segment 4070SN/A mov %ax,edd_addr+0x2 # and store 4080SN/Aread.retry: call twiddle # Entertain the user 4090SN/A push %dx # Save 410430SN/A mov $edd_packet,%si # Address Packet 4114190SN/A mov %dh,edd_len # Set length 4124190SN/A mov drive,%dl # BIOS Device 413430SN/A mov $0x42,%ah # BIOS: Extended Read 414430SN/A int $0x13 # Call BIOS 415430SN/A pop %dx # Restore 416430SN/A jc read.fail # Worked? 417430SN/A pop %cx # Restore 418430SN/A pop %si 419430SN/A ret # Return 420430SN/Aread.fail: cmp $ERROR_TIMEOUT,%ah # Timeout? 421430SN/A je read.retry # Yes, Retry. 422430SN/Aread.error: mov %ah,%al # Save error 423430SN/A mov $hex_error,%di # Format it 424430SN/A call hex8 # as hex 425430SN/A mov $msg_badread,%si # Display Read error message 426430SN/A 427430SN/A# 428430SN/A# Display error message at [SI] and halt. 429430SN/A# 430430SN/Aerror: call putstr # Display message 431430SN/Ahalt: hlt 432430SN/A jmp halt # Spin 433430SN/A 4344190SN/A# 4354190SN/A# Display a null-terminated string. 436430SN/A# 437430SN/A# Trashes: AX, SI 438430SN/A# 439430SN/Aputstr: push %bx # Save 440430SN/Aputstr.load: lodsb # load %al from %ds:(%si) 441430SN/A test %al,%al # stop at null 442430SN/A jnz putstr.putc # if the char != null, output it 443430SN/A pop %bx # Restore 444430SN/A ret # return when null is hit 445430SN/Aputstr.putc: call putc # output char 446430SN/A jmp putstr.load # next char 447430SN/A 448430SN/A# 449430SN/A# Display a single char. 450430SN/A# 451430SN/Aputc: mov $0x7,%bx # attribute for output 452430SN/A mov $0xe,%ah # BIOS: put_char 453430SN/A int $0x10 # call BIOS, print char in %al 454430SN/A ret # Return to caller 455430SN/A 456430SN/A# 457430SN/A# Output the "twiddle" 458430SN/A# 459430SN/Atwiddle: push %ax # Save 460430SN/A push %bx # Save 4614190SN/A mov twiddle_index,%al # Load index 4624190SN/A mov twiddle_chars,%bx # Address table 463430SN/A inc %al # Next 464430SN/A and $3,%al # char 465430SN/A mov %al,twiddle_index # Save index for next call 466430SN/A xlat # Get char 467430SN/A call putc # Output it 468430SN/A mov $8,%al # Backspace 469430SN/A call putc # Output it 470430SN/A pop %bx # Restore 471430SN/A pop %ax # Restore 472430SN/A ret 473430SN/A 474430SN/A# 475430SN/A# Enable A20 476430SN/A# 477430SN/Aseta20: cli # Disable interrupts 478430SN/Aseta20.1: in $0x64,%al # Get status 479430SN/A test $0x2,%al # Busy? 480430SN/A jnz seta20.1 # Yes 481430SN/A mov $0xd1,%al # Command: Write 482430SN/A out %al,$0x64 # output port 483430SN/Aseta20.2: in $0x64,%al # Get status 4844190SN/A test $0x2,%al # Busy? 4854190SN/A jnz seta20.2 # Yes 486430SN/A mov $0xdf,%al # Enable 487430SN/A out %al,$0x60 # A20 488430SN/A sti # Enable interrupts 489430SN/A ret # To caller 490430SN/A 491430SN/A# 492430SN/A# Convert AL to hex, saving the result to [EDI]. 493430SN/A# 494430SN/Ahex8: pushl %eax # Save 495430SN/A shrb $0x4,%al # Do upper 496430SN/A call hex8.1 # 4 497430SN/A popl %eax # Restore 498430SN/Ahex8.1: andb $0xf,%al # Get lower 4 499430SN/A cmpb $0xa,%al # Convert 500430SN/A sbbb $0x69,%al # to hex 501430SN/A das # digit 502430SN/A orb $0x20,%al # To lower case 503430SN/A stosb # Save char 504430SN/A ret # (Recursive) 505430SN/A 506430SN/A# 507430SN/A# BTX client to start btxldr 508430SN/A# 509430SN/A .code32 5100SN/Abtx_client: mov $(MEM_ARG_BTX-MEM_BTX_CLIENT+MEM_ARG_SIZE-4), %esi 5114768SN/A # %ds:(%esi) -> end 5120SN/A # of boot[12] args 5134768SN/A mov $(MEM_ARG_SIZE/4),%ecx # Number of words to push 5140SN/A std # Go backwards 5150SN/Apush_arg: lodsl # Read argument 5160SN/A push %eax # Push it onto the stack 5170SN/A loop push_arg # Push all of the arguments 5180SN/A cld # In case anyone depends on this 5190SN/A pushl MEM_ARG_BTX-MEM_BTX_CLIENT+MEM_ARG_SIZE # Entry point of 5200SN/A # the loader 5214768SN/A push %eax # Emulate a near call 5220SN/A mov $0x1,%eax # 'exec' system call 5230SN/A int $INT_SYS # BTX system call 5240SN/Abtx_client_end: 5250SN/A .code16 5260SN/A 5270SN/A .p2align 4 5280SN/A# 5290SN/A# Global descriptor table. 5300SN/A# 5310SN/Agdt: .word 0x0,0x0,0x0,0x0 # Null entry 5320SN/A .word 0xffff,0x0,0x9200,0xcf # SEL_SDATA 5330SN/A .word 0xffff,0x0,0x9200,0x0 # SEL_RDATA 5340SN/A .word 0xffff,0x0,0x9a00,0xcf # SEL_SCODE (32-bit) 5354768SN/A .word 0xffff,0x0,0x9a00,0x8f # SEL_SCODE16 (16-bit) 5360SN/Agdt.1: 5370SN/A# 5380SN/A# Pseudo-descriptors. 5390SN/A# 5400SN/Agdtdesc: .word gdt.1-gdt-1 # Limit 5410SN/A .long gdt # Base 5420SN/A# 5430SN/A# EDD Packet 5440SN/A# 5450SN/Aedd_packet: .byte 0x10 # Length 5460SN/A .byte 0 # Reserved 5470SN/Aedd_len: .byte 0x0 # Num to read 5480SN/A .byte 0 # Reserved 5490SN/Aedd_addr: .word 0x0,0x0 # Seg:Off 5504768SN/Aedd_lba: .quad 0x0 # LBA 5510SN/A 5520SN/Adrive: .byte 0 5530SN/A 5544768SN/A# 5550SN/A# State for searching dir 5560SN/A# 5570SN/Arec_lba: .long 0x0 # LBA (adjusted for EA) 5580SN/Arec_size: .long 0x0 # File size 5590SN/Aname_len: .byte 0x0 # Length of current name 5600SN/A 5610SN/Atwiddle_index: .byte 0x0 5620SN/A 5630SN/Amsg_welcome: .asciz "CD Loader 1.2\r\n\n" 5640SN/Amsg_bootinfo: .asciz "Building the boot loader arguments\r\n" 5650SN/Amsg_relocate: .asciz "Relocating the loader and the BTX\r\n" 5660SN/Amsg_jump: .asciz "Starting the BTX loader\r\n" 5670SN/Amsg_badread: .ascii "Read Error: 0x" 5680SN/Ahex_error: .ascii "00\r\n" 5690SN/Amsg_novd: .asciz "Could not find Primary Volume Descriptor\r\n" 5700SN/Amsg_lookup: .asciz "Looking up " 5710SN/Amsg_lookup2: .asciz "... " 5724768SN/Amsg_lookupok: .asciz "Found\r\n" 5730SN/Amsg_lookupfail: .asciz "File not found\r\n" 5740SN/Amsg_load2big: .asciz "File too big\r\n" 5750SN/Amsg_failed: .asciz "Boot failed\r\n" 5760SN/Atwiddle_chars: .ascii "|/-\\" 5770SN/Aloader_paths: .asciz "/BOOT/LOADER" 5780SN/A .asciz "/boot/loader" 5790SN/A .byte 0 5800SN/A 5810SN/A