From 33cf0c5194d050de1b3520b40e23b28d380b1fbb Mon Sep 17 00:00:00 2001 From: Yoshihiro Takahashi Date: Sun, 14 May 2006 07:26:02 +0000 Subject: [PATCH] Add a bootable CD support. --- sys/boot/pc98/Makefile | 2 +- sys/boot/pc98/cdboot/Makefile | 13 + sys/boot/pc98/cdboot/cdboot.s | 811 +++++++++++++++++++++++++++++++++ sys/boot/pc98/libpc98/bioscd.c | 344 ++++++++++++++ 4 files changed, 1169 insertions(+), 1 deletion(-) create mode 100644 sys/boot/pc98/cdboot/Makefile create mode 100644 sys/boot/pc98/cdboot/cdboot.s create mode 100644 sys/boot/pc98/libpc98/bioscd.c diff --git a/sys/boot/pc98/Makefile b/sys/boot/pc98/Makefile index d63cb4c2e0a4..ca89dbc242de 100644 --- a/sys/boot/pc98/Makefile +++ b/sys/boot/pc98/Makefile @@ -1,5 +1,5 @@ # $FreeBSD$ -SUBDIR= btx boot0 boot0.5 boot2 kgzldr libpc98 loader +SUBDIR= btx boot0 boot0.5 boot2 cdboot kgzldr libpc98 loader .include diff --git a/sys/boot/pc98/cdboot/Makefile b/sys/boot/pc98/cdboot/Makefile new file mode 100644 index 000000000000..d9e3fb57914f --- /dev/null +++ b/sys/boot/pc98/cdboot/Makefile @@ -0,0 +1,13 @@ +# $FreeBSD$ + +PROG= cdboot +STRIP= +BINMODE=${NOBINMODE} +NO_MAN= +SRCS= ${PROG}.s + +ORG= 0x0000 + +LDFLAGS=-N -e start -Ttext ${ORG} -Wl,-S,--oformat,binary + +.include diff --git a/sys/boot/pc98/cdboot/cdboot.s b/sys/boot/pc98/cdboot/cdboot.s new file mode 100644 index 000000000000..c98cd21f896a --- /dev/null +++ b/sys/boot/pc98/cdboot/cdboot.s @@ -0,0 +1,811 @@ +# +# Copyright (c) 2006 TAKAHASHI Yoshihiro +# Copyright (c) 2001 John Baldwin +# All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions +# are met: +# 1. Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# 2. Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution. +# 3. Neither the name of the author nor the names of any co-contributors +# may be used to endorse or promote products derived from this software +# without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND +# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +# ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE +# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +# SUCH DAMAGE. +# + +# $FreeBSD$ + +# +# 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. +# + +# +# Memory locations. +# + .set STACK_OFF,0x6000 # Stack offset + .set LOAD_SEG,0x0700 # Load segment + .set LOAD_SIZE,2048 # Load size + .set DAUA,0x0584 # DA/UA + + .set MEM_PAGE_SIZE,0x1000 # memory page size, 4k + .set MEM_ARG,0x900 # Arguments at start + .set MEM_ARG_BTX,0xa100 # Where we move them to so the + # BTX client can see them + .set MEM_ARG_SIZE,0x18 # Size of the arguments + .set MEM_BTX_ADDRESS,0x9000 # where BTX lives + .set MEM_BTX_ENTRY,0x9010 # where BTX starts to execute + .set MEM_BTX_OFFSET,MEM_PAGE_SIZE # offset of BTX in the loader + .set MEM_BTX_CLIENT,0xa000 # where BTX clients live +# +# PC98 machine type from sys/pc98/pc98/pc98_machdep.h +# + .set MEM_SYS, 0xa100 # System common area segment + .set PC98_MACHINE_TYPE, 0x0620 # PC98 machine type + .set EPSON_ID, 0x0624 # EPSON machine id + + .set M_NEC_PC98, 0x0001 + .set M_EPSON_PC98, 0x0002 + .set M_NOT_H98, 0x0010 + .set M_H98, 0x0020 + .set M_NOTE, 0x0040 + .set M_NORMAL, 0x1000 + .set M_8M, 0x8000 +# +# Signature Constants +# + .set SIG1_OFF,0x1fe # Signature offset + .set SIG2_OFF,0x7fe # Signature offset +# +# a.out header fields +# + .set AOUT_TEXT,0x04 # text segment size + .set AOUT_DATA,0x08 # data segment size + .set AOUT_BSS,0x0c # zero'd BSS size + .set AOUT_SYMBOLS,0x10 # symbol table + .set AOUT_ENTRY,0x14 # entry point + .set AOUT_HEADER,MEM_PAGE_SIZE # size of the a.out header +# +# Flags for kargs->bootflags +# + .set KARGS_FLAGS_CD,0x1 # flag to indicate booting from + # CD loader +# +# Segment selectors. +# + .set SEL_SDATA,0x8 # Supervisor data + .set SEL_RDATA,0x10 # Real mode data + .set SEL_SCODE,0x18 # PM-32 code + .set SEL_SCODE16,0x20 # PM-16 code +# +# BTX constants +# + .set INT_SYS,0x30 # BTX syscall interrupt +# +# Constants for reading from the CD. +# + .set ERROR_TIMEOUT,0x90 # 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,0xf800 # 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 + +# +# Program start. +# + .code16 + .globl start + +start: jmp main + + .org 4 + .ascii "IPL1 " + +main: cld + + /* Setup the stack */ + xor %ax,%ax + mov %ax,%ss + mov $STACK_OFF,%sp + + push %ecx + + /* Setup graphic screen */ + mov $0x42,%ah # 640x400 + mov $0xc0,%ch + int $0x18 + mov $0x40,%ah # graph on + int $0x18 + + /* Setup text screen */ + mov $0x0a00,%ax # 80x25 + int $0x18 + mov $0x0c,%ah # text on + int $0x18 + mov $0x13,%ah # cursor home + xor %dx,%dx + int $0x18 + mov $0x11,%ah # cursor on + int $0x18 + + /* Setup keyboard */ + mov $0x03,%ah + int $0x18 + + /* Transfer PC-9801 system common area */ + xor %ax,%ax + mov %ax,%si + mov %ax,%ds + mov %ax,%di + mov $MEM_SYS,%ax + mov %ax,%es + mov $0x0600,%cx + rep + movsb + + /* Transfer EPSON machine type */ + mov $0xfd00,%ax + mov %ax,%ds + mov (0x804),%eax + and $0x00ffffff,%eax + mov %eax,%es:(EPSON_ID) + + /* Set machine type to PC98_SYSTEM_PARAMETER */ + call machine_check + + /* Load cdboot */ + xor %ax,%ax + mov %ax,%ds + mov $0x06,%ah /* Read data */ + mov (DAUA),%al /* Read drive */ + pop %ecx /* cylinder */ + xor %dx,%dx /* head / sector */ + mov $LOAD_SEG,%bx /* Load address */ + mov %bx,%es + xor %bp,%bp + mov $LOAD_SIZE,%bx /* Load size */ + int $0x1b + mov $msg_readerr,%si + jc error + + /* Jump to cdboot */ + ljmp $LOAD_SEG,$cdboot + +# +# Set machine type to PC98_SYSTEM_PARAMETER. +# +machine_check: xor %edx,%edx + mov %dx,%ds + mov $MEM_SYS,%ax + mov %ax,%es + + /* Wait V-SYNC */ +vsync.1: inb $0x60,%al + test $0x20,%al + jnz vsync.1 +vsync.2: inb $0x60,%al + test $0x20,%al + jz vsync.2 + + /* ANK 'A' font */ + xor %al,%al + outb %al,$0xa1 + mov $0x41,%al + outb %al,$0xa3 + + /* Get 'A' font from CG window */ + push %ds + mov $0xa400,%ax + mov %ax,%ds + xor %eax,%eax + xor %bx,%bx + mov $4,%cx +font.1: add (%bx),%eax + add $4,%bx + loop font.1 + pop %ds + cmp $0x6efc58fc,%eax + jnz m_epson + +m_pc98: or $M_NEC_PC98,%edx + mov $0x0458,%bx + mov (%bx),%al + test $0x80,%al + jz m_not_h98 + or $M_H98,%edx + jmp 1f +m_epson: or $M_EPSON_PC98,%edx +m_not_h98: or $M_NOT_H98,%edx + +1: inb $0x42,%al + test $0x20,%al + jz 1f + or $M_8M,%edx + +1: mov $0x0400,%bx + mov (%bx),%al + test $0x80,%al + jz 1f + or $M_NOTE,%edx + +1: mov $PC98_MACHINE_TYPE,%bx + mov %edx,%es:(%bx) + ret + +# +# Print out the error message at [SI], wait for a keypress, and then +# reboot the machine. +# +error: call putstr + mov $msg_keypress,%si + call putstr + xor %ax,%ax # Get keypress + int $0x18 + xor %ax,%ax # CPU reset + outb %al,$0xf0 +halt: hlt + jmp halt # Spin + +# +# Display a null-terminated string at [SI]. +# +# Trashes: AX, BX, CX, DX, SI, DI +# +putstr: push %ds + push %es + mov %cs,%ax + mov %ax,%ds + mov $0xa000,%ax + mov %ax,%es + mov cursor,%di + mov $0x00e1,%bx # Attribute + mov $160,%cx +putstr.0: lodsb + testb %al,%al + jz putstr.done + cmp $0x0d,%al + jz putstr.cr + cmp $0x0a,%al + jz putstr.lf + mov %bl,%es:0x2000(%di) + stosb + inc %di + jmp putstr.move +putstr.cr: xor %dx,%dx + mov %di,%ax + div %cx + sub %dx,%di + jmp putstr.move +putstr.lf: add %cx,%di +putstr.move: mov %di,%dx + mov $0x13,%ah # Move cursor + int $0x18 + jmp putstr.0 +putstr.done: mov %di,cursor + pop %es + pop %ds + ret + +# +# Display a single char at [AL], but don't move a cursor. +# +putc: push %es + push %di + push %bx + mov $0xa000,%bx + mov %bx,%es + mov cursor,%di + mov $0xe1,%bl # Attribute + mov %bl,%es:0x2000(%di) + stosb + pop %bx + pop %di + pop %es + ret + +msg_readerr: .asciz "Read Error\r\n" +msg_keypress: .asciz "\r\nPress any key to reboot\r\n" + +/* Boot signature */ + + .org SIG1_OFF,0x90 + + .word 0xaa55 # Magic number + +# +# cdboot +# +cdboot: mov %cs,%ax + mov %ax,%ds + xor %ax,%ax + mov %ax,%es + mov %es:(DAUA),%al # Save BIOS boot device + mov %al,drive + mov %cx,cylinder # Save BIOS boot cylinder + + 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] +# + 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 + mov drive,%dl # Store BIOS boot device + mov %dl,%es:0x4(%bx) # in kargs->bootdev + or $KARGS_FLAGS_CD,%es: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 + cmpb $VD_PRIMARY,%es:(%bx) # Primary VD? + je have_vd # Yes + pop %eax # Prepare to + inc %eax # try next + cmpb $VD_END,%es:(%bx) # Last VD? + jne load_vd # No, read next + mov $msg_novd,%si # No VD + jmp error # Halt +have_vd: # Have Primary VD +# +# Try to look up the loader binary using the paths in the loader_paths +# array. +# + mov $loader_paths,%si # Point to start of array +lookup_path: push %si # Save file name pointer + call lookup # Try to find file + pop %di # Restore file name pointer + jnc lookup_found # Found this file + push %es + mov %cs,%ax + mov %ax,%es + xor %al,%al # Look for next + mov $0xffff,%cx # path name by + repnz # scanning for + scasb # nul char + pop %es + mov %di,%si # Point %si at next path + mov (%si),%al # Get first char of next path + or %al,%al # Is it double nul? + jnz lookup_path # No, try it. + mov $msg_failed,%si # Failed message + jmp error # Halt +lookup_found: # Found a loader file +# +# Load the binary into the buffer. Due to real mode addressing limitations +# we have to read it in in 64k chunks. +# + mov %es:DIR_SIZE(%bx),%eax # Read file length + add $SECTOR_SIZE-1,%eax # Convert length to sectors + shr $SECTOR_SHIFT,%eax + cmp $BUFFER_LEN,%eax + jbe load_sizeok + mov $msg_load2big,%si # Error message + jmp error +load_sizeok: movzbw %al,%cx # Num sectors to read + mov %es:DIR_EXTENT(%bx),%eax # Load extent + xor %edx,%edx + mov %es: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 +# + xor %ax,%ax # Turn A20 on + outb %al,$0xf2 + mov $0x02,%al + outb %al,$0xf6 +# +# Relocate the loader and BTX using a very lazy protected mode +# + mov $msg_relocate,%si # Display the + call putstr # relocation message + mov %es:(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 + mov %es:(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 + 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: mov $SEL_SDATA,%ax # Initialize + mov %ax,%ds # %ds and + mov %ax,%es # %es to a flat selector + rep # Relocate the + movsb # text 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 + 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 + 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: 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 $LOAD_SEG,$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 +# + mov %cs,%ax + mov %ax,%ds + xor %ax,%ax + mov %ax,%es + 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 +# + xor %ax,%ax + mov %ax,%ds + 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 +# + pop %eax # Restore saved entry point + stosl # and add it to the end of + # the arguments +# +# Now we just start up BTX and let it do the rest +# + mov $msg_jump,%si # Display the + call putstr # jump message + ljmp $0,$MEM_BTX_ENTRY # Jump to the BTX entry point + +# +# Lookup the file in the path at [SI] from the root directory. +# +# Trashes: All but BX +# Returns: CF = 0 (success), BX = pointer to record +# CF = 1 (not found) +# +lookup: mov $VD_ROOTDIR+MEM_VOLDESC,%bx # Root directory record + push %bx + 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 + pop %bx +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 message + push %bx + call putstr + pop %bx + stc # Set carry + ret +lookup_done: mov $msg_lookupok,%si # Success message + push %bx + call putstr + pop %bx + clc # Clear carry + ret + +# +# Lookup file at [SI] in directory whose record is at [BX]. +# +# Trashes: All but returns +# Returns: CF = 0 (success), BX = pointer to record, SI = next path item +# CF = 1 (not found), SI = preserved +# +find_file: mov %es:DIR_EXTENT(%bx),%eax # Load extent + xor %edx,%edx + mov %es:DIR_EA_LEN(%bx),%dl + add %edx,%eax # Skip extended attributes + mov %eax,rec_lba # Save LBA + mov %es: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 + 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,%es:DIR_LEN(%bx) # Last record in block? + je ff.nextblock + push %si # Save + movzbw %es:DIR_NAMELEN(%bx),%si # Find end of string +ff.checkver: cmpb $'0',%es:DIR_NAME-1(%bx,%si) # Less than '0'? + jb ff.checkver.1 + cmpb $'9',%es: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 %es:DIR_NAMELEN(%bx),%cx + cmp %cx,%si # Did we find any digits? + je ff.checkdot # No + cmpb $';',%es:DIR_NAME-1(%bx,%si) # Check for semicolon + jne ff.checkver.2 + dec %si # Skip semicolon + mov %si,%cx + mov %cl,%es:DIR_NAMELEN(%bx) # Adjust length + jmp ff.checkdot +ff.checkver.2: mov %cx,%si # Restore %si to end of string +ff.checkdot: cmpb $'.',%es:DIR_NAME-1(%bx,%si) # Trailing dot? + jne ff.checklen # No + decb %es:DIR_NAMELEN(%bx) # Adjust length +ff.checklen: pop %si # Restore + movzbw name_len,%cx # Load length of name + cmp %cl,%es:DIR_NAMELEN(%bx) # Does length match? + je ff.checkname # Yes, check name +ff.nextrec: add %es: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: lea DIR_NAME(%bx),%di # Address name in record + push %si # Save + repe cmpsb # Compare name + je 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 %es # Save + push %bp + push %dx + push %cx + push %ebx + mov %bx,%bp # Set destination address + and $0x000f,%bp + shr $4,%ebx + mov %bx,%es + xor %bx,%bx # Set read bytes + mov %dh,%bl + shl $SECTOR_SHIFT,%bx # 2048 bytes/sec + mov %ax,%cx # Set LBA + shr $16,%eax + mov %ax,%dx +read.retry: mov $0x06,%ah # BIOS device read + mov drive,%al + and $0x7f,%al + call twiddle # Entertain the user + int $0x1b # Call BIOS + jc read.fail # Worked? + pop %ebx # Restore + pop %cx + pop %dx + pop %bp + pop %es + 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 + jmp error + +# +# 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 + mov %al,twiddle_index # Save index for next call + xlat # Get char + call putc # Output it + pop %bx # Restore + pop %ax # Restore + ret + +# +# Convert AL to hex, saving the result to [EDI]. +# +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 + mov %al,(%di) # Save char + inc %di + ret # (Recursive) + +# +# BTX client to start btxldr +# + .code32 +btx_client: mov $(MEM_ARG_BTX-MEM_BTX_CLIENT+MEM_ARG_SIZE-4), %esi + # %ds:(%esi) -> end + # of boot[12] args + mov $(MEM_ARG_SIZE/4),%ecx # Number of words to push + std # Go backwards +push_arg: lodsl # Read argument + 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 + push %eax # Emulate a near call + mov $0x1,%eax # 'exec' system call + int $INT_SYS # BTX system call +btx_client_end: + .code16 + + .p2align 4 +# +# Global descriptor table. +# +gdt: .word 0x0,0x0,0x0,0x0 # Null entry + .word 0xffff,0x0000,0x9200,0x00cf # SEL_SDATA + .word 0xffff,0x0000,0x9200,0x0000 # SEL_RDATA + .word 0xffff,LOAD_SEG<<4,0x9a00,0x00cf # SEL_SCODE (32-bit) + .word 0xffff,LOAD_SEG<<4,0x9a00,0x008f # SEL_SCODE16 (16-bit) +gdt.1: +# +# Pseudo-descriptors. +# +gdtdesc: .word gdt.1-gdt-1 # Limit + .long LOAD_SEG<<4 + gdt # Base + +# +# BOOT device +# +drive: .byte 0 +cylinder: .word 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 + +cursor: .word 0 +twiddle_index: .byte 0x0 + +msg_welcome: .asciz "CD Loader 1.2\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_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" +msg_failed: .asciz "Boot failed\r\n" +twiddle_chars: .ascii "|/-\\" +loader_paths: .asciz "/BOOT.PC98/LOADER" + .asciz "/boot.pc98/loader" + .asciz "/BOOT/LOADER" + .asciz "/boot/loader" + .byte 0 + +/* Boot signature */ + + .org SIG2_OFF,0x90 + + .word 0xaa55 # Magic number diff --git a/sys/boot/pc98/libpc98/bioscd.c b/sys/boot/pc98/libpc98/bioscd.c new file mode 100644 index 000000000000..1b8d432c5103 --- /dev/null +++ b/sys/boot/pc98/libpc98/bioscd.c @@ -0,0 +1,344 @@ +/*- + * Copyright (c) 1998 Michael Smith + * Copyright (c) 2001 John H. Baldwin + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include +__FBSDID("$FreeBSD$"); + +/* + * BIOS CD device handling for CD's that have been booted off of via no + * emulation booting as defined in the El Torito standard. + * + * Ideas and algorithms from: + * + * - FreeBSD libi386/biosdisk.c + * + */ + +#include + +#include +#include +#include + +#include + +#include +#include +#include "libi386.h" + +#define BIOSCD_SECSIZE 2048 +#define BUFSIZE (1 * BIOSCD_SECSIZE) +#define MAXBCDEV 1 + +/* Major numbers for devices we frontend for. */ +#define ACDMAJOR 117 +#define CDMAJOR 15 + +#ifdef DISK_DEBUG +# define DEBUG(fmt, args...) printf("%s: " fmt "\n" , __func__ , ## args) +#else +# define DEBUG(fmt, args...) +#endif + +struct specification_packet { + u_char sp_size; + u_char sp_bootmedia; + u_char sp_drive; + u_char sp_controller; + u_int sp_lba; + u_short sp_devicespec; + u_short sp_buffersegment; + u_short sp_loadsegment; + u_short sp_sectorcount; + u_short sp_cylsec; + u_char sp_head; +}; + +/* + * List of BIOS devices, translation from disk unit number to + * BIOS unit number. + */ +static struct bcinfo { + int bc_unit; /* BIOS unit number */ + struct specification_packet bc_sp; +} bcinfo [MAXBCDEV]; +static int nbcinfo = 0; + +static int bc_read(int unit, daddr_t dblk, int blks, caddr_t dest); +static int bc_init(void); +static int bc_strategy(void *devdata, int flag, daddr_t dblk, + size_t size, char *buf, size_t *rsize); +static int bc_open(struct open_file *f, ...); +static int bc_close(struct open_file *f); +static void bc_print(int verbose); + +struct devsw bioscd = { + "cd", + DEVT_CD, + bc_init, + bc_strategy, + bc_open, + bc_close, + noioctl, + bc_print, + NULL +}; + +/* + * Translate between BIOS device numbers and our private unit numbers. + */ +int +bc_bios2unit(int biosdev) +{ + int i; + + DEBUG("looking for bios device 0x%x", biosdev); + for (i = 0; i < nbcinfo; i++) { + DEBUG("bc unit %d is BIOS device 0x%x", i, bcinfo[i].bc_unit); + if (bcinfo[i].bc_unit == biosdev) + return(i); + } + return(-1); +} + +int +bc_unit2bios(int unit) +{ + if ((unit >= 0) && (unit < nbcinfo)) + return(bcinfo[unit].bc_unit); + return(-1); +} + +/* + * We can't quiz, we have to be told what device to use, so this functoin + * doesn't do anything. Instead, the loader calls bc_add() with the BIOS + * device number to add. + */ +static int +bc_init(void) +{ + + return (0); +} + +int +bc_add(int biosdev) +{ + + if (nbcinfo >= MAXBCDEV) + return (-1); + bcinfo[nbcinfo].bc_unit = biosdev; + + /* SCSI CD-ROM only */ + if ((biosdev & 0xf0) != 0xa0) + return (-1); + if ((((uint32_t *)PTOV(0xA1460))[biosdev & 0x0f] & 0x1f) != 5) + return (-1); + + printf("BIOS CD is cd%d\n", nbcinfo); + nbcinfo++; + return(0); +} + +/* + * Print information about disks + */ +static void +bc_print(int verbose) +{ + int i; + char line[80]; + + for (i = 0; i < nbcinfo; i++) { + sprintf(line, " cd%d: Device 0x%x\n", i, + bcinfo[i].bc_sp.sp_devicespec); + pager_output(line); + } +} + +/* + * Attempt to open the disk described by (dev) for use by (f). + */ +static int +bc_open(struct open_file *f, ...) +{ + va_list ap; + struct i386_devdesc *dev; + + va_start(ap, f); + dev = va_arg(ap, struct i386_devdesc *); + va_end(ap); + if (dev->d_kind.bioscd.unit >= nbcinfo) { + DEBUG("attempt to open nonexistent disk"); + return(ENXIO); + } + + return(0); +} + +static int +bc_close(struct open_file *f) +{ + + return(0); +} + +static int +bc_strategy(void *devdata, int rw, daddr_t dblk, size_t size, char *buf, + size_t *rsize) +{ + struct i386_devdesc *dev; + int unit; + int blks; +#ifdef BD_SUPPORT_FRAGS + char fragbuf[BIOSCD_SECSIZE]; + size_t fragsize; + + fragsize = size % BIOSCD_SECSIZE; +#else + if (size % BIOSCD_SECSIZE) + return (EINVAL); +#endif + + if (rw != F_READ) + return(EROFS); + dev = (struct i386_devdesc *)devdata; + unit = dev->d_kind.bioscd.unit; + blks = size / BIOSCD_SECSIZE; + if (dblk % (BIOSCD_SECSIZE / DEV_BSIZE) != 0) + return (EINVAL); + dblk /= (BIOSCD_SECSIZE / DEV_BSIZE); + DEBUG("read %d from %d to %p", blks, dblk, buf); + + if (rsize) + *rsize = 0; + if (blks && bc_read(unit, dblk, blks, buf)) { + DEBUG("read error"); + return (EIO); + } +#ifdef BD_SUPPORT_FRAGS + DEBUG("bc_strategy: frag read %d from %d+%d to %p", + fragsize, dblk, blks, buf + (blks * BIOSCD_SECSIZE)); + if (fragsize && bc_read(unit, dblk + blks, 1, fragsize)) { + DEBUG("frag read error"); + return(EIO); + } + bcopy(fragbuf, buf + (blks * BIOSCD_SECSIZE), fragsize); +#endif + if (rsize) + *rsize = size; + return (0); +} + +static int +bc_read(int unit, daddr_t dblk, int blks, caddr_t dest) +{ + u_int result, retry; + static unsigned short packet[8]; + int biosdev; +#ifdef DISK_DEBUG + int error; +#endif + + /* Just in case some idiot actually tries to read -1 blocks... */ + if (blks < 0) + return (-1); + + /* If nothing to do, just return succcess. */ + if (blks == 0) + return (0); + + biosdev = bc_unit2bios(unit); + /* + * Loop retrying the operation a couple of times. The BIOS + * may also retry. + */ + for (retry = 0; retry < 3; retry++) { + /* If retrying, reset the drive */ + if (retry > 0) { + v86.ctl = V86_FLAGS; + v86.addr = 0x1b; + v86.eax = 0x0300 | biosdev; + v86int(); + } + + v86.ctl = V86_FLAGS; + v86.addr = 0x1b; + v86.eax = 0x0600 | (biosdev & 0x7f); + v86.ebx = blks * BIOSCD_SECSIZE; + v86.ecx = dblk & 0xffff; + v86.edx = (dblk >> 16) & 0xffff; + v86.ebp = VTOPOFF(dest); + v86.es = VTOPSEG(dest); + v86int(); + result = (v86.efl & PSL_C); + if (result == 0) + break; + } + +#ifdef DISK_DEBUG + error = (v86.eax >> 8) & 0xff; +#endif + DEBUG("%d sectors from %ld to %p (0x%x) %s", blks, dblk, dest, + VTOP(dest), result ? "failed" : "ok"); + DEBUG("unit %d status 0x%x", unit, error); + +/* hexdump(dest, (blks * BIOSCD_SECSIZE)); */ + return(0); +} + +/* + * Return a suitable dev_t value for (dev). + */ +int +bc_getdev(struct i386_devdesc *dev) +{ + int biosdev, unit, device; + int major; + int rootdev; + + unit = dev->d_kind.bioscd.unit; + biosdev = bc_unit2bios(unit); + DEBUG("unit %d BIOS device %d", unit, biosdev); + if (biosdev == -1) /* not a BIOS device */ + return(-1); + + device = biosdev & 0xf0; + if (device == 0x80) + major = ACDMAJOR; + else if (device == 0xa0) + major = CDMAJOR; + else + return (-1); + + unit = 0; /* XXX */ + + /* XXX: Assume partition 'a'. */ + rootdev = MAKEBOOTDEV(major, 0, 0, unit, 0); + DEBUG("dev is 0x%x\n", rootdev); + return(rootdev); +}