2 # Copyright (c) 2006 TAKAHASHI Yoshihiro <nyan@FreeBSD.org>
3 # Copyright (c) 2001 John Baldwin <jhb@FreeBSD.org>
6 # Redistribution and use in source and binary forms, with or without
7 # modification, are permitted provided that the following conditions
9 # 1. Redistributions of source code must retain the above copyright
10 # notice, this list of conditions and the following disclaimer.
11 # 2. Redistributions in binary form must reproduce the above copyright
12 # notice, this list of conditions and the following disclaimer in the
13 # documentation and/or other materials provided with the distribution.
15 # THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
16 # ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
17 # IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
18 # ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
19 # FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
20 # DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
21 # OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
22 # HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
23 # LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
24 # OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
31 # Basically, we first create a set of boot arguments to pass to the loaded
32 # binary. Then we attempt to load /boot/loader from the CD we were booted
41 .set STACK_OFF,0x6000 # Stack offset
42 .set LOAD_SEG,0x0700 # Load segment
43 .set LOAD_SIZE,2048 # Load size
44 .set DAUA,0x0584 # DA/UA
46 .set MEM_PAGE_SIZE,0x1000 # memory page size, 4k
47 .set MEM_ARG,0x900 # Arguments at start
48 .set MEM_ARG_BTX,0xa100 # Where we move them to so the
49 # BTX client can see them
50 .set MEM_ARG_SIZE,0x18 # Size of the arguments
51 .set MEM_BTX_ADDRESS,0x9000 # where BTX lives
52 .set MEM_BTX_ENTRY,0x9010 # where BTX starts to execute
53 .set MEM_BTX_OFFSET,MEM_PAGE_SIZE # offset of BTX in the loader
54 .set MEM_BTX_CLIENT,0xa000 # where BTX clients live
56 # PC98 machine type from sys/pc98/pc98/pc98_machdep.h
58 .set MEM_SYS, 0xa100 # System common area segment
59 .set PC98_MACHINE_TYPE, 0x0620 # PC98 machine type
60 .set EPSON_ID, 0x0624 # EPSON machine id
62 .set M_NEC_PC98, 0x0001
63 .set M_EPSON_PC98, 0x0002
64 .set M_NOT_H98, 0x0010
72 .set SIG1_OFF,0x1fe # Signature offset
73 .set SIG2_OFF,0x7fe # Signature offset
77 .set AOUT_TEXT,0x04 # text segment size
78 .set AOUT_DATA,0x08 # data segment size
79 .set AOUT_BSS,0x0c # zero'd BSS size
80 .set AOUT_SYMBOLS,0x10 # symbol table
81 .set AOUT_ENTRY,0x14 # entry point
82 .set AOUT_HEADER,MEM_PAGE_SIZE # size of the a.out header
86 .set SEL_SDATA,0x8 # Supervisor data
87 .set SEL_RDATA,0x10 # Real mode data
88 .set SEL_SCODE,0x18 # PM-32 code
89 .set SEL_SCODE16,0x20 # PM-16 code
93 .set INT_SYS,0x30 # BTX syscall interrupt
95 # Constants for reading from the CD.
97 .set ERROR_TIMEOUT,0x90 # BIOS timeout on read
98 .set NUM_RETRIES,3 # Num times to retry
99 .set SECTOR_SIZE,0x800 # size of a sector
100 .set SECTOR_SHIFT,11 # number of place to shift
101 .set BUFFER_LEN,0x100 # number of sectors in buffer
102 .set MAX_READ,0xf800 # max we can read at a time
103 .set MAX_READ_SEC,MAX_READ >> SECTOR_SHIFT
104 .set MEM_READ_BUFFER,0x9000 # buffer to read from CD
105 .set MEM_VOLDESC,MEM_READ_BUFFER # volume descriptor
106 .set MEM_DIR,MEM_VOLDESC+SECTOR_SIZE # Lookup buffer
107 .set VOLDESC_LBA,0x10 # LBA of vol descriptor
108 .set VD_PRIMARY,1 # Primary VD
109 .set VD_END,255 # VD Terminator
110 .set VD_ROOTDIR,156 # Offset of Root Dir Record
111 .set DIR_LEN,0 # Offset of Dir Record length
112 .set DIR_EA_LEN,1 # Offset of EA length
113 .set DIR_EXTENT,2 # Offset of 64-bit LBA
114 .set DIR_SIZE,10 # Offset of 64-bit length
115 .set DIR_NAMELEN,32 # Offset of 8-bit name len
116 .set DIR_NAME,33 # Offset of dir name
131 /* Setup the stack */
138 /* Setup graphic screen */
139 mov $0x42,%ah # 640x400
142 mov $0x40,%ah # graph on
145 /* Setup text screen */
146 mov $0x0a00,%ax # 80x25
148 mov $0x0c,%ah # text on
150 mov $0x13,%ah # cursor home
153 mov $0x11,%ah # cursor on
160 /* Transfer PC-9801 system common area */
171 /* Transfer EPSON machine type */
176 mov %eax,%es:(EPSON_ID)
178 /* Set machine type to PC98_SYSTEM_PARAMETER */
184 mov $0x06,%ah /* Read data */
185 mov (DAUA),%al /* Read drive */
186 pop %ecx /* cylinder */
187 xor %dx,%dx /* head / sector */
188 mov $LOAD_SEG,%bx /* Load address */
191 mov $LOAD_SIZE,%bx /* Load size */
197 ljmp $LOAD_SEG,$cdboot
200 # Set machine type to PC98_SYSTEM_PARAMETER.
202 machine_check: xor %edx,%edx
208 vsync.1: inb $0x60,%al
211 vsync.2: inb $0x60,%al
221 /* Get 'A' font from CG window */
228 font.1: add (%bx),%eax
235 m_pc98: or $M_NEC_PC98,%edx
242 m_epson: or $M_EPSON_PC98,%edx
243 m_not_h98: or $M_NOT_H98,%edx
256 1: mov $PC98_MACHINE_TYPE,%bx
261 # Print out the error message at [SI], wait for a keypress, and then
262 # reboot the machine.
265 mov $msg_keypress,%si
267 xor %ax,%ax # Get keypress
269 xor %ax,%ax # CPU reset
275 # Display a null-terminated string at [SI].
277 # Trashes: AX, BX, CX, DX, SI, DI
286 mov $0x00e1,%bx # Attribute
295 mov %bl,%es:0x2000(%di)
299 putstr.cr: xor %dx,%dx
304 putstr.lf: add %cx,%di
305 putstr.move: mov %di,%dx
306 mov $0x13,%ah # Move cursor
309 putstr.done: mov %di,cursor
315 # Display a single char at [AL], but don't move a cursor.
323 mov $0xe1,%bl # Attribute
324 mov %bl,%es:0x2000(%di)
331 msg_readerr: .asciz "Read Error\r\n"
332 msg_keypress: .asciz "\r\nPress any key to reboot\r\n"
338 .word 0xaa55 # Magic number
347 mov %es:(DAUA),%al # Save BIOS boot device
349 mov %cx,cylinder # Save BIOS boot cylinder
351 mov $msg_welcome,%si # %ds:(%si) -> welcome message
352 call putstr # display the welcome message
354 # Setup the arguments that the loader is expecting from boot[12]
356 mov $msg_bootinfo,%si # %ds:(%si) -> boot args message
357 call putstr # display the message
358 mov $MEM_ARG,%bx # %ds:(%bx) -> boot args
359 mov %bx,%di # %es:(%di) -> boot args
360 xor %eax,%eax # zero %eax
361 mov $(MEM_ARG_SIZE/4),%cx # Size of arguments in 32-bit
363 rep # Clear the arguments
365 mov drive,%dl # Store BIOS boot device
366 mov %dl,%es:0x4(%bx) # in kargs->bootdev
367 or $KARGS_FLAGS_CD,%es:0x8(%bx) # kargs->bootflags |=
370 # Load Volume Descriptor
372 mov $VOLDESC_LBA,%eax # Set LBA of first VD
373 load_vd: push %eax # Save %eax
374 mov $1,%dh # One sector
375 mov $MEM_VOLDESC,%ebx # Destination
376 call read # Read it in
377 cmpb $VD_PRIMARY,%es:(%bx) # Primary VD?
379 pop %eax # Prepare to
381 cmpb $VD_END,%es:(%bx) # Last VD?
382 jne load_vd # No, read next
383 mov $msg_novd,%si # No VD
385 have_vd: # Have Primary VD
387 # Try to look up the loader binary using the paths in the loader_paths
390 mov $loader_paths,%si # Point to start of array
391 lookup_path: push %si # Save file name pointer
392 call lookup # Try to find file
393 pop %di # Restore file name pointer
394 jnc lookup_found # Found this file
398 xor %al,%al # Look for next
399 mov $0xffff,%cx # path name by
403 mov %di,%si # Point %si at next path
404 mov (%si),%al # Get first char of next path
405 or %al,%al # Is it double nul?
406 jnz lookup_path # No, try it.
407 mov $msg_failed,%si # Failed message
409 lookup_found: # Found a loader file
411 # Load the binary into the buffer. Due to real mode addressing limitations
412 # we have to read it in 64k chunks.
414 mov %es:DIR_SIZE(%bx),%eax # Read file length
415 add $SECTOR_SIZE-1,%eax # Convert length to sectors
416 shr $SECTOR_SHIFT,%eax
419 mov $msg_load2big,%si # Error message
421 load_sizeok: movzbw %al,%cx # Num sectors to read
422 mov %es:DIR_EXTENT(%bx),%eax # Load extent
424 mov %es:DIR_EA_LEN(%bx),%dl
425 add %edx,%eax # Skip extended
426 mov $MEM_READ_BUFFER,%ebx # Read into the buffer
427 load_loop: mov %cl,%dh
428 cmp $MAX_READ_SEC,%cl # Truncate to max read size
430 mov $MAX_READ_SEC,%dh
431 load_notrunc: sub %dh,%cl # Update count
433 call read # Read it in
435 add $MAX_READ_SEC,%eax # Update LBA
436 add $MAX_READ,%ebx # Update dest addr
437 jcxz load_done # Done?
438 jmp load_loop # Keep going
441 # Turn on the A20 address line
443 xor %ax,%ax # Turn A20 on
448 # Relocate the loader and BTX using a very lazy protected mode
450 mov $msg_relocate,%si # Display the
451 call putstr # relocation message
452 mov %es:(MEM_READ_BUFFER+AOUT_ENTRY),%edi # %edi is the destination
453 mov $(MEM_READ_BUFFER+AOUT_HEADER),%esi # %esi is
454 # the start of the text
456 mov %es:(MEM_READ_BUFFER+AOUT_TEXT),%ecx # %ecx = length of the text
458 push %edi # Save entry point for later
459 lgdt gdtdesc # setup our own gdt
460 cli # turn off interrupts
461 mov %cr0,%eax # Turn on
462 or $0x1,%al # protected
464 ljmp $SEL_SCODE,$pm_start # long jump to clear the
465 # instruction pre-fetch queue
467 pm_start: mov $SEL_SDATA,%ax # Initialize
468 mov %ax,%ds # %ds and
469 mov %ax,%es # %es to a flat selector
472 add $(MEM_PAGE_SIZE - 1),%edi # pad %edi out to a new page
473 and $~(MEM_PAGE_SIZE - 1),%edi # for the data segment
474 mov MEM_READ_BUFFER+AOUT_DATA,%ecx # size of the data segment
477 mov MEM_READ_BUFFER+AOUT_BSS,%ecx # size of the bss
478 xor %eax,%eax # zero %eax
479 add $3,%cl # round %ecx up to
480 shr $2,%ecx # a multiple of 4
483 mov MEM_READ_BUFFER+AOUT_ENTRY,%esi # %esi -> relocated loader
484 add $MEM_BTX_OFFSET,%esi # %esi -> BTX in the loader
485 mov $MEM_BTX_ADDRESS,%edi # %edi -> where BTX needs to go
486 movzwl 0xa(%esi),%ecx # %ecx -> length of BTX
489 ljmp $SEL_SCODE16,$pm_16 # Jump to 16-bit PM
491 pm_16: mov $SEL_RDATA,%ax # Initialize
492 mov %ax,%ds # %ds and
493 mov %ax,%es # %es to a real mode selector
494 mov %cr0,%eax # Turn off
495 and $~0x1,%al # protected
497 ljmp $LOAD_SEG,$pm_end # Long jump to clear the
498 # instruction pre-fetch queue
499 pm_end: sti # Turn interrupts back on now
501 # Copy the BTX client to MEM_BTX_CLIENT
507 mov $MEM_BTX_CLIENT,%di # Prepare to relocate
508 mov $btx_client,%si # the simple btx client
509 mov $(btx_client_end-btx_client),%cx # length of btx client
511 movsb # simple BTX client
513 # Copy the boot[12] args to where the BTX client can see them
517 mov $MEM_ARG,%si # where the args are at now
518 mov $MEM_ARG_BTX,%di # where the args are moving to
519 mov $(MEM_ARG_SIZE/4),%cx # size of the arguments in longs
523 # Save the entry point so the client can get to it later on
525 pop %eax # Restore saved entry point
526 stosl # and add it to the end of
529 # Now we just start up BTX and let it do the rest
531 mov $msg_jump,%si # Display the
532 call putstr # jump message
533 ljmp $0,$MEM_BTX_ENTRY # Jump to the BTX entry point
536 # Lookup the file in the path at [SI] from the root directory.
538 # Trashes: All but BX
539 # Returns: CF = 0 (success), BX = pointer to record
542 lookup: mov $VD_ROOTDIR+MEM_VOLDESC,%bx # Root directory record
545 mov $msg_lookup,%si # Display lookup message
554 lookup_dir: lodsb # Get first char of path
555 cmp $0,%al # Are we done?
557 cmp $'/',%al # Skip path separator.
559 dec %si # Undo lodsb side effect
560 call find_file # Lookup first path item
561 jnc lookup_dir # Try next component
562 mov $msg_lookupfail,%si # Not found message
568 lookup_done: mov $msg_lookupok,%si # Success message
576 # Lookup file at [SI] in directory whose record is at [BX].
578 # Trashes: All but returns
579 # Returns: CF = 0 (success), BX = pointer to record, SI = next path item
580 # CF = 1 (not found), SI = preserved
582 find_file: mov %es:DIR_EXTENT(%bx),%eax # Load extent
584 mov %es:DIR_EA_LEN(%bx),%dl
585 add %edx,%eax # Skip extended attributes
586 mov %eax,rec_lba # Save LBA
587 mov %es:DIR_SIZE(%bx),%eax # Save size
589 xor %cl,%cl # Zero length
591 ff.namelen: inc %cl # Update length
595 cmp $'/',%al # Path separator?
596 jnz ff.namelen # No, keep going
597 ff.namedone: dec %cl # Adjust length and save
600 ff.load: mov rec_lba,%eax # Load LBA
601 mov $MEM_DIR,%ebx # Address buffer
602 mov $1,%dh # One sector
603 call read # Read directory block
604 incl rec_lba # Update LBA to next block
605 ff.scan: mov %ebx,%edx # Check for EOF
611 ff.scan.1: cmpb $0,%es:DIR_LEN(%bx) # Last record in block?
614 movzbw %es:DIR_NAMELEN(%bx),%si # Find end of string
615 ff.checkver: cmpb $'0',%es:DIR_NAME-1(%bx,%si) # Less than '0'?
617 cmpb $'9',%es:DIR_NAME-1(%bx,%si) # Greater than '9'?
621 jmp ff.checklen # All numbers in name, so
623 ff.checkver.1: movzbw %es:DIR_NAMELEN(%bx),%cx
624 cmp %cx,%si # Did we find any digits?
626 cmpb $';',%es:DIR_NAME-1(%bx,%si) # Check for semicolon
628 dec %si # Skip semicolon
630 mov %cl,%es:DIR_NAMELEN(%bx) # Adjust length
632 ff.checkver.2: mov %cx,%si # Restore %si to end of string
633 ff.checkdot: cmpb $'.',%es:DIR_NAME-1(%bx,%si) # Trailing dot?
635 decb %es:DIR_NAMELEN(%bx) # Adjust length
636 ff.checklen: pop %si # Restore
637 movzbw name_len,%cx # Load length of name
638 cmp %cl,%es:DIR_NAMELEN(%bx) # Does length match?
639 je ff.checkname # Yes, check name
640 ff.nextrec: add %es:DIR_LEN(%bx),%bl # Next record
643 ff.nextblock: subl $SECTOR_SIZE,rec_size # Adjust size
644 jnc ff.load # If subtract ok, keep going
645 ret # End of file, so not found
646 ff.checkname: lea DIR_NAME(%bx),%di # Address name in record
648 repe cmpsb # Compare name
649 je ff.match # We have a winner!
651 jmp ff.nextrec # Keep looking.
652 ff.match: add $2,%sp # Discard saved %si
657 # Load DH sectors starting at LBA EAX into [EBX].
661 read: push %es # Save
666 mov %bx,%bp # Set destination address
670 xor %bx,%bx # Set read bytes
672 shl $SECTOR_SHIFT,%bx # 2048 bytes/sec
673 mov %ax,%cx # Set LBA
676 read.retry: mov $0x06,%ah # BIOS device read
679 call twiddle # Entertain the user
680 int $0x1b # Call BIOS
681 jc read.fail # Worked?
688 read.fail: cmp $ERROR_TIMEOUT,%ah # Timeout?
689 je read.retry # Yes, Retry.
690 read.error: mov %ah,%al # Save error
691 mov $hex_error,%di # Format it
693 mov $msg_badread,%si # Display Read error message
697 # Output the "twiddle"
699 twiddle: push %ax # Save
701 mov twiddle_index,%al # Load index
702 mov $twiddle_chars,%bx # Address table
705 mov %al,twiddle_index # Save index for next call
707 call putc # Output it
713 # Convert AL to hex, saving the result to [EDI].
715 hex8: pushl %eax # Save
716 shrb $0x4,%al # Do upper
719 hex8.1: andb $0xf,%al # Get lower 4
720 cmpb $0xa,%al # Convert
721 sbbb $0x69,%al # to hex
723 orb $0x20,%al # To lower case
724 mov %al,(%di) # Save char
729 # BTX client to start btxldr
732 btx_client: mov $(MEM_ARG_BTX-MEM_BTX_CLIENT+MEM_ARG_SIZE-4), %esi
735 mov $(MEM_ARG_SIZE/4),%ecx # Number of words to push
737 push_arg: lodsl # Read argument
738 push %eax # Push it onto the stack
739 loop push_arg # Push all of the arguments
740 cld # In case anyone depends on this
741 pushl MEM_ARG_BTX-MEM_BTX_CLIENT+MEM_ARG_SIZE # Entry point of
743 push %eax # Emulate a near call
744 mov $0x1,%eax # 'exec' system call
745 int $INT_SYS # BTX system call
751 # Global descriptor table.
753 gdt: .word 0x0,0x0,0x0,0x0 # Null entry
754 .word 0xffff,0x0000,0x9200,0x00cf # SEL_SDATA
755 .word 0xffff,0x0000,0x9200,0x0000 # SEL_RDATA
756 .word 0xffff,LOAD_SEG<<4,0x9a00,0x00cf # SEL_SCODE (32-bit)
757 .word 0xffff,LOAD_SEG<<4,0x9a00,0x008f # SEL_SCODE16 (16-bit)
760 # Pseudo-descriptors.
762 gdtdesc: .word gdt.1-gdt-1 # Limit
763 .long LOAD_SEG<<4 + gdt # Base
772 # State for searching dir
774 rec_lba: .long 0x0 # LBA (adjusted for EA)
775 rec_size: .long 0x0 # File size
776 name_len: .byte 0x0 # Length of current name
779 twiddle_index: .byte 0x0
781 msg_welcome: .asciz "CD Loader 1.2\r\n\n"
782 msg_bootinfo: .asciz "Building the boot loader arguments\r\n"
783 msg_relocate: .asciz "Relocating the loader and the BTX\r\n"
784 msg_jump: .asciz "Starting the BTX loader\r\n"
785 msg_badread: .ascii "Read Error: 0x"
786 hex_error: .asciz "00\r\n"
787 msg_novd: .asciz "Could not find Primary Volume Descriptor\r\n"
788 msg_lookup: .asciz "Looking up "
789 msg_lookup2: .asciz "... "
790 msg_lookupok: .asciz "Found\r\n"
791 msg_lookupfail: .asciz "File not found\r\n"
792 msg_load2big: .asciz "File too big\r\n"
793 msg_failed: .asciz "Boot failed\r\n"
794 twiddle_chars: .ascii "|/-\\"
795 loader_paths: .asciz "/BOOT.PC98/LOADER"
796 .asciz "/boot.pc98/loader"
797 .asciz "/BOOT/LOADER"
798 .asciz "/boot/loader"
805 .word 0xaa55 # Magic number