1
0
mirror of https://git.FreeBSD.org/src.git synced 2024-12-17 10:26:15 +00:00

This is a new CD bootstrap utility designed to replace cdldr. According

to the El Torito standard for CD booting, a CD may boot in "No emulation"
mode without using a floppy image.  In this mode, the BIOS loads a program
off of the CD into memory and creates a BIOS device using 2048 byte sectors
for the CD.  According to the standard, this program can be up to 0xFFFF
virtual (512-byte) sectors long.  The old cdldr depended on this by having
the BIOS load the entire loader and the small cdldr stub as one binary
similar to pxeboot so that cdldr didn't have to read the CD to find the
loader.  However, the NT no emulation loader just uses 1 disk sector
(4 virtual sectors), so it seems that at least some BIOS writers just did
enough to get NT to boot by only loading 1 sector and ignoring the sector
count.  Thus, while cdldr should have worked in theory, it doesn't in
practice.  This replacment fits entirely in 1 sector and includes simple
ISO 9660 support.  It looks for /boot/loader on the CD and loads it up
using the BIOS.  This allows us to not have to depend on the limited size
of floppy images but use a full GENERIC kernel for CD-ROM installs in the
future, among other things.

This version of cdboot is a bit bloated as it includes some useful
debugging routines that people can pull to use in other x86 assembly
modules.  Even with all the debugging cruft, we still have 272 bytes to
spare.
This commit is contained in:
John Baldwin 2001-11-04 03:24:16 +00:00
parent f0699bb775
commit 5af9115c77
Notes: svn2git 2020-12-20 02:59:44 +00:00
svn path=/head/; revision=85998
2 changed files with 594 additions and 119 deletions

View File

@ -4,34 +4,20 @@ MAINTAINER=jhb@FreeBSD.org
ORG= 0x7c00
LDR= cdldr
BOOT= cdboot
PROG= ${BOOT}
PROG= cdboot
NOMAN=
STRIP=
BINDIR?= /boot
.if exists(${.OBJDIR}/../loader)
LOADER= ${.OBJDIR}/../loader/loader
.else
LOADER= ${.CURDIR}/../loader/loader
.endif
${BOOT}: ${LDR} ${LOADER}
cat ${LDR} ${LOADER} > ${.TARGET}.tmp
dd if=${.TARGET}.tmp of=${.TARGET} obs=2k conv=osync
rm ${.TARGET}.tmp
${LDR}: ${LDR}.o
${PROG}: ${PROG}.o
.if ${OBJFORMAT} == aout
${LD} -nostdlib -N -s -T ${ORG} -o ${LDR}.out ${LDR}.o
dd if=${LDR}.out of=${.TARGET} ibs=32 skip=1
${LD} -nostdlib -N -s -T ${ORG} -o ${PROG}.out ${PROG}.o
dd if=${PROG}.out of=${.TARGET} ibs=32 skip=1
.else
${LD} -N -e start -Ttext ${ORG} -o ${LDR}.out ${LDR}.o
objcopy -S -O binary ${LDR}.out ${.TARGET}
${LD} -N -e start -Ttext ${ORG} -o ${PROG}.out ${PROG}.o
objcopy -S -O binary ${PROG}.out ${.TARGET}
.endif
CLEANFILES+= ${LDR} ${LDR}.out ${LDR}.o ${BOOT}.tmp
CLEANFILES+= ${PROG}.o ${PROG}.out
.include <bsd.prog.mk>

View File

@ -1,5 +1,5 @@
#
# Copyright (c) 2000 John Baldwin
# Copyright (c) 2001 John Baldwin
# All rights reserved.
#
# Redistribution and use in source and binary forms are freely
@ -16,13 +16,14 @@
# $FreeBSD$
#
# This simple program is a preloader for the normal boot3 loader. It is simply
# prepended to the beginning of a fully built and btxld'd loader. It then
# copies the loader to the address boot2 normally loads it, emulates the
# boot[12] environment (protected mode, a bootinfo struct, etc.), and then jumps
# to the start of btxldr to start the boot process. This method allows a stock
# /boot/loader to be used w/o having to fully rewrite boot[12] to handle the
# cd9660 file system.
# This program is a freestanding boot program to load an a.out binary
# from a CD-ROM booted with no emulation mode as described by the El
# Torito standard. Due to broken BIOSen that do not load the desired
# number of sectors, we try to fit this in as small a space as possible.
#
# Basically, we first create a set of boot arguments to pass to the loaded
# binary. Then we attempt to load /boot/loader from the CD we were booted
# off of.
#
#
@ -63,6 +64,29 @@
#
.set INT_SYS,0x30 # BTX syscall interrupt
#
# Constants for reading from the CD.
#
.set ERROR_TIMEOUT,0x80 # BIOS timeout on read
.set NUM_RETRIES,3 # Num times to retry
.set SECTOR_SIZE,0x800 # size of a sector
.set SECTOR_SHIFT,11 # number of place to shift
.set BUFFER_LEN,0x100 # number of sectors in buffer
.set MAX_READ,0x10000 # max we can read at a time
.set MAX_READ_SEC,MAX_READ >> SECTOR_SHIFT
.set MEM_READ_BUFFER,0x9000 # buffer to read from CD
.set MEM_VOLDESC,MEM_READ_BUFFER # volume descriptor
.set MEM_DIR,MEM_VOLDESC+SECTOR_SIZE # Lookup buffer
.set VOLDESC_LBA,0x10 # LBA of vol descriptor
.set VD_PRIMARY,1 # Primary VD
.set VD_END,255 # VD Terminator
.set VD_ROOTDIR,156 # Offset of Root Dir Record
.set DIR_LEN,0 # Offset of Dir Record length
.set DIR_EA_LEN,1 # Offset of EA length
.set DIR_EXTENT,2 # Offset of 64-bit LBA
.set DIR_SIZE,10 # Offset of 64-bit length
.set DIR_NAMELEN,32 # Offset of 8-bit name len
.set DIR_NAME,33 # Offset of dir name
#
# We expect to be loaded by the BIOS at 0x7c00 (standard boot loader entry
# point)
#
@ -70,168 +94,579 @@
.globl start
.org 0x0, 0x0
#
# BTX program loader for CD booting
# Program start.
#
start: cld # string ops inc
xorw %ax, %ax # zero %ax
movw %ax, %ss # setup the
movw $start, %sp # stack
pushw %dx # save the BIOS boot device in
# %dl for later
movw %ax, %ds # setup the
movw %ax, %es # data segments
movw $welcome_msg, %si # %ds:(%si) -> welcome message
callw putstr # display the welcome message
xor %ax,%ax # zero %ax
mov %ax,%ss # setup the
mov $start,%sp # stack
mov %ax,%ds # setup the
mov %ax,%es # data segments
mov %dl,drive # Save BIOS boot device
mov $0xe3,%al
xor %dx,%dx
int $0x14 # Init COM1 9600,n,8,1
mov $msg_welcome,%si # %ds:(%si) -> welcome message
call putstr # display the welcome message
#
# Setup the arguments that the loader is expecting from boot[12]
#
movw $bootinfo_msg, %si # %ds:(%si) -> boot args message
callw putstr # display the message
movw $MEM_ARG, %bx # %ds:(%bx) -> boot args
movw %bx, %di # %es:(%di) -> boot args
xorl %eax, %eax # zero %eax
movw $(MEM_ARG_SIZE/4), %cx # Size of arguments in 32-bit
mov $msg_bootinfo,%si # %ds:(%si) -> boot args message
call putstr # display the message
mov $MEM_ARG,%bx # %ds:(%bx) -> boot args
mov %bx,%di # %es:(%di) -> boot args
xor %eax,%eax # zero %eax
mov $(MEM_ARG_SIZE/4),%cx # Size of arguments in 32-bit
# dwords
rep # Clear the arguments
stosl # to zero
popw %dx # restore BIOS boot device
movb %dl, 0x4(%bx) # set kargs->bootdev
orb $KARGS_FLAGS_CD, 0x8(%bx) # kargs->bootflags |=
mov drive,%dl # Store BIOS boot device
mov %dl,0x4(%bx) # in kargs->bootdev
or $KARGS_FLAGS_CD,0x8(%bx) # kargs->bootflags |=
# KARGS_FLAGS_CD
#
# Load Volume Descriptor
#
mov $VOLDESC_LBA,%eax # Set LBA of first VD
load_vd: push %eax # Save %eax
mov $1,%dh # One sector
mov $MEM_VOLDESC,%ebx # Destination
call read # Read it in
mov $16,%cx
call hexdump
cmpb $VD_PRIMARY,(%bx) # Primary VD?
je have_vd # Yes
pop %eax # Prepare to
inc %eax # try next
cmpb $VD_END,(%bx) # Last VD?
jne load_vd # No, read next
mov $msg_novd,%si # No VD
jmp error # Halt
have_vd: mov $msg_vd,%si # Have Primary VD
call putstr
#
# Lookup the loader binary.
#
mov $loader_path,%si # File to lookup
call lookup # Try to find it
mov $msg_lookup_done,%si
call putstr
#
# Load the binary into the buffer. Due to real mode addressing limitations
# we have to read it in in 64k chunks.
#
mov DIR_SIZE(%bx),%eax # Read file length
add $SECTOR_SIZE-1,%eax # Convert length to sectors
shr $11,%eax
cmp $BUFFER_LEN,%eax
jbe load_sizeok
mov $msg_load2big,%si # Error message
call error
load_sizeok: movzbw %al,%cx # Num sectors to read
mov DIR_EXTENT(%bx),%eax # Load extent
xor %edx,%edx
mov DIR_EA_LEN(%bx),%dl
add %edx,%eax # Skip extended
mov $MEM_READ_BUFFER,%ebx # Read into the buffer
load_loop: mov %cl,%dh
cmp $MAX_READ_SEC,%cl # Truncate to max read size
jbe load_notrunc
mov $MAX_READ_SEC,%dh
load_notrunc: sub %dh,%cl # Update count
push %eax # Save
call read # Read it in
pop %eax # Restore
add $MAX_READ_SEC,%eax # Update LBA
add $MAX_READ,%ebx # Update dest addr
jcxz load_done # Done?
jmp load_loop # Keep going
load_done:
#
# Turn on the A20 address line
#
callw seta20 # Turn A20 on
call seta20 # Turn A20 on
#
# Relocate the loader and BTX using a very lazy protected mode
#
movw $relocate_msg, %si # Display the
callw putstr # relocation message
movl end+AOUT_ENTRY, %edi # %edi is the destination
movl $(end+AOUT_HEADER), %esi # %esi is
mov $msg_relocate,%si # Display the
call putstr # relocation message
mov MEM_READ_BUFFER+AOUT_ENTRY,%edi # %edi is the destination
mov $(MEM_READ_BUFFER+AOUT_HEADER),%esi # %esi is
# the start of the text
# segment
movl end+AOUT_TEXT, %ecx # %ecx = length of the text
mov MEM_READ_BUFFER+AOUT_TEXT,%ecx # %ecx = length of the text
# segment
push %edi # Save entry point for later
lgdt gdtdesc # setup our own gdt
cli # turn off interrupts
movl %cr0, %eax # Turn on
orb $0x1, %al # protected
movl %eax, %cr0 # mode
mov %cr0,%eax # Turn on
or $0x1,%al # protected
mov %eax,%cr0 # mode
ljmp $SEL_SCODE,$pm_start # long jump to clear the
# instruction pre-fetch queue
.code32
pm_start: movw $SEL_SDATA, %ax # Initialize
movw %ax, %ds # %ds and
movw %ax, %es # %es to a flat selector
pm_start: mov $SEL_SDATA,%ax # Initialize
mov %ax,%ds # %ds and
mov %ax,%es # %es to a flat selector
rep # Relocate the
movsb # text segment
addl $(MEM_PAGE_SIZE - 1), %edi # pad %edi out to a new page
andl $~(MEM_PAGE_SIZE - 1), %edi # for the data segment
movl end+AOUT_DATA, %ecx # size of the data segment
add $(MEM_PAGE_SIZE - 1),%edi # pad %edi out to a new page
and $~(MEM_PAGE_SIZE - 1),%edi # for the data segment
mov MEM_READ_BUFFER+AOUT_DATA,%ecx # size of the data segment
rep # Relocate the
movsb # data segment
movl end+AOUT_BSS, %ecx # size of the bss
xorl %eax, %eax # zero %eax
addb $3, %cl # round %ecx up to
shrl $2, %ecx # a multiple of 4
mov MEM_READ_BUFFER+AOUT_BSS,%ecx # size of the bss
xor %eax,%eax # zero %eax
add $3,%cl # round %ecx up to
shr $2,%ecx # a multiple of 4
rep # zero the
stosl # bss
movl end+AOUT_ENTRY, %esi # %esi -> relocated loader
addl $MEM_BTX_OFFSET, %esi # %esi -> BTX in the loader
movl $MEM_BTX_ADDRESS, %edi # %edi -> where BTX needs to go
mov MEM_READ_BUFFER+AOUT_ENTRY,%esi # %esi -> relocated loader
add $MEM_BTX_OFFSET,%esi # %esi -> BTX in the loader
mov $MEM_BTX_ADDRESS,%edi # %edi -> where BTX needs to go
movzwl 0xa(%esi),%ecx # %ecx -> length of BTX
rep # Relocate
movsb # BTX
ljmp $SEL_SCODE16,$pm_16 # Jump to 16-bit PM
.code16
pm_16: movw $SEL_RDATA, %ax # Initialize
movw %ax, %ds # %ds and
movw %ax, %es # %es to a real mode selector
movl %cr0, %eax # Turn off
andb $~0x1, %al # protected
movl %eax, %cr0 # mode
pm_16: mov $SEL_RDATA,%ax # Initialize
mov %ax,%ds # %ds and
mov %ax,%es # %es to a real mode selector
mov %cr0,%eax # Turn off
and $~0x1,%al # protected
mov %eax,%cr0 # mode
ljmp $0,$pm_end # Long jump to clear the
# instruction pre-fetch queue
pm_end: sti # Turn interrupts back on now
#
# Copy the BTX client to MEM_BTX_CLIENT
#
xorw %ax, %ax # zero %ax and set
movw %ax, %ds # %ds and %es
movw %ax, %es # to segment 0
movw $MEM_BTX_CLIENT, %di # Prepare to relocate
movw $btx_client, %si # the simple btx client
movw $(btx_client_end-btx_client), %cx # length of btx client
xor %ax,%ax # zero %ax and set
mov %ax,%ds # %ds and %es
mov %ax,%es # to segment 0
mov $MEM_BTX_CLIENT,%di # Prepare to relocate
mov $btx_client,%si # the simple btx client
mov $(btx_client_end-btx_client),%cx # length of btx client
rep # Relocate the
movsb # simple BTX client
#
# Copy the boot[12] args to where the BTX client can see them
#
movw $MEM_ARG, %si # where the args are at now
movw $MEM_ARG_BTX, %di # where the args are moving to
movw $(MEM_ARG_SIZE/4), %cx # size of the arguments in longs
mov $MEM_ARG,%si # where the args are at now
mov $MEM_ARG_BTX,%di # where the args are moving to
mov $(MEM_ARG_SIZE/4),%cx # size of the arguments in longs
rep # Relocate
movsl # the words
#
# Save the entry point so the client can get to it later on
#
movl end+AOUT_ENTRY, %eax # load the entry point
stosl # add it to the end of the
# arguments
pop %eax # Restore saved entry point
stosl # and add it to the end of
# the arguments
mov $msg_entry2,%di
call hex32
mov $msg_entry,%si
call putstr
#
# Now we just start up BTX and let it do the rest
#
movw $jump_message, %si # Display the
callw putstr # jump message
mov $msg_jump,%si # Display the
call putstr # jump message
ljmp $0,$MEM_BTX_ENTRY # Jump to the BTX entry point
#
# Display a null-terminated string
# Lookup the file in the path at [SI] from the root directory.
#
putstr: lodsb # load %al from %ds:(%si)
testb %al,%al # stop at null
jnz putc # if the char != null, output it
retw # return when null is hit
putc: movw $0x7,%bx # attribute for output
movb $0xe,%ah # BIOS: put_char
# Trashes: All but BX
# Returns: BX = pointer to record
#
lookup: mov $VD_ROOTDIR+MEM_VOLDESC,%bx # Root directory record
push %si
mov $msg_lookup,%si # Display lookup message
call putstr
pop %si
push %si
call putstr
mov $msg_lookup2,%si
call putstr
pop %si
lookup_dir: lodsb # Get first char of path
cmp $0,%al # Are we done?
je lookup_done # Yes
cmp $'/',%al # Skip path separator.
je lookup_dir
dec %si # Undo lodsb side effect
call find_file # Lookup first path item
jnc lookup_dir # Try next component
mov $msg_lookupfail,%si # Not found.
jmp error
lookup_done: mov $msg_lookupok,%si # Success message
call putstr
ret
#
# Lookup file at [SI] in directory whose record is at [BX].
#
# Trashes: All but returns
# Returns: CF = 0 (success), BX = pointer to record, SX = next path item
# CF = 1 (not found), SI = preserved
#
find_file: push %si
mov $msg_startff,%si
call putstr
pop %si
movzbw DIR_LEN(%bx),%cx
call hexdump
mov DIR_EXTENT(%bx),%eax # Load extent
xor %edx,%edx
mov DIR_EA_LEN(%bx),%dl
add %edx,%eax # Skip extended attributes
mov %eax,rec_lba # Save LBA
mov DIR_SIZE(%bx),%eax # Save size
mov %eax,rec_size
xor %cl,%cl # Zero length
push %si # Save
ff.namelen: inc %cl # Update length
lodsb # Read char
cmp $0,%al # Nul?
je ff.namedone # Yes
cmp $'/',%al # Path separator?
jnz ff.namelen # No, keep going
ff.namedone: dec %cl # Adjust length and save
mov %cl,name_len
mov %cl,%al
mov $msg_fflen,%di
call hex8
mov $msg_ffpath,%si
call putstr
pop %si
push %si
call putstr
mov $msg_ffpath2,%si
call putstr
pop %si # Restore
ff.load: mov rec_lba,%eax # Load LBA
mov $MEM_DIR,%ebx # Address buffer
mov $1,%dh # One sector
call read # Read directory block
incl rec_lba # Update LBA to next block
ff.scan: mov %ebx,%edx # Check for EOF
sub $MEM_DIR,%edx
cmp %edx,rec_size
ja ff.scan.1
stc # EOF reached
ret
ff.scan.1: cmpb $0,DIR_LEN(%bx) # Last record in block?
je ff.nextblock
movzbw DIR_LEN(%bx),%cx
call hexdump
push %si # Save
mov $msg_ffscan,%si
call putstr
movzbw DIR_NAMELEN(%bx),%si # Find end of string
ff.checkver: push %bx
mov DIR_NAME-1(%bx,%si),%al
call putc
pop %bx
cmpb $'0',DIR_NAME-1(%bx,%si) # Less than '0'?
jb ff.checkver.1
cmpb $'9',DIR_NAME-1(%bx,%si) # Greater than '9'?
ja ff.checkver.1
dec %si # Next char
jnz ff.checkver
jmp ff.checklen # All numbers in name, so
# no version
ff.checkver.1: movzbw DIR_NAMELEN(%bx),%cx
cmp %cx,%si # Did we find any digits?
je ff.checkdot # No
push %bx
mov DIR_NAME-1(%bx,%si),%al
call putc
pop %bx
cmpb $';',DIR_NAME-1(%bx,%si) # Check for semicolon
jne ff.checkver.2
dec %si # Skip semicolon
mov %si,%cx
mov %cl,DIR_NAMELEN(%bx) # Adjust length
push %bx
mov $'-',%al
call putc
pop %bx
jmp ff.checkdot
ff.checkver.2: mov %cx,%si # Restore %si to end of string
ff.checkdot: cmpb $'.',DIR_NAME-1(%bx,%si) # Trailing dot?
jne ff.checklen # No
push %bx
mov $'-',%al
call putc
pop %bx
decb DIR_NAMELEN(%bx) # Adjust length
ff.checklen: pop %si # Restore
push %si
mov $msg_ffscan2,%si
call putstr
mov $msg_ffcheck,%si
call putstr
lea DIR_NAMELEN(%bx),%si
call putstrl
mov $msg_ffcheck2,%si
call putstr
pop %si
movzbw name_len,%cx # Load length of name
cmp %cl,DIR_NAMELEN(%bx) # Does length match?
je ff.checkname # Yes, check name
ff.nextrec: add DIR_LEN(%bx),%bl # Next record
adc $0,%bh
jmp ff.scan
ff.nextblock: subl $SECTOR_SIZE,rec_size # Adjust size
jnc ff.load # If subtract ok, keep going
ret # End of file, so not found
ff.checkname: push %si
mov $msg_lenmatch,%si
call putstr
pop %si
lea DIR_NAME(%bx),%di # Address name in record
push %si # Save
repe cmpsb # Compare name
jcxz ff.match # We have a winner!
pop %si # Restore
jmp ff.nextrec # Keep looking.
ff.match: add $2,%sp # Discard saved %si
clc # Clear carry
ret
#
# Load DH sectors starting at LBA EAX into [EBX].
#
# Trashes: EAX
#
read: push %si # Save
mov %eax,edd_lba # LBA to read from
mov %ebx,%eax # Convert address
shr $4,%eax # to segment
mov %ax,edd_addr+0x2 # and store
read.retry: #call twiddle # Entertain the user
push %dx # Save
push %di # DEBUG: dump packet
push %bx
mov %dh,%al # Length
mov $dump_len,%di
call hex8
mov edd_addr+0x2,%ax # Seg
mov $dump_seg,%di
call hex16
mov edd_addr,%ax # Offset
mov $dump_offset,%di
call hex16
mov edd_lba,%eax # LBA
mov $dump_lba,%di
call hex32
mov $dump_packet,%si # Display
call putstr
pop %bx
pop %di
mov $edd_packet,%si # Address Packet
mov %dh,edd_len # Set length
mov drive,%dl # BIOS Device
mov $0x42,%ah # BIOS: Extended Read
int $0x13 # Call BIOS
pop %dx # Restore
jc read.fail # Worked?
pop %si # Restore
ret # Return
read.fail: cmp $ERROR_TIMEOUT,%ah # Timeout?
je read.retry # Yes, Retry.
read.error: mov %ah,%al # Save error
mov $hex_error,%di # Format it
call hex8 # as hex
mov $msg_badread,%si # Display Read error message
#
# Display error message at [SI] and halt.
#
error: call putstr # Display message
halt: hlt
jmp halt # Spin
#
# Dump CX bytes from memory at [BX].
#
hexdump: push %ax # Save
push %bx # Save
push %dx # Save
push %si # Save
push %di # Save
mov %bx,%si # Where to read from
hd.line: mov $16,%dx # Bytes per line
push %si # Save offset
push %cx # Save
push %dx # counts
mov $hex_line,%di
mov %si,%ax # Format hex
call hex16 # offset
inc %di
hd.hexloop: jcxz hd.hexblank # Are we done yet?
lodsb # Read
call hex8 # Hexify
inc %di
dec %cx # Update total count
dec %dx # and per-line count
jnz hd.hexloop # Next char
jmp hd.raw # Second half of line
hd.hexblank: mov $' ',%al # Put spaces as
hd.hb.loop: stosb # placeholders
stosb
inc %di
dec %dx # Just do per-line count
jnz hd.hb.loop # Next blank
hd.raw: pop %dx # Restore
pop %cx # counts
pop %si # Restart input
inc %di # Skip pipe char
hd.rawloop: jcxz hd.rawblank # Done yet?
lodsb # Read
cmp $0x20,%al # Use '.' for
jge hd.rawok # special
mov $'.',%al # characters
hd.rawok: stosb
dec %cx
dec %dx
jnz hd.rawloop # Next char
jmp hd.outline # Next line
hd.rawblank: mov $' ',%al # Space as placeholder
mov %dx,%cx # Fill rest
rep stosb # of line
hd.outline: push %si # Save
mov $hex_line,%si # Now spit it out
call putstr
pop %si # Restore
jcxz hd.ret # Return if done
jmp hd.line # Next line
hd.ret: pop %di # Restore
pop %si # Restore
pop %dx # Restore
pop %bx # Restore
pop %ax # Restore
ret
#
# Display a null-terminated string.
#
# Trashes: AX, SI
#
putstr: push %bx # Save
putstr.load: lodsb # load %al from %ds:(%si)
test %al,%al # stop at null
jnz putstr.putc # if the char != null, output it
pop %bx # Restore
ret # return when null is hit
putstr.putc: call putc # output char
jmp putstr.load # next char
#
# Print out length-based string from [SI].
#
# Trashes: AX, SI
#
putstrl: push %bx # Save
push %cx # Save
lodsb
movzbw %al,%cx # Length
jcxz putstrl.ret # Skip if empty
putstrl.loop: lodsb # Read char
call putc # Display
loop putstrl.loop # Loop
putstrl.ret: pop %cx # Restore
pop %bx # Restore
ret
#
# Display a single char.
#
putc: push %ax
push %dx
mov $0x1,%ah
xor %dx,%dx
int $0x14
pop %dx
pop %ax
mov $0x7,%bx # attribute for output
mov $0xe,%ah # BIOS: put_char
int $0x10 # call BIOS, print char in %al
jmp putstr # keep looping
ret # Return to caller
#
# Output the "twiddle"
#
twiddle: push %ax # Save
push %bx # Save
mov twiddle_index,%al # Load index
mov twiddle_chars,%bx # Address table
inc %al # Next
and $3,%al # char
xlat # Get char
call putc # Output it
mov $8,%al # Backspace
call putc # Output it
pop %bx # Restore
pop %ax # Restore
ret
#
# Enable A20
#
seta20: cli # Disable interrupts
seta20.1: inb $0x64,%al # Get status
testb $0x2,%al # Busy?
seta20.1: in $0x64,%al # Get status
test $0x2,%al # Busy?
jnz seta20.1 # Yes
movb $0xd1,%al # Command: Write
outb %al,$0x64 # output port
seta20.2: inb $0x64,%al # Get status
testb $0x2,%al # Busy?
mov $0xd1,%al # Command: Write
out %al,$0x64 # output port
seta20.2: in $0x64,%al # Get status
test $0x2,%al # Busy?
jnz seta20.2 # Yes
movb $0xdf,%al # Enable
outb %al,$0x60 # A20
mov $0xdf,%al # Enable
out %al,$0x60 # A20
sti # Enable interrupts
retw # To caller
ret # To caller
#
# Convert EAX, AX, or AL to hex, saving the result to [EDI].
#
hex32: pushl %eax # Save
shrl $0x10,%eax # Do upper
call hex16 # 16
popl %eax # Restore
hex16: call hex16.1 # Do upper 8
hex16.1: xchgb %ah,%al # Save/restore
hex8: pushl %eax # Save
shrb $0x4,%al # Do upper
call hex8.1 # 4
popl %eax # Restore
hex8.1: andb $0xf,%al # Get lower 4
cmpb $0xa,%al # Convert
sbbb $0x69,%al # to hex
das # digit
orb $0x20,%al # To lower case
stosb # Save char
ret # (Recursive)
#
# BTX client to start btxldr
#
.code32
btx_client: movl $(MEM_ARG_BTX-MEM_BTX_CLIENT+MEM_ARG_SIZE-4), %esi
btx_client: mov $(MEM_ARG_BTX-MEM_BTX_CLIENT+MEM_ARG_SIZE-4), %esi
# %ds:(%esi) -> end
# of boot[12] args
movl $(MEM_ARG_SIZE/4), %ecx # Number of words to push
mov $(MEM_ARG_SIZE/4),%ecx # Number of words to push
std # Go backwards
push_arg: lodsl # Read argument
pushl %eax # Push it onto the stack
push %eax # Push it onto the stack
loop push_arg # Push all of the arguments
cld # In case anyone depends on this
pushl MEM_ARG_BTX-MEM_BTX_CLIENT+MEM_ARG_SIZE # Entry point of
# the loader
pushl %eax # Emulate a near call
movl $0x1, %eax # 'exec' system call
push %eax # Emulate a near call
mov $0x1,%eax # 'exec' system call
int $INT_SYS # BTX system call
btx_client_end:
.code16
@ -251,11 +686,65 @@ gdt.1:
#
gdtdesc: .word gdt.1-gdt-1 # Limit
.long gdt # Base
#
# EDD Packet
#
edd_packet: .byte 0x10 # Length
.byte 0 # Reserved
edd_len: .byte 0x0 # Num to read
.byte 0 # Reserved
edd_addr: .word 0x0,0x0 # Seg:Off
edd_lba: .quad 0x0 # LBA
welcome_msg: .asciz "CD Loader 1.00\r\n\n"
bootinfo_msg: .asciz "Building the boot loader arguments\r\n"
relocate_msg: .asciz "Relocating the loader and the BTX\r\n"
jump_message: .asciz "Starting the BTX loader\r\n"
drive: .byte 0
#
# State for searching dir
#
rec_lba: .long 0x0 # LBA (adjusted for EA)
rec_size: .long 0x0 # File size
name_len: .byte 0x0 # Length of current name
twiddle_index: .byte 0x0
msg_welcome: .asciz "CD Loader 1.01\r\n\n"
msg_bootinfo: .asciz "Building the boot loader arguments\r\n"
msg_relocate: .asciz "Relocating the loader and the BTX\r\n"
msg_jump: .asciz "Starting the BTX loader\r\n"
msg_badread: .ascii "Read Error: 0x"
hex_error: .ascii "00\r\n"
msg_vd: .asciz "Read Volume Descriptor\r\n"
msg_novd: .asciz "Could not find Primary Volume Descriptor\r\n"
msg_lookup: .asciz "Looking up "
msg_lookup2: .asciz "... "
msg_lookupok: .asciz "Found\r\n"
msg_lookupfail: .asciz "File not found\r\n"
msg_load2big: .asciz "File too big\r\n"
loader_path: .asciz "/BOOT/LOADER"
twiddle_chars: .ascii "|/-\\"
msg_entry: .ascii "Entry point: "
msg_entry2: .asciz "00000000\r\n"
msg_lookup_done:.asciz "Lookup returned\r\n"
msg_startff: .asciz "\r\nStarting find_file\r\n"
msg_ffpath: .asciz "Path = \""
msg_ffpath2: .ascii "\" Length = "
msg_fflen: .asciz "00\r\n"
msg_lenmatch: .asciz "ff: Length matched\r\n"
msg_ffcheck: .asciz "ff: Checking name: "
msg_ffscan2:
msg_ffcheck2: .asciz "\r\n"
msg_ffscan: .asciz "ff: Scanning name: "
dump_packet: .ascii "Len "
dump_len: .ascii "00 Addr "
dump_seg: .ascii "0000:"
dump_offset: .ascii "0000 LBA "
dump_lba: .asciz "00000000\r\n"
dump_bx: .ascii "bx = "
hex_bx: .asciz "0000\r\n"
hex_line: .ascii "0000:00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00 "
.asciz "|................|\r\n"
.p2align 4
end: