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