summaryrefslogtreecommitdiffstats
path: root/sys/boot/i386
diff options
context:
space:
mode:
Diffstat (limited to 'sys/boot/i386')
-rw-r--r--sys/boot/i386/Makefile8
-rw-r--r--sys/boot/i386/Makefile.inc17
-rw-r--r--sys/boot/i386/boot0/Makefile36
-rw-r--r--sys/boot/i386/boot0/boot0.S439
-rw-r--r--sys/boot/i386/boot0/boot0.s419
-rw-r--r--sys/boot/i386/boot0/boot0sio.s439
-rw-r--r--sys/boot/i386/boot0sio/Makefile7
-rw-r--r--sys/boot/i386/boot2/Makefile123
-rw-r--r--sys/boot/i386/boot2/boot1.S365
-rw-r--r--sys/boot/i386/boot2/boot1.s365
-rw-r--r--sys/boot/i386/boot2/boot2.c645
-rw-r--r--sys/boot/i386/boot2/lib.h24
-rw-r--r--sys/boot/i386/boot2/sio.S80
-rw-r--r--sys/boot/i386/boot2/sio.s80
-rw-r--r--sys/boot/i386/btx/Makefile5
-rw-r--r--sys/boot/i386/btx/Makefile.inc2
-rw-r--r--sys/boot/i386/btx/btx/Makefile40
-rw-r--r--sys/boot/i386/btx/btx/btx.S1173
-rw-r--r--sys/boot/i386/btx/btx/btx.s1173
-rw-r--r--sys/boot/i386/btx/btxldr/Makefile22
-rw-r--r--sys/boot/i386/btx/btxldr/btxldr.S396
-rw-r--r--sys/boot/i386/btx/btxldr/btxldr.s396
-rw-r--r--sys/boot/i386/btx/lib/Makefile11
-rw-r--r--sys/boot/i386/btx/lib/btxcsu.s43
-rw-r--r--sys/boot/i386/btx/lib/btxsys.s40
-rw-r--r--sys/boot/i386/btx/lib/btxv86.h63
-rw-r--r--sys/boot/i386/btx/lib/btxv86.s85
-rw-r--r--sys/boot/i386/cdboot/Makefile18
-rw-r--r--sys/boot/i386/cdboot/cdboot.s553
-rw-r--r--sys/boot/i386/gptboot/Makefile123
-rw-r--r--sys/boot/i386/gptboot/gptboot.c645
-rw-r--r--sys/boot/i386/kgzldr/Makefile25
-rw-r--r--sys/boot/i386/kgzldr/boot.c129
-rw-r--r--sys/boot/i386/kgzldr/crt.s83
-rw-r--r--sys/boot/i386/kgzldr/kgzldr.h41
-rw-r--r--sys/boot/i386/kgzldr/lib.c88
-rw-r--r--sys/boot/i386/kgzldr/sio.s44
-rw-r--r--sys/boot/i386/kgzldr/start.s45
-rw-r--r--sys/boot/i386/libi386/Makefile48
-rw-r--r--sys/boot/i386/libi386/amd64_tramp.S113
-rw-r--r--sys/boot/i386/libi386/biosacpi.c130
-rw-r--r--sys/boot/i386/libi386/bioscd.c359
-rw-r--r--sys/boot/i386/libi386/biosdisk.c1233
-rw-r--r--sys/boot/i386/libi386/biosmem.c107
-rw-r--r--sys/boot/i386/libi386/biospci.c294
-rw-r--r--sys/boot/i386/libi386/biospnp.c291
-rw-r--r--sys/boot/i386/libi386/biossmap.c108
-rw-r--r--sys/boot/i386/libi386/bootinfo.c157
-rw-r--r--sys/boot/i386/libi386/bootinfo32.c273
-rw-r--r--sys/boot/i386/libi386/bootinfo64.c208
-rw-r--r--sys/boot/i386/libi386/comconsole.c114
-rw-r--r--sys/boot/i386/libi386/devicename.c244
-rw-r--r--sys/boot/i386/libi386/elf32_freebsd.c76
-rw-r--r--sys/boot/i386/libi386/elf64_freebsd.c118
-rw-r--r--sys/boot/i386/libi386/gatea20.c54
-rw-r--r--sys/boot/i386/libi386/i386_copy.c91
-rw-r--r--sys/boot/i386/libi386/i386_module.c68
-rw-r--r--sys/boot/i386/libi386/libi386.h108
-rw-r--r--sys/boot/i386/libi386/nullconsole.c88
-rw-r--r--sys/boot/i386/libi386/pread.c80
-rw-r--r--sys/boot/i386/libi386/pxe.c609
-rw-r--r--sys/boot/i386/libi386/pxe.h522
-rw-r--r--sys/boot/i386/libi386/pxetramp.s38
-rw-r--r--sys/boot/i386/libi386/time.c83
-rw-r--r--sys/boot/i386/libi386/vidconsole.c632
-rw-r--r--sys/boot/i386/loader/Makefile130
-rw-r--r--sys/boot/i386/loader/conf.c120
-rw-r--r--sys/boot/i386/loader/help.i38664
-rw-r--r--sys/boot/i386/loader/loader.rc17
-rw-r--r--sys/boot/i386/loader/main.c296
-rw-r--r--sys/boot/i386/loader/version14
-rw-r--r--sys/boot/i386/mbr/Makefile17
-rw-r--r--sys/boot/i386/mbr/mbr.s157
-rw-r--r--sys/boot/i386/pxeldr/Makefile60
-rw-r--r--sys/boot/i386/pxeldr/pxeboot.8123
-rw-r--r--sys/boot/i386/pxeldr/pxeldr.S287
-rw-r--r--sys/boot/i386/pxeldr/pxeldr.s287
77 files changed, 16108 insertions, 0 deletions
diff --git a/sys/boot/i386/Makefile b/sys/boot/i386/Makefile
new file mode 100644
index 0000000..aac1821
--- /dev/null
+++ b/sys/boot/i386/Makefile
@@ -0,0 +1,8 @@
+# $FreeBSD$
+
+SUBDIR= mbr boot0 boot0sio btx boot2 cdboot kgzldr libi386 loader
+
+# special boot programs, 'self-extracting boot2+loader'
+SUBDIR+= pxeldr
+
+.include <bsd.subdir.mk>
diff --git a/sys/boot/i386/Makefile.inc b/sys/boot/i386/Makefile.inc
new file mode 100644
index 0000000..f8c00de
--- /dev/null
+++ b/sys/boot/i386/Makefile.inc
@@ -0,0 +1,17 @@
+# Common defines for all of /sys/boot/i386/
+#
+# $FreeBSD$
+
+LOADER_ADDRESS?= 0x200000
+CFLAGS+= -ffreestanding -mpreferred-stack-boundary=2
+
+.if ${MACHINE_ARCH} == "amd64"
+.MAKEFLAGS: MACHINE_ARCH=i386 MACHINE=i386 REALLY_AMD64=true
+.endif
+
+.if defined(REALLY_AMD64) && !defined(__been_to_Makefile_inc)
+__been_to_Makefile_inc= 1
+CFLAGS+= -m32
+LDFLAGS+= -m elf_i386_fbsd
+AFLAGS+= --32
+.endif
diff --git a/sys/boot/i386/boot0/Makefile b/sys/boot/i386/boot0/Makefile
new file mode 100644
index 0000000..825e50c
--- /dev/null
+++ b/sys/boot/i386/boot0/Makefile
@@ -0,0 +1,36 @@
+# $FreeBSD$
+
+PROG?= boot0
+NOMAN=
+STRIP=
+BINDIR?= /boot
+BINMODE= 444
+
+M4?= m4
+
+# The default set of flags compiled into boot0. This enables update (writing
+# the modified boot0 back to disk after running so that the selection made is
+# saved), packet mode (detect and use the BIOS EDD extensions if we try to
+# boot past the 1024 cylinder liimt), and booting from all valid slices.
+BOOT_BOOT0_FLAGS?= 0xf
+
+# The number of timer ticks to wait for a keypress before assuming the default
+# selection. Since there are 18.2 ticks per second, the default value of
+# 0xb6 (182d) corresponds to 10 seconds.
+BOOT_BOOT0_TICKS?= 0xb6
+
+# The base address that we the boot0 code to to run it. Don't change this
+# unless you are glutton for punishment.
+BOOT_BOOT0_ORG?= 0x600
+
+${PROG}: ${PROG}.o
+ ${LD} -N -e start -Ttext ${BOOT_BOOT0_ORG} -o ${PROG}.out ${PROG}.o
+ objcopy -S -O binary ${PROG}.out ${.TARGET}
+
+${PROG}.o: ${PROG}.s
+ ${AS} ${AFLAGS} --defsym FLAGS=${BOOT_BOOT0_FLAGS} \
+ --defsym TICKS=${BOOT_BOOT0_TICKS} ${.IMPSRC} -o ${.TARGET}
+
+CLEANFILES+= ${PROG}.out ${PROG}.o
+
+.include <bsd.prog.mk>
diff --git a/sys/boot/i386/boot0/boot0.S b/sys/boot/i386/boot0/boot0.S
new file mode 100644
index 0000000..c60e512
--- /dev/null
+++ b/sys/boot/i386/boot0/boot0.S
@@ -0,0 +1,439 @@
+#
+# Copyright (c) 2002 Bruce M. Simpson
+# Copyright (c) 1998 Robert Nordier
+# All rights reserved.
+#
+# Redistribution and use in source and binary forms are freely
+# permitted provided that the above copyright notice and this
+# paragraph and the following disclaimer are duplicated in all
+# such forms.
+#
+# This software is provided "AS IS" and without any express or
+# implied warranties, including, without limitation, the implied
+# warranties of merchantability and fitness for a particular
+# purpose.
+#
+
+# $FreeBSD$
+
+# A 512-byte boot manager which uses a serial console on COM1.
+
+ .set NHRDRV,0x475 # Number of hard drives
+ .set ORIGIN,0x600 # Execution address
+ .set FAKE,0x800 # Partition entry
+ .set LOAD,0x7c00 # Load address
+
+ .set PRT_OFF,0x1be # Partition table
+
+ .set TBL0SZ,0x3 # Table 0 size
+ .set TBL1SZ,0xb # Table 1 size
+
+ .set MAGIC,0xaa55 # Magic: bootable
+ .set B0MAGIC,0xbb66 # Identification
+
+ .set KEY_ENTER,0x1c # Enter key scan code
+ .set KEY_F1,0x3b # F1 key scan code
+ .set KEY_1,0x02 # #1 key scan code
+
+ .set ASCII_BEL,0x07 # ASCII code for <BEL>
+ .set ASCII_CR,0x0D # ASCII code for <CR>
+ .set ASCII_1,0x31 # ASCII code for '1'
+
+#
+# Addresses in the sector of embedded data values.
+# Accessed with negative offsets from the end of the relocated sector (%ebp).
+#
+ .set _NXTDRV,-0x48 # Next drive
+ .set _OPT,-0x47 # Default option
+ .set _SETDRV,-0x46 # Drive to force
+ .set _FLAGS,-0x45 # Flags
+ .set _TICKS,-0x44 # Timeout ticks
+ .set _FAKE,0x0 # Fake partition entry
+ .set _MNUOPT,0xc # Menu options
+
+ .globl start # Entry point
+ .code16 # This runs in real mode
+
+#
+# Initialise segments and registers to known values.
+# segments start at 0.
+# The stack is immediately below the address we were loaded to.
+#
+start: cld # String ops inc
+ xorw %ax,%ax # Zero
+ movw %ax,%es # Address
+ movw %ax,%ds # data
+ movw %ax,%ss # Set up
+ movw $LOAD,%sp # stack
+
+#
+# Copy this code to the address it was linked for
+#
+ movw %sp,%si # Source
+ movw $start,%di # Destination
+ movw $0x100,%cx # Word count
+ rep # Relocate
+ movsw # code
+#
+# Set address for variable space beyond code, and clear it.
+# Notice that this is also used to point to the values embedded in the block,
+# by using negative offsets.
+#
+ movw %di,%bp # Address variables
+ movb $0x8,%cl # Words to clear
+ rep # Zero
+ stosw # them
+#
+# Relocate to the new copy of the code.
+#
+ incb -0xe(%di) # Sector number
+ jmp main-LOAD+ORIGIN # To relocated code
+
+#
+# Initialize the serial port.
+# Must save DX (contains drive number)
+#
+main: pushw %dx # Save
+ xorw %dx,%dx # Port: COM1
+ movb $0xE3,%al # 9600 8-N-1
+
+ movb $0x00,%ah # BIOS: Set COM Port
+ int $0x14 # Parameters
+ popw %dx # Restore
+#
+# Check what flags were loaded with us, specifically, Use a predefined Drive.
+# If what the bios gives us is bad, use the '0' in the block instead, as well.
+#
+ testb $0x20,_FLAGS(%bp) # Set number drive?
+ jnz main.1 # Yes
+ testb %dl,%dl # Drive number valid?
+ js main.2 # Possibly (0x80 set)
+main.1: movb _SETDRV(%bp),%dl # Drive number to use
+#
+# Whatever we decided to use, now store it into the fake
+# partition entry that lives in the data space above us.
+#
+main.2: movb %dl,_FAKE(%bp) # Save drive number
+ callw putn # To new line
+ pushw %dx # Save drive number
+#
+# Start out with a pointer to the 4th byte of the first table entry
+# so that after 4 iterations it's beyond the end of the sector.
+# and beyond a 256 byte boundary and has overflowed 8 bits (see next comment).
+# (remember that the table starts 2 bytes earlier than you would expect
+# as the bootable flag is after it in the block)
+#
+ movw $(partbl+0x4),%bx # Partition table (+4)
+ xorw %dx,%dx # Item number
+#
+# Loop around on the partition table, printing values until we
+# pass a 256 byte boundary. The end of loop test is at main.5.
+#
+main.3: movb %ch,-0x4(%bx) # Zero active flag (ch == 0)
+ btw %dx,_FLAGS(%bp) # Entry enabled?
+ jnc main.5 # No
+#
+# If any of the entries in the table are
+# the same as the 'type' in the slice table entry,
+# then this is an empty or non bootable partition. Skip it.
+#
+ movb (%bx),%al # Load type
+ movw $tables,%di # Lookup tables
+ movb $TBL0SZ,%cl # Number of entries
+ repne # Exclude
+ scasb # partition?
+ je main.5 # Yes
+#
+# Now scan the table of known types
+#
+ movb $TBL1SZ,%cl # Number of entries
+ repne # Known
+ scasb # type?
+ jne main.4 # No
+#
+# If it matches get the matching element in the
+# next array. if it doesn't, we are already
+# pointing at its first element which points to a "?".
+#
+ addw $TBL1SZ,%di # Adjust
+main.4: movb (%di),%cl # Partition
+ addw %cx,%di # description
+ callw putx # Display it
+main.5: incw %dx # Next item
+ addb $0x10,%bl # Next entry
+ jnc main.3 # Till done
+#
+# Passed a 256 byte boundary..
+# table is finished.
+# Add one to the drive number and check it is valid,
+#
+ popw %ax # Drive number
+ subb $0x80-0x1,%al # Does next
+ cmpb NHRDRV,%al # drive exist? (from BIOS?)
+ jb main.6 # Yes
+# If not then if there is only one drive,
+# Don't display drive as an option.
+#
+ decw %ax # Already drive 0?
+ jz main.7 # Yes
+# If it was illegal or we cycled through them,
+# then go back to drive 0.
+#
+ xorb %al,%al # Drive 0
+#
+# Whatever drive we selected, make it an ascii digit and save it back
+# to the "next drive" location in the loaded block in case we
+# want to save it for next time.
+# This also is part of the printed drive string so add 0x80 to indicate
+# end of string.
+#
+main.6: addb $'0'|0x80,%al # Save next
+ movb %al,_NXTDRV(%bp) # drive number
+ movw $drive,%di # Display
+ callw putx # item
+#
+# Now that we've printed the drive (if we needed to), display a prompt.
+# Get ready for the input byte noting the time.
+#
+main.7: movw $prompt,%si # Display
+ callw putstr # prompt
+ movb _OPT(%bp),%dl # Display
+ decw %si # default
+ callw putkey # key
+main.7_1:
+ xorb %ah,%ah # BIOS: Get
+ int $0x1a # system time
+ movw %dx,%si # Ticks when
+ addw _TICKS(%bp),%si # timeout
+
+#
+# Busy loop, checking for a character on the serial port, but
+# keeping one eye on the time.
+#
+
+main.8: xorw %dx,%dx # Use COM1
+ movb $0x03,%ah # BIOS: Read COM
+ int $0x14 # Status
+ testb $0x01,%ah # Check line status
+ jnz main.11 # (bit 1 indicates input)
+
+ xorb %ah,%ah # BIOS: Get
+ int $0x1a # system time
+ cmpw %si,%dx # Timeout?
+ jb main.8 # No
+
+#
+# If timed out or defaulting, come here.
+#
+main.9: movb _OPT(%bp),%al # Load default
+ jmp main.12 # Join common code
+#
+# User's last try was bad, beep in displeasure.
+# Since nothing was printed, just continue on as if the user
+# hadn't done anything. This gives the effect of the user getting a beep
+# for all bad keystrokes but no action until either the timeout
+# occurs or the user hits a good key.
+#
+main.10: movb $ASCII_BEL,%al # Signal
+ callw putchr # error
+ jmp main.7_1 # Go back
+#
+# Check the character we just got on the serial port.
+#
+main.11: movb $0x02,%ah # BIOS: Receive
+ int $0x14 # COM Byte
+#
+# If it's CR act as if timed out.
+#
+ cmpb $ASCII_CR,%al # Enter pressed?
+ je main.9 # Yes
+#
+# Otherwise check if legal
+# If not ask again.
+#
+ subb $ASCII_1,%al # Less '1' ascii character
+ cmpb $0x4,%al # #1..#5?
+ ja main.10 # No
+#
+# We have a selection.
+# but if it's a bad selection go back to complain.
+# The bits in MNUOPT were set when the options were printed.
+# Anything not printed is not an option.
+#
+main.12: cbtw # Option
+ btw %ax,_MNUOPT(%bp) # enabled?
+ jnc main.10 # No
+#
+# Save the info in the original tables
+# for rewriting to the disk.
+#
+ movb %al,_OPT(%bp) # Save option
+ movw $FAKE,%si # Partition for write
+ movb (%si),%dl # Drive number
+ movw %si,%bx # Partition for read
+ cmpb $0x4,%al # F5/#5 pressed?
+ pushf # Save
+ je main.13 # Yes
+ shlb $0x4,%al # Point to
+ addw $partbl,%ax # selected
+ xchgw %bx,%ax # partition
+ movb $0x80,(%bx) # Flag active
+#
+# If not asked to do a write-back (flags 0x40) don't do one.
+#
+main.13: pushw %bx # Save
+ testb $0x40,_FLAGS(%bp) # No updates?
+ jnz main.14 # Yes
+ movw $start,%bx # Data to write
+ movb $0x3,%ah # Write sector
+ callw intx13 # to disk
+main.14: popw %si # Restore
+ popf # Restore
+#
+# If going to next drive, replace drive with selected one.
+# Remember to un-ascii it. Hey 0x80 is already set, cool!
+#
+ jne main.15 # If not F5/#5
+ movb _NXTDRV(%bp),%dl # Next drive
+ subb $'0',%dl # number
+#
+# load selected bootsector to the LOAD location in RAM.
+# If it fails to read or isn't marked bootable, treat it
+# as a bad selection.
+# XXX what does %si carry?
+#
+main.15: movw $LOAD,%bx # Address for read
+ movb $0x2,%ah # Read sector
+ callw intx13 # from disk
+ jc main.10 # If error
+ cmpw $MAGIC,0x1fe(%bx) # Bootable?
+ jne main.10 # No
+ pushw %si # Save
+ movw $crlf,%si # Leave some
+ callw puts # space
+ popw %si # Restore
+ jmp *%bx # Invoke bootstrap
+#
+# Display routines
+#
+
+putkey: movb $ASCII_1,%al # Prepare
+ addb %dl,%al # digit
+ jmp putstr.1 # Display the rest
+
+#
+# Display the option and note that it is a valid option.
+# That last point is a bit tricky..
+#
+putx: btsw %dx,_MNUOPT(%bp) # Enable menu option
+ movw $item,%si # Display
+ callw putkey # key
+ movw %di,%si # Display the rest
+
+puts: callw putstr # Display string
+
+putn: movw $crlf,%si # To next line
+
+putstr: lodsb # Get byte
+ testb $0x80,%al # End of string?
+ jnz putstr.2 # Yes
+putstr.1: callw putchr # Display char
+ jmp putstr # Continue
+putstr.2: andb $~0x80,%al # Clear MSB
+
+putchr: pushw %dx # Save
+ xorw %dx,%dx # Use COM1
+ xorw %cx,%cx # No timeout
+ movb $0x01,%ah # BIOS: Send
+ int $0x14 # Character
+ popw %dx # Restore
+ retw # To caller
+
+# One-sector disk I/O routine
+
+intx13: movb 0x1(%si),%dh # Load head
+ movw 0x2(%si),%cx # Load cylinder:sector
+ movb $0x1,%al # Sector count
+ pushw %si # Save
+ movw %sp,%di # Save
+ testb $0x80,_FLAGS(%bp) # Use packet interface?
+ jz intx13.1 # No
+ pushl $0x0 # Set the
+ pushl 0x8(%si) # LBA address
+ pushw %es # Set the transfer
+ pushw %bx # buffer address
+ push $0x1 # Block count
+ push $0x10 # Packet size
+ movw %sp,%si # Packet pointer
+ decw %ax # Verify off
+ orb $0x40,%ah # Use disk packet
+intx13.1: int $0x13 # BIOS: Disk I/O
+ movw %di,%sp # Restore
+ popw %si # Restore
+ retw # To caller
+
+# Menu strings
+
+item: .ascii " "; .byte ' '|0x80
+prompt: .ascii "\nDef:"; .byte ' '|0x80
+crlf: .ascii "\r"; .byte '\n'|0x80
+
+# Partition type tables
+
+tables:
+#
+# These entries identify invalid or NON BOOT types and partitions.
+#
+ .byte 0x0, 0x5, 0xf
+#
+# These values indicate bootable types we know the names of
+#
+ .byte 0x1, 0x4, 0x6, 0xb, 0xc, 0xe, 0x83
+ .byte 0x9f, 0xa5, 0xa6, 0xa9
+#
+# These are offsets that match the known names above and point to the strings
+# that will be printed.
+#
+ .byte os_misc-. # Unknown
+ .byte os_dos-. # DOS
+ .byte os_dos-. # DOS
+ .byte os_dos-. # DOS
+ .byte os_dos-. # Windows
+ .byte os_dos-. # Windows
+ .byte os_dos-. # Windows
+ .byte os_linux-. # Linux
+ .byte os_bsd-. # BSD/OS
+ .byte os_freebsd-. # FreeBSD
+ .byte os_bsd-. # OpenBSD
+ .byte os_bsd-. # NetBSD
+#
+# And here are the strings themselves. 0x80 or'd into a byte indicates
+# the end of the string. (not so great for Russians but...)
+#
+os_misc: .ascii "?"; .byte '?'|0x80
+os_dos: .ascii "DO"; .byte 'S'|0x80
+os_linux: .ascii "Linu"; .byte 'x'|0x80
+os_freebsd: .ascii "Free"
+os_bsd: .ascii "BS"; .byte 'D'|0x80
+
+ .org PRT_OFF-0xe,0x90
+
+ .word B0MAGIC # Magic number
+
+#
+# These values are sometimes changed before writing back to the drive
+# Be especially careful that nxtdrv: must come after drive:, as it
+# is part of the same string.
+#
+drive: .ascii "Drive "
+nxtdrv: .byte 0x0 # Next drive number
+opt: .byte 0x0 # Option
+setdrv: .byte 0x80 # Drive to force
+flags: .byte FLAGS # Flags
+ticks: .word TICKS # Delay
+
+#
+# here is the 64 byte partition table that fdisk would fiddle with.
+#
+partbl: .fill 0x40,0x1,0x0 # Partition table
+ .word MAGIC # Magic number
diff --git a/sys/boot/i386/boot0/boot0.s b/sys/boot/i386/boot0/boot0.s
new file mode 100644
index 0000000..5774052
--- /dev/null
+++ b/sys/boot/i386/boot0/boot0.s
@@ -0,0 +1,419 @@
+#
+# Copyright (c) 1998 Robert Nordier
+# All rights reserved.
+#
+# Redistribution and use in source and binary forms are freely
+# permitted provided that the above copyright notice and this
+# paragraph and the following disclaimer are duplicated in all
+# such forms.
+#
+# This software is provided "AS IS" and without any express or
+# implied warranties, including, without limitation, the implied
+# warranties of merchantability and fitness for a particular
+# purpose.
+#
+
+# $FreeBSD$
+
+# A 512-byte boot manager.
+
+ .set NHRDRV,0x475 # Number of hard drives
+ .set ORIGIN,0x600 # Execution address
+ .set FAKE,0x800 # Partition entry
+ .set LOAD,0x7c00 # Load address
+
+ .set PRT_OFF,0x1be # Partition table
+
+ .set TBL0SZ,0x3 # Table 0 size
+ .set TBL1SZ,0xb # Table 1 size
+
+ .set MAGIC,0xaa55 # Magic: bootable
+ .set B0MAGIC,0xbb66 # Identification
+
+ .set KEY_ENTER,0x1c # Enter key scan code
+ .set KEY_F1,0x3b # F1 key scan code
+ .set KEY_1,0x02 # #1 key scan code
+
+#
+# Addresses in the sector of embedded data values.
+# Accessed with negative offsets from the end of the relocated sector (%ebp).
+#
+ .set _NXTDRV,-0x48 # Next drive
+ .set _OPT,-0x47 # Default option
+ .set _SETDRV,-0x46 # Drive to force
+ .set _FLAGS,-0x45 # Flags
+ .set _TICKS,-0x44 # Timeout ticks
+ .set _FAKE,0x0 # Fake partition entry
+ .set _MNUOPT,0xc # Menu options
+
+ .globl start # Entry point
+ .code16 # This runs in real mode
+
+#
+# Initialise segments and registers to known values.
+# segments start at 0.
+# The stack is immediately below the address we were loaded to.
+#
+start: cld # String ops inc
+ xorw %ax,%ax # Zero
+ movw %ax,%es # Address
+ movw %ax,%ds # data
+ movw %ax,%ss # Set up
+ movw $LOAD,%sp # stack
+
+#
+# Copy this code to the address it was linked for
+#
+ movw %sp,%si # Source
+ movw $start,%di # Destination
+ movw $0x100,%cx # Word count
+ rep # Relocate
+ movsw # code
+#
+# Set address for variable space beyond code, and clear it.
+# Notice that this is also used to point to the values embedded in the block,
+# by using negative offsets.
+#
+ movw %di,%bp # Address variables
+ movb $0x8,%cl # Words to clear
+ rep # Zero
+ stosw # them
+#
+# Relocate to the new copy of the code.
+#
+ incb -0xe(%di) # Sector number
+ jmp main-LOAD+ORIGIN # To relocated code
+#
+# Check what flags were loaded with us, specifically, Use a predefined Drive.
+# If what the bios gives us is bad, use the '0' in the block instead, as well.
+#
+main: testb $0x20,_FLAGS(%bp) # Set number drive?
+ jnz main.1 # Yes
+ testb %dl,%dl # Drive number valid?
+ js main.2 # Possibly (0x80 set)
+main.1: movb _SETDRV(%bp),%dl # Drive number to use
+#
+# Whatever we decided to use, now store it into the fake
+# partition entry that lives in the data space above us.
+#
+main.2: movb %dl,_FAKE(%bp) # Save drive number
+ callw putn # To new line
+ pushw %dx # Save drive number
+#
+# Start out with a pointer to the 4th byte of the first table entry
+# so that after 4 iterations it's beyond the end of the sector.
+# and beyond a 256 byte boundary and has overflowed 8 bits (see next comment).
+# (remember that the table starts 2 bytes earlier than you would expect
+# as the bootable flag is after it in the block)
+#
+ movw $(partbl+0x4),%bx # Partition table (+4)
+ xorw %dx,%dx # Item number
+#
+# Loop around on the partition table, printing values until we
+# pass a 256 byte boundary. The end of loop test is at main.5.
+#
+main.3: movb %ch,-0x4(%bx) # Zero active flag (ch == 0)
+ btw %dx,_FLAGS(%bp) # Entry enabled?
+ jnc main.5 # No
+#
+# If any of the entries in the table are
+# the same as the 'type' in the slice table entry,
+# then this is an empty or non bootable partition. Skip it.
+#
+ movb (%bx),%al # Load type
+ movw $tables,%di # Lookup tables
+ movb $TBL0SZ,%cl # Number of entries
+ repne # Exclude
+ scasb # partition?
+ je main.5 # Yes
+#
+# Now scan the table of known types
+#
+ movb $TBL1SZ,%cl # Number of entries
+ repne # Known
+ scasb # type?
+ jne main.4 # No
+#
+# If it matches get the matching element in the
+# next array. if it doesn't, we are already
+# pointing at its first element which points to a "?".
+#
+ addw $TBL1SZ,%di # Adjust
+main.4: movb (%di),%cl # Partition
+ addw %cx,%di # description
+ callw putx # Display it
+main.5: incw %dx # Next item
+ addb $0x10,%bl # Next entry
+ jnc main.3 # Till done
+#
+# Passed a 256 byte boundary..
+# table is finished.
+# Add one to the drive number and check it is valid,
+#
+ popw %ax # Drive number
+ subb $0x80-0x1,%al # Does next
+ cmpb NHRDRV,%al # drive exist? (from BIOS?)
+ jb main.6 # Yes
+# If not then if there is only one drive,
+# Don't display drive as an option.
+#
+ decw %ax # Already drive 0?
+ jz main.7 # Yes
+# If it was illegal or we cycled through them,
+# then go back to drive 0.
+#
+ xorb %al,%al # Drive 0
+#
+# Whatever drive we selected, make it an ascii digit and save it back
+# to the "next drive" location in the loaded block in case we
+# want to save it for next time.
+# This also is part of the printed drive string so add 0x80 to indicate
+# end of string.
+#
+main.6: addb $'0'|0x80,%al # Save next
+ movb %al,_NXTDRV(%bp) # drive number
+ movw $drive,%di # Display
+ callw putx # item
+#
+# Now that we've printed the drive (if we needed to), display a prompt.
+# Get ready for the input byte noting the time.
+#
+main.7: movw $prompt,%si # Display
+ callw putstr # prompt
+ movb _OPT(%bp),%dl # Display
+ decw %si # default
+ callw putkey # key
+ xorb %ah,%ah # BIOS: Get
+ int $0x1a # system time
+ movw %dx,%di # Ticks when
+ addw _TICKS(%bp),%di # timeout
+#
+# Busy loop, looking for keystrokes but
+# keeping one eye on the time.
+#
+main.8: movb $0x1,%ah # BIOS: Check
+ int $0x16 # for keypress
+ jnz main.11 # Have one
+ xorb %ah,%ah # BIOS: Get
+ int $0x1a # system time
+ cmpw %di,%dx # Timeout?
+ jb main.8 # No
+#
+# If timed out or defaulting, come here.
+#
+main.9: movb _OPT(%bp),%al # Load default
+ jmp main.12 # Join common code
+#
+# User's last try was bad, beep in displeasure.
+# Since nothing was printed, just continue on as if the user
+# hadn't done anything. This gives the effect of the user getting a beep
+# for all bad keystrokes but no action until either the timeout
+# occurs or the user hits a good key.
+#
+main.10: movb $0x7,%al # Signal
+ callw putchr # error
+#
+# Get the keystroke.
+#
+main.11: xorb %ah,%ah # BIOS: Get
+ int $0x16 # keypress
+ movb %ah,%al # Scan code
+#
+# If it's CR act as if timed out.
+#
+ cmpb $KEY_ENTER,%al # Enter pressed?
+ je main.9 # Yes
+#
+# Otherwise check if legal
+# If not ask again.
+#
+ subb $KEY_F1,%al # Less F1 scan code
+ cmpb $0x4,%al # F1..F5?
+ jna main.12 # Yes
+ subb $(KEY_1 - KEY_F1),%al # Less #1 scan code
+ cmpb $0x4,%al # #1..#5?
+ ja main.10 # No
+#
+# We have a selection.
+# but if it's a bad selection go back to complain.
+# The bits in MNUOPT were set when the options were printed.
+# Anything not printed is not an option.
+#
+main.12: cbtw # Option
+ btw %ax,_MNUOPT(%bp) # enabled?
+ jnc main.10 # No
+#
+# Save the info in the original tables
+# for rewriting to the disk.
+#
+ movb %al,_OPT(%bp) # Save option
+ movw $FAKE,%si # Partition for write
+ movb (%si),%dl # Drive number
+ movw %si,%bx # Partition for read
+ cmpb $0x4,%al # F5/#5 pressed?
+ pushf # Save
+ je main.13 # Yes
+ shlb $0x4,%al # Point to
+ addw $partbl,%ax # selected
+ xchgw %bx,%ax # partition
+ movb $0x80,(%bx) # Flag active
+#
+# If not asked to do a write-back (flags 0x40) don't do one.
+#
+main.13: pushw %bx # Save
+ testb $0x40,_FLAGS(%bp) # No updates?
+ jnz main.14 # Yes
+ movw $start,%bx # Data to write
+ movb $0x3,%ah # Write sector
+ callw intx13 # to disk
+main.14: popw %si # Restore
+ popf # Restore
+#
+# If going to next drive, replace drive with selected one.
+# Remember to un-ascii it. Hey 0x80 is already set, cool!
+#
+ jne main.15 # If not F5/#5
+ movb _NXTDRV(%bp),%dl # Next drive
+ subb $'0',%dl # number
+#
+# load selected bootsector to the LOAD location in RAM.
+# If it fails to read or isn't marked bootable, treat it
+# as a bad selection.
+# XXX what does %si carry?
+#
+main.15: movw $LOAD,%bx # Address for read
+ movb $0x2,%ah # Read sector
+ callw intx13 # from disk
+ jc main.10 # If error
+ cmpw $MAGIC,0x1fe(%bx) # Bootable?
+ jne main.10 # No
+ pushw %si # Save
+ movw $crlf,%si # Leave some
+ callw puts # space
+ popw %si # Restore
+ jmp *%bx # Invoke bootstrap
+#
+# Display routines
+#
+
+putkey: movb $'F',%al # Display
+ callw putchr # 'F'
+ movb $'1',%al # Prepare
+ addb %dl,%al # digit
+ jmp putstr.1 # Display the rest
+
+#
+# Display the option and note that it is a valid option.
+# That last point is a bit tricky..
+#
+putx: btsw %dx,_MNUOPT(%bp) # Enable menu option
+ movw $item,%si # Display
+ callw putkey # key
+ movw %di,%si # Display the rest
+
+puts: callw putstr # Display string
+
+putn: movw $crlf,%si # To next line
+
+putstr: lodsb # Get byte
+ testb $0x80,%al # End of string?
+ jnz putstr.2 # Yes
+putstr.1: callw putchr # Display char
+ jmp putstr # Continue
+putstr.2: andb $~0x80,%al # Clear MSB
+
+putchr: pushw %bx # Save
+ movw $0x7,%bx # Page:attribute
+ movb $0xe,%ah # BIOS: Display
+ int $0x10 # character
+ popw %bx # Restore
+ retw # To caller
+
+# One-sector disk I/O routine
+
+intx13: movb 0x1(%si),%dh # Load head
+ movw 0x2(%si),%cx # Load cylinder:sector
+ movb $0x1,%al # Sector count
+ pushw %si # Save
+ movw %sp,%di # Save
+ testb $0x80,_FLAGS(%bp) # Use packet interface?
+ jz intx13.1 # No
+ pushl $0x0 # Set the
+ pushl 0x8(%si) # LBA address
+ pushw %es # Set the transfer
+ pushw %bx # buffer address
+ push $0x1 # Block count
+ push $0x10 # Packet size
+ movw %sp,%si # Packet pointer
+ decw %ax # Verify off
+ orb $0x40,%ah # Use disk packet
+intx13.1: int $0x13 # BIOS: Disk I/O
+ movw %di,%sp # Restore
+ popw %si # Restore
+ retw # To caller
+
+# Menu strings
+
+item: .ascii " "; .byte ' '|0x80
+prompt: .ascii "\nDefault:"; .byte ' '|0x80
+crlf: .ascii "\r"; .byte '\n'|0x80
+
+# Partition type tables
+
+tables:
+#
+# These entries identify invalid or NON BOOT types and partitions.
+#
+ .byte 0x0, 0x5, 0xf
+#
+# These values indicate bootable types we know the names of
+#
+ .byte 0x1, 0x4, 0x6, 0xb, 0xc, 0xe, 0x83
+ .byte 0x9f, 0xa5, 0xa6, 0xa9
+#
+# These are offsets that match the known names above and point to the strings
+# that will be printed.
+#
+ .byte os_misc-. # Unknown
+ .byte os_dos-. # DOS
+ .byte os_dos-. # DOS
+ .byte os_dos-. # DOS
+ .byte os_dos-. # Windows
+ .byte os_dos-. # Windows
+ .byte os_dos-. # Windows
+ .byte os_linux-. # Linux
+ .byte os_bsd-. # BSD/OS
+ .byte os_freebsd-. # FreeBSD
+ .byte os_bsd-. # OpenBSD
+ .byte os_bsd-. # NetBSD
+#
+# And here are the strings themselves. 0x80 or'd into a byte indicates
+# the end of the string. (not so great for Russians but...)
+#
+os_misc: .ascii "?"; .byte '?'|0x80
+os_dos: .ascii "DO"; .byte 'S'|0x80
+os_linux: .ascii "Linu"; .byte 'x'|0x80
+os_freebsd: .ascii "Free"
+os_bsd: .ascii "BS"; .byte 'D'|0x80
+
+ .org PRT_OFF-0xe,0x90
+
+ .word B0MAGIC # Magic number
+
+#
+# These values are sometimes changed before writing back to the drive
+# Be especially careful that nxtdrv: must come after drive:, as it
+# is part of the same string.
+#
+drive: .ascii "Drive "
+nxtdrv: .byte 0x0 # Next drive number
+opt: .byte 0x0 # Option
+setdrv: .byte 0x80 # Drive to force
+flags: .byte FLAGS # Flags
+ticks: .word TICKS # Delay
+
+#
+# here is the 64 byte partition table that fdisk would fiddle with.
+#
+partbl: .fill 0x40,0x1,0x0 # Partition table
+ .word MAGIC # Magic number
diff --git a/sys/boot/i386/boot0/boot0sio.s b/sys/boot/i386/boot0/boot0sio.s
new file mode 100644
index 0000000..c60e512
--- /dev/null
+++ b/sys/boot/i386/boot0/boot0sio.s
@@ -0,0 +1,439 @@
+#
+# Copyright (c) 2002 Bruce M. Simpson
+# Copyright (c) 1998 Robert Nordier
+# All rights reserved.
+#
+# Redistribution and use in source and binary forms are freely
+# permitted provided that the above copyright notice and this
+# paragraph and the following disclaimer are duplicated in all
+# such forms.
+#
+# This software is provided "AS IS" and without any express or
+# implied warranties, including, without limitation, the implied
+# warranties of merchantability and fitness for a particular
+# purpose.
+#
+
+# $FreeBSD$
+
+# A 512-byte boot manager which uses a serial console on COM1.
+
+ .set NHRDRV,0x475 # Number of hard drives
+ .set ORIGIN,0x600 # Execution address
+ .set FAKE,0x800 # Partition entry
+ .set LOAD,0x7c00 # Load address
+
+ .set PRT_OFF,0x1be # Partition table
+
+ .set TBL0SZ,0x3 # Table 0 size
+ .set TBL1SZ,0xb # Table 1 size
+
+ .set MAGIC,0xaa55 # Magic: bootable
+ .set B0MAGIC,0xbb66 # Identification
+
+ .set KEY_ENTER,0x1c # Enter key scan code
+ .set KEY_F1,0x3b # F1 key scan code
+ .set KEY_1,0x02 # #1 key scan code
+
+ .set ASCII_BEL,0x07 # ASCII code for <BEL>
+ .set ASCII_CR,0x0D # ASCII code for <CR>
+ .set ASCII_1,0x31 # ASCII code for '1'
+
+#
+# Addresses in the sector of embedded data values.
+# Accessed with negative offsets from the end of the relocated sector (%ebp).
+#
+ .set _NXTDRV,-0x48 # Next drive
+ .set _OPT,-0x47 # Default option
+ .set _SETDRV,-0x46 # Drive to force
+ .set _FLAGS,-0x45 # Flags
+ .set _TICKS,-0x44 # Timeout ticks
+ .set _FAKE,0x0 # Fake partition entry
+ .set _MNUOPT,0xc # Menu options
+
+ .globl start # Entry point
+ .code16 # This runs in real mode
+
+#
+# Initialise segments and registers to known values.
+# segments start at 0.
+# The stack is immediately below the address we were loaded to.
+#
+start: cld # String ops inc
+ xorw %ax,%ax # Zero
+ movw %ax,%es # Address
+ movw %ax,%ds # data
+ movw %ax,%ss # Set up
+ movw $LOAD,%sp # stack
+
+#
+# Copy this code to the address it was linked for
+#
+ movw %sp,%si # Source
+ movw $start,%di # Destination
+ movw $0x100,%cx # Word count
+ rep # Relocate
+ movsw # code
+#
+# Set address for variable space beyond code, and clear it.
+# Notice that this is also used to point to the values embedded in the block,
+# by using negative offsets.
+#
+ movw %di,%bp # Address variables
+ movb $0x8,%cl # Words to clear
+ rep # Zero
+ stosw # them
+#
+# Relocate to the new copy of the code.
+#
+ incb -0xe(%di) # Sector number
+ jmp main-LOAD+ORIGIN # To relocated code
+
+#
+# Initialize the serial port.
+# Must save DX (contains drive number)
+#
+main: pushw %dx # Save
+ xorw %dx,%dx # Port: COM1
+ movb $0xE3,%al # 9600 8-N-1
+
+ movb $0x00,%ah # BIOS: Set COM Port
+ int $0x14 # Parameters
+ popw %dx # Restore
+#
+# Check what flags were loaded with us, specifically, Use a predefined Drive.
+# If what the bios gives us is bad, use the '0' in the block instead, as well.
+#
+ testb $0x20,_FLAGS(%bp) # Set number drive?
+ jnz main.1 # Yes
+ testb %dl,%dl # Drive number valid?
+ js main.2 # Possibly (0x80 set)
+main.1: movb _SETDRV(%bp),%dl # Drive number to use
+#
+# Whatever we decided to use, now store it into the fake
+# partition entry that lives in the data space above us.
+#
+main.2: movb %dl,_FAKE(%bp) # Save drive number
+ callw putn # To new line
+ pushw %dx # Save drive number
+#
+# Start out with a pointer to the 4th byte of the first table entry
+# so that after 4 iterations it's beyond the end of the sector.
+# and beyond a 256 byte boundary and has overflowed 8 bits (see next comment).
+# (remember that the table starts 2 bytes earlier than you would expect
+# as the bootable flag is after it in the block)
+#
+ movw $(partbl+0x4),%bx # Partition table (+4)
+ xorw %dx,%dx # Item number
+#
+# Loop around on the partition table, printing values until we
+# pass a 256 byte boundary. The end of loop test is at main.5.
+#
+main.3: movb %ch,-0x4(%bx) # Zero active flag (ch == 0)
+ btw %dx,_FLAGS(%bp) # Entry enabled?
+ jnc main.5 # No
+#
+# If any of the entries in the table are
+# the same as the 'type' in the slice table entry,
+# then this is an empty or non bootable partition. Skip it.
+#
+ movb (%bx),%al # Load type
+ movw $tables,%di # Lookup tables
+ movb $TBL0SZ,%cl # Number of entries
+ repne # Exclude
+ scasb # partition?
+ je main.5 # Yes
+#
+# Now scan the table of known types
+#
+ movb $TBL1SZ,%cl # Number of entries
+ repne # Known
+ scasb # type?
+ jne main.4 # No
+#
+# If it matches get the matching element in the
+# next array. if it doesn't, we are already
+# pointing at its first element which points to a "?".
+#
+ addw $TBL1SZ,%di # Adjust
+main.4: movb (%di),%cl # Partition
+ addw %cx,%di # description
+ callw putx # Display it
+main.5: incw %dx # Next item
+ addb $0x10,%bl # Next entry
+ jnc main.3 # Till done
+#
+# Passed a 256 byte boundary..
+# table is finished.
+# Add one to the drive number and check it is valid,
+#
+ popw %ax # Drive number
+ subb $0x80-0x1,%al # Does next
+ cmpb NHRDRV,%al # drive exist? (from BIOS?)
+ jb main.6 # Yes
+# If not then if there is only one drive,
+# Don't display drive as an option.
+#
+ decw %ax # Already drive 0?
+ jz main.7 # Yes
+# If it was illegal or we cycled through them,
+# then go back to drive 0.
+#
+ xorb %al,%al # Drive 0
+#
+# Whatever drive we selected, make it an ascii digit and save it back
+# to the "next drive" location in the loaded block in case we
+# want to save it for next time.
+# This also is part of the printed drive string so add 0x80 to indicate
+# end of string.
+#
+main.6: addb $'0'|0x80,%al # Save next
+ movb %al,_NXTDRV(%bp) # drive number
+ movw $drive,%di # Display
+ callw putx # item
+#
+# Now that we've printed the drive (if we needed to), display a prompt.
+# Get ready for the input byte noting the time.
+#
+main.7: movw $prompt,%si # Display
+ callw putstr # prompt
+ movb _OPT(%bp),%dl # Display
+ decw %si # default
+ callw putkey # key
+main.7_1:
+ xorb %ah,%ah # BIOS: Get
+ int $0x1a # system time
+ movw %dx,%si # Ticks when
+ addw _TICKS(%bp),%si # timeout
+
+#
+# Busy loop, checking for a character on the serial port, but
+# keeping one eye on the time.
+#
+
+main.8: xorw %dx,%dx # Use COM1
+ movb $0x03,%ah # BIOS: Read COM
+ int $0x14 # Status
+ testb $0x01,%ah # Check line status
+ jnz main.11 # (bit 1 indicates input)
+
+ xorb %ah,%ah # BIOS: Get
+ int $0x1a # system time
+ cmpw %si,%dx # Timeout?
+ jb main.8 # No
+
+#
+# If timed out or defaulting, come here.
+#
+main.9: movb _OPT(%bp),%al # Load default
+ jmp main.12 # Join common code
+#
+# User's last try was bad, beep in displeasure.
+# Since nothing was printed, just continue on as if the user
+# hadn't done anything. This gives the effect of the user getting a beep
+# for all bad keystrokes but no action until either the timeout
+# occurs or the user hits a good key.
+#
+main.10: movb $ASCII_BEL,%al # Signal
+ callw putchr # error
+ jmp main.7_1 # Go back
+#
+# Check the character we just got on the serial port.
+#
+main.11: movb $0x02,%ah # BIOS: Receive
+ int $0x14 # COM Byte
+#
+# If it's CR act as if timed out.
+#
+ cmpb $ASCII_CR,%al # Enter pressed?
+ je main.9 # Yes
+#
+# Otherwise check if legal
+# If not ask again.
+#
+ subb $ASCII_1,%al # Less '1' ascii character
+ cmpb $0x4,%al # #1..#5?
+ ja main.10 # No
+#
+# We have a selection.
+# but if it's a bad selection go back to complain.
+# The bits in MNUOPT were set when the options were printed.
+# Anything not printed is not an option.
+#
+main.12: cbtw # Option
+ btw %ax,_MNUOPT(%bp) # enabled?
+ jnc main.10 # No
+#
+# Save the info in the original tables
+# for rewriting to the disk.
+#
+ movb %al,_OPT(%bp) # Save option
+ movw $FAKE,%si # Partition for write
+ movb (%si),%dl # Drive number
+ movw %si,%bx # Partition for read
+ cmpb $0x4,%al # F5/#5 pressed?
+ pushf # Save
+ je main.13 # Yes
+ shlb $0x4,%al # Point to
+ addw $partbl,%ax # selected
+ xchgw %bx,%ax # partition
+ movb $0x80,(%bx) # Flag active
+#
+# If not asked to do a write-back (flags 0x40) don't do one.
+#
+main.13: pushw %bx # Save
+ testb $0x40,_FLAGS(%bp) # No updates?
+ jnz main.14 # Yes
+ movw $start,%bx # Data to write
+ movb $0x3,%ah # Write sector
+ callw intx13 # to disk
+main.14: popw %si # Restore
+ popf # Restore
+#
+# If going to next drive, replace drive with selected one.
+# Remember to un-ascii it. Hey 0x80 is already set, cool!
+#
+ jne main.15 # If not F5/#5
+ movb _NXTDRV(%bp),%dl # Next drive
+ subb $'0',%dl # number
+#
+# load selected bootsector to the LOAD location in RAM.
+# If it fails to read or isn't marked bootable, treat it
+# as a bad selection.
+# XXX what does %si carry?
+#
+main.15: movw $LOAD,%bx # Address for read
+ movb $0x2,%ah # Read sector
+ callw intx13 # from disk
+ jc main.10 # If error
+ cmpw $MAGIC,0x1fe(%bx) # Bootable?
+ jne main.10 # No
+ pushw %si # Save
+ movw $crlf,%si # Leave some
+ callw puts # space
+ popw %si # Restore
+ jmp *%bx # Invoke bootstrap
+#
+# Display routines
+#
+
+putkey: movb $ASCII_1,%al # Prepare
+ addb %dl,%al # digit
+ jmp putstr.1 # Display the rest
+
+#
+# Display the option and note that it is a valid option.
+# That last point is a bit tricky..
+#
+putx: btsw %dx,_MNUOPT(%bp) # Enable menu option
+ movw $item,%si # Display
+ callw putkey # key
+ movw %di,%si # Display the rest
+
+puts: callw putstr # Display string
+
+putn: movw $crlf,%si # To next line
+
+putstr: lodsb # Get byte
+ testb $0x80,%al # End of string?
+ jnz putstr.2 # Yes
+putstr.1: callw putchr # Display char
+ jmp putstr # Continue
+putstr.2: andb $~0x80,%al # Clear MSB
+
+putchr: pushw %dx # Save
+ xorw %dx,%dx # Use COM1
+ xorw %cx,%cx # No timeout
+ movb $0x01,%ah # BIOS: Send
+ int $0x14 # Character
+ popw %dx # Restore
+ retw # To caller
+
+# One-sector disk I/O routine
+
+intx13: movb 0x1(%si),%dh # Load head
+ movw 0x2(%si),%cx # Load cylinder:sector
+ movb $0x1,%al # Sector count
+ pushw %si # Save
+ movw %sp,%di # Save
+ testb $0x80,_FLAGS(%bp) # Use packet interface?
+ jz intx13.1 # No
+ pushl $0x0 # Set the
+ pushl 0x8(%si) # LBA address
+ pushw %es # Set the transfer
+ pushw %bx # buffer address
+ push $0x1 # Block count
+ push $0x10 # Packet size
+ movw %sp,%si # Packet pointer
+ decw %ax # Verify off
+ orb $0x40,%ah # Use disk packet
+intx13.1: int $0x13 # BIOS: Disk I/O
+ movw %di,%sp # Restore
+ popw %si # Restore
+ retw # To caller
+
+# Menu strings
+
+item: .ascii " "; .byte ' '|0x80
+prompt: .ascii "\nDef:"; .byte ' '|0x80
+crlf: .ascii "\r"; .byte '\n'|0x80
+
+# Partition type tables
+
+tables:
+#
+# These entries identify invalid or NON BOOT types and partitions.
+#
+ .byte 0x0, 0x5, 0xf
+#
+# These values indicate bootable types we know the names of
+#
+ .byte 0x1, 0x4, 0x6, 0xb, 0xc, 0xe, 0x83
+ .byte 0x9f, 0xa5, 0xa6, 0xa9
+#
+# These are offsets that match the known names above and point to the strings
+# that will be printed.
+#
+ .byte os_misc-. # Unknown
+ .byte os_dos-. # DOS
+ .byte os_dos-. # DOS
+ .byte os_dos-. # DOS
+ .byte os_dos-. # Windows
+ .byte os_dos-. # Windows
+ .byte os_dos-. # Windows
+ .byte os_linux-. # Linux
+ .byte os_bsd-. # BSD/OS
+ .byte os_freebsd-. # FreeBSD
+ .byte os_bsd-. # OpenBSD
+ .byte os_bsd-. # NetBSD
+#
+# And here are the strings themselves. 0x80 or'd into a byte indicates
+# the end of the string. (not so great for Russians but...)
+#
+os_misc: .ascii "?"; .byte '?'|0x80
+os_dos: .ascii "DO"; .byte 'S'|0x80
+os_linux: .ascii "Linu"; .byte 'x'|0x80
+os_freebsd: .ascii "Free"
+os_bsd: .ascii "BS"; .byte 'D'|0x80
+
+ .org PRT_OFF-0xe,0x90
+
+ .word B0MAGIC # Magic number
+
+#
+# These values are sometimes changed before writing back to the drive
+# Be especially careful that nxtdrv: must come after drive:, as it
+# is part of the same string.
+#
+drive: .ascii "Drive "
+nxtdrv: .byte 0x0 # Next drive number
+opt: .byte 0x0 # Option
+setdrv: .byte 0x80 # Drive to force
+flags: .byte FLAGS # Flags
+ticks: .word TICKS # Delay
+
+#
+# here is the 64 byte partition table that fdisk would fiddle with.
+#
+partbl: .fill 0x40,0x1,0x0 # Partition table
+ .word MAGIC # Magic number
diff --git a/sys/boot/i386/boot0sio/Makefile b/sys/boot/i386/boot0sio/Makefile
new file mode 100644
index 0000000..27b21aa
--- /dev/null
+++ b/sys/boot/i386/boot0sio/Makefile
@@ -0,0 +1,7 @@
+# $FreeBSD$
+
+.PATH: ${.CURDIR}/../boot0
+
+PROG= boot0sio
+
+.include "${.CURDIR}/../boot0/Makefile"
diff --git a/sys/boot/i386/boot2/Makefile b/sys/boot/i386/boot2/Makefile
new file mode 100644
index 0000000..aa8c0cd
--- /dev/null
+++ b/sys/boot/i386/boot2/Makefile
@@ -0,0 +1,123 @@
+# $FreeBSD$
+
+PROG= boot2
+NOMAN=
+STRIP=
+BINDIR?= /boot
+BINMODE= 444
+CLEANFILES= boot boot1 boot1.out boot1.o \
+ boot2.ldr boot2.bin boot2.ld boot2.out boot2.o boot2.h \
+ boot2.s sio.o
+
+NM?= nm
+
+# A value of 0x80 enables LBA support.
+B1FLAGS= 0x80
+
+BOOT_COMCONSOLE_PORT?= 0x3f8
+BOOT_COMCONSOLE_SPEED?= 9600
+B2SIOFMT?= 0x3
+
+.if exists(${.OBJDIR}/../btx)
+BTX= ${.OBJDIR}/../btx
+.else
+BTX= ${.CURDIR}/../btx
+.endif
+
+REL1= 0x700
+ORG1= 0x7c00
+ORG2= 0x2000
+
+# Decide Level of UFS support. UFS1_AND_UFS2 doesn't fit.
+
+# BOOT2_UFS?= UFS2_ONLY
+BOOT2_UFS?= UFS1_AND_UFS2
+# BOOT2_UFS?= UFS1_ONLY
+
+CFLAGS= -elf -ffreestanding -Os -fno-builtin \
+ -fno-guess-branch-probability -fomit-frame-pointer\
+ -mno-align-long-strings \
+ -mrtd \
+ -D${BOOT2_UFS} \
+ -I${.CURDIR}/../../common \
+ -I${.CURDIR}/../btx/lib -I. \
+ -Wall -Waggregate-return -Wbad-function-cast -Wcast-align \
+ -Wmissing-declarations -Wmissing-prototypes -Wnested-externs \
+ -Wpointer-arith -Wshadow -Wstrict-prototypes -Wwrite-strings
+
+LDFLAGS=-nostdlib -static -N --gc-sections
+
+all: boot1 boot2 boot
+
+boot: boot1 boot2
+ cat boot1 boot2 > boot
+
+boot1: boot1.out
+ objcopy -S -O binary boot1.out ${.TARGET}
+
+boot1.out: boot1.o
+ ${LD} ${LDFLAGS} -e start -Ttext ${ORG1} -o ${.TARGET} boot1.o
+
+boot1.o: boot1.s
+ ${CPP} ${CFLAGS} ${.IMPSRC} | \
+ ${AS} ${AFLAGS} --defsym FLAGS=${B1FLAGS} -o ${.TARGET}
+
+boot2.o: boot2.c ${.CURDIR}/../../common/ufsread.c
+ ${CC} ${CFLAGS} -S -o boot2.s.tmp ${.IMPSRC}
+ sed -e '/align/d' -e '/nop/d' < boot2.s.tmp > boot2.s
+ rm -f boot2.s.tmp
+ ${AS} ${AFLAGS} -o boot2.o boot2.s
+
+boot2.h: boot1.out
+ ${NM} -t d ${.ALLSRC} | awk '/([0-9])+ T xread/ \
+ { x = $$1 - ORG1; \
+ printf("#define XREADORG %#x\n", REL1 + x) }' \
+ ORG1=`printf "%d" ${ORG1}` \
+ REL1=`printf "%d" ${REL1}` > boot2.h
+
+boot2: boot2.ldr boot2.bin ${BTX}/btx/btx
+ btxld -v -E ${ORG2} -f bin -b ${BTX}/btx/btx -l boot2.ldr \
+ -o boot2.ld -P 1 boot2.bin
+ @ls -l boot2.ld | awk '{ x = 7680 - $$5; \
+ print x " bytes available"; if (x < 0) exit 1 }'
+ dd if=boot2.ld of=${.TARGET} obs=7680 conv=osync 2>/dev/null
+
+boot2.ldr:
+ dd if=/dev/zero of=${.TARGET} bs=276 count=1 2>/dev/null
+
+boot2.bin: boot2.out
+ objcopy -S -O binary boot2.out ${.TARGET}
+
+boot2.out: boot2.o sio.o
+ ${LD} ${LDFLAGS} -Ttext ${ORG2} -o ${.TARGET} \
+ ${BTX}/lib/crt0.o ${.ALLSRC}
+
+boot2.o: boot2.h
+
+sio.o: sio.s
+ ${AS} ${AFLAGS} --defsym SIOPRT=${BOOT_COMCONSOLE_PORT} \
+ --defsym SIOFMT=${B2SIOFMT} \
+ --defsym SIOSPD=${BOOT_COMCONSOLE_SPEED} \
+ ${.IMPSRC} -o ${.TARGET}
+
+install:
+ ${INSTALL} -o ${BINOWN} -g ${BINGRP} -m ${BINMODE} \
+ boot ${DESTDIR}${BINDIR}/boot
+ ${INSTALL} -o ${BINOWN} -g ${BINGRP} -m ${BINMODE} \
+ boot1 ${DESTDIR}${BINDIR}/boot1
+ ${INSTALL} -o ${BINOWN} -g ${BINGRP} -m ${BINMODE} \
+ boot2 ${DESTDIR}${BINDIR}/boot2
+
+.include <bsd.prog.mk>
+
+.if defined(REALLY_AMD64)
+boot2.o: machine
+
+beforedepend ${OBJS}: machine
+
+machine:
+ ln -sf ${.CURDIR}/../../../i386/include machine
+
+CLEANFILES+= machine
+.endif
+
diff --git a/sys/boot/i386/boot2/boot1.S b/sys/boot/i386/boot2/boot1.S
new file mode 100644
index 0000000..f7d9df6
--- /dev/null
+++ b/sys/boot/i386/boot2/boot1.S
@@ -0,0 +1,365 @@
+//
+// Copyright (c) 1998 Robert Nordier
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms are freely
+// permitted provided that the above copyright notice and this
+// paragraph and the following disclaimer are duplicated in all
+// such forms.
+//
+// This software is provided "AS IS" and without any express or
+// implied warranties, including, without limitation, the implied
+// warranties of merchantability and fitness for a particular
+// purpose.
+//
+
+// $FreeBSD$
+
+// Memory Locations
+ .set MEM_REL,0x700 // Relocation address
+ .set MEM_ARG,0x900 // Arguments
+ .set MEM_ORG,0x7c00 // Origin
+ .set MEM_BUF,0x8cec // Load area
+ .set MEM_BTX,0x9000 // BTX start
+ .set MEM_JMP,0x9010 // BTX entry point
+ .set MEM_USR,0xa000 // Client start
+ .set BDA_BOOT,0x472 // Boot howto flag
+
+// Partition Constants
+ .set PRT_OFF,0x1be // Partition offset
+ .set PRT_NUM,0x4 // Partitions
+ .set PRT_BSD,0xa5 // Partition type
+
+// Flag Bits
+ .set FL_PACKET,0x80 // Packet mode
+
+// Misc. Constants
+ .set SIZ_PAG,0x1000 // Page size
+ .set SIZ_SEC,0x200 // Sector size
+
+ .set NSECT,0x10
+ .globl start
+ .globl xread
+ .code16
+
+start: jmp main // Start recognizably
+
+// This is the start of a standard BIOS Parameter Block (BPB). Most bootable
+// FAT disks have this at the start of their MBR. While normal BIOS's will
+// work fine without this section, IBM's El Torito emulation "fixes" up the
+// BPB by writing into the memory copy of the MBR. Rather than have data
+// written into our xread routine, we'll define a BPB to work around it.
+// The data marked with (T) indicates a field required for a ThinkPad to
+// recognize the disk and (W) indicates fields written from IBM BIOS code.
+// The use of the BPB is based on what OpenBSD and NetBSD implemented in
+// their boot code but the required fields were determined by trial and error.
+//
+// Note: If additional space is needed in boot1, one solution would be to
+// move the "prompt" message data (below) to replace the OEM ID.
+
+ .org 0x03, 0x00
+oemid: .space 0x08, 0x00 // OEM ID
+
+ .org 0x0b, 0x00
+bpb: .word 512 // sector size (T)
+ .byte 0 // sectors/clustor
+ .word 0 // reserved sectors
+ .byte 0 // number of FATs
+ .word 0 // root entries
+ .word 0 // small sectors
+ .byte 0 // media type (W)
+ .word 0 // sectors/fat
+ .word 18 // sectors per track (T)
+ .word 2 // number of heads (T)
+ .long 0 // hidden sectors (W)
+ .long 0 // large sectors
+
+ .org 0x24, 0x00
+ebpb: .byte 0 // BIOS physical drive number (W)
+
+ .org 0x25,0x90
+//
+// Trampoline used by boot2 to call read to read data from the disk via
+// the BIOS. Call with:
+//
+// %cx:%ax - long - LBA to read in
+// %es:(%bx) - caddr_t - buffer to read data into
+// %dl - byte - drive to read from
+// %dh - byte - num sectors to read
+//
+
+xread: push %ss // Address
+ pop %ds // data
+//
+// Setup an EDD disk packet and pass it to read
+//
+xread.1: // Starting
+ pushl $0x0 // absolute
+ push %cx // block
+ push %ax // number
+ push %es // Address of
+ push %bx // transfer buffer
+ xor %ax,%ax // Number of
+ movb %dh,%al // blocks to
+ push %ax // transfer
+ push $0x10 // Size of packet
+ mov %sp,%bp // Packet pointer
+ callw read // Read from disk
+ lea 0x10(%bp),%sp // Clear stack
+ lret // To far caller
+//
+// Load the rest of boot2 and BTX up, copy the parts to the right locations,
+// and start it all up.
+//
+
+//
+// Setup the segment registers to flat addressing (segment 0) and setup the
+// stack to end just below the start of our code.
+//
+main: cld // String ops inc
+ xor %cx,%cx // Zero
+ mov %cx,%es // Address
+ mov %cx,%ds // data
+ mov %cx,%ss // Set up
+ mov $start,%sp // stack
+//
+// Relocate ourself to MEM_REL. Since %cx == 0, the inc %ch sets
+// %cx == 0x100.
+//
+ mov %sp,%si // Source
+ mov $MEM_REL,%di // Destination
+ incb %ch // Word count
+ rep // Copy
+ movsw // code
+//
+// If we are on a hard drive, then load the MBR and look for the first
+// FreeBSD slice. We use the fake partition entry below that points to
+// the MBR when we call nread. The first pass looks for the first active
+// FreeBSD slice. The second pass looks for the first non-active FreeBSD
+// slice if the first one fails.
+//
+ mov $part4,%si // Partition
+ cmpb $0x80,%dl // Hard drive?
+ jb main.4 // No
+ movb $0x1,%dh // Block count
+ callw nread // Read MBR
+ mov $0x1,%cx // Two passes
+main.1: mov $MEM_BUF+PRT_OFF,%si // Partition table
+ movb $0x1,%dh // Partition
+main.2: cmpb $PRT_BSD,0x4(%si) // Our partition type?
+ jne main.3 // No
+ jcxz main.5 // If second pass
+ testb $0x80,(%si) // Active?
+ jnz main.5 // Yes
+main.3: add $0x10,%si // Next entry
+ incb %dh // Partition
+ cmpb $0x1+PRT_NUM,%dh // In table?
+ jb main.2 // Yes
+ dec %cx // Do two
+ jcxz main.1 // passes
+//
+// If we get here, we didn't find any FreeBSD slices at all, so print an
+// error message and die.
+//
+ mov $msg_part,%si // Message
+ jmp error // Error
+//
+// Floppies use partition 0 of drive 0.
+//
+main.4: xor %dx,%dx // Partition:drive
+//
+// Ok, we have a slice and drive in %dx now, so use that to locate and load
+// boot2. %si references the start of the slice we are looking for, so go
+// ahead and load up the first 16 sectors (boot1 + boot2) from that. When
+// we read it in, we conveniently use 0x8cec as our transfer buffer. Thus,
+// boot1 ends up at 0x8cec, and boot2 starts at 0x8cec + 0x200 = 0x8eec.
+// The first part of boot2 is the disklabel, which is 0x114 bytes long.
+// The second part is BTX, which is thus loaded into 0x9000, which is where
+// it also runs from. The boot2.bin binary starts right after the end of
+// BTX, so we have to figure out where the start of it is and then move the
+// binary to 0xc000. Normally, BTX clients start at MEM_USR, or 0xa000, but
+// when we use btxld to create boot2, we use an entry point of 0x2000. That
+// entry point is relative to MEM_USR; thus boot2.bin starts at 0xc000.
+//
+main.5: mov %dx,MEM_ARG // Save args
+ movb $NSECT,%dh // Sector count
+ callw nread // Read disk
+ mov $MEM_BTX,%bx // BTX
+ mov 0xa(%bx),%si // Get BTX length and set
+ add %bx,%si // %si to start of boot2.bin
+ mov $MEM_USR+SIZ_PAG*2,%di // Client page 2
+ mov $MEM_BTX+(NSECT-1)*SIZ_SEC,%cx // Byte
+ sub %si,%cx // count
+ rep // Relocate
+ movsb // client
+ sub %di,%cx // Byte count
+ xorb %al,%al // Zero assumed bss from
+ rep // the end of boot2.bin
+ stosb // up to 0x10000
+ callw seta20 // Enable A20
+ jmp start+MEM_JMP-MEM_ORG // Start BTX
+//
+// Enable A20 so we can access memory above 1 meg.
+//
+seta20: cli // Disable interrupts
+seta20.1: inb $0x64,%al // Get status
+ testb $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?
+ jnz seta20.2 // Yes
+ movb $0xdf,%al // Enable
+ outb %al,$0x60 // A20
+ sti // Enable interrupts
+ retw // To caller
+//
+// Trampoline used to call read from within boot1.
+//
+nread: mov $MEM_BUF,%bx // Transfer buffer
+ mov 0x8(%si),%ax // Get
+ mov 0xa(%si),%cx // LBA
+ push %cs // Read from
+ callw xread.1 // disk
+ jnc return // If success, return
+ mov $msg_read,%si // Otherwise, set the error
+ // message and fall through to
+ // the error routine
+//
+// Print out the error message pointed to by %ds:(%si) followed
+// by a prompt, wait for a keypress, and then reboot the machine.
+//
+error: callw putstr // Display message
+ mov $prompt,%si // Display
+ callw putstr // prompt
+ xorb %ah,%ah // BIOS: Get
+ int $0x16 // keypress
+ movw $0x1234, BDA_BOOT // Do a warm boot
+ ljmp $0xffff,$0x0 // reboot the machine
+//
+// Display a null-terminated string using the BIOS output.
+//
+putstr.0: mov $0x7,%bx // Page:attribute
+ movb $0xe,%ah // BIOS: Display
+ int $0x10 // character
+putstr: lodsb // Get char
+ testb %al,%al // End of string?
+ jne putstr.0 // No
+
+//
+// Overused return code. ereturn is used to return an error from the
+// read function. Since we assume putstr succeeds, we (ab)use the
+// same code when we return from putstr.
+//
+ereturn: movb $0x1,%ah // Invalid
+ stc // argument
+return: retw // To caller
+//
+// Reads sectors from the disk. If EDD is enabled, then check if it is
+// installed and use it if it is. If it is not installed or not enabled, then
+// fall back to using CHS. Since we use a LBA, if we are using CHS, we have to
+// fetch the drive parameters from the BIOS and divide it out ourselves.
+// Call with:
+//
+// %dl - byte - drive number
+// stack - 10 bytes - EDD Packet
+//
+read: push %dx // Save
+ movb $0x8,%ah // BIOS: Get drive
+ int $0x13 // parameters
+ movb %dh,%ch // Max head number
+ pop %dx // Restore
+ jc return // If error
+ andb $0x3f,%cl // Sectors per track
+ jz ereturn // If zero
+ cli // Disable interrupts
+ mov 0x8(%bp),%eax // Get LBA
+ push %dx // Save
+ movzbl %cl,%ebx // Divide by
+ xor %edx,%edx // sectors
+ div %ebx // per track
+ movb %ch,%bl // Max head number
+ movb %dl,%ch // Sector number
+ inc %bx // Divide by
+ xorb %dl,%dl // number
+ div %ebx // of heads
+ movb %dl,%bh // Head number
+ pop %dx // Restore
+ cmpl $0x3ff,%eax // Cylinder number supportable?
+ sti // Enable interrupts
+ ja read.7 // No, try EDD
+ xchgb %al,%ah // Set up cylinder
+ rorb $0x2,%al // number
+ orb %ch,%al // Merge
+ inc %ax // sector
+ xchg %ax,%cx // number
+ movb %bh,%dh // Head number
+ subb %ah,%al // Sectors this track
+ mov 0x2(%bp),%ah // Blocks to read
+ cmpb %ah,%al // To read
+ jb read.2 // this
+#ifdef TRACK_AT_A_TIME
+ movb %ah,%al // track
+#else
+ movb $1,%al // one sector
+#endif
+read.2: mov $0x5,%di // Try count
+read.3: les 0x4(%bp),%bx // Transfer buffer
+ push %ax // Save
+ movb $0x2,%ah // BIOS: Read
+ int $0x13 // from disk
+ pop %bx // Restore
+ jnc read.4 // If success
+ dec %di // Retry?
+ jz read.6 // No
+ xorb %ah,%ah // BIOS: Reset
+ int $0x13 // disk system
+ xchg %bx,%ax // Block count
+ jmp read.3 // Continue
+read.4: movzbw %bl,%ax // Sectors read
+ add %ax,0x8(%bp) // Adjust
+ jnc read.5 // LBA,
+ incw 0xa(%bp) // transfer
+read.5: shlb %bl // buffer
+ add %bl,0x5(%bp) // pointer,
+ sub %al,0x2(%bp) // block count
+ ja read // If not done
+read.6: retw // To caller
+read.7: testb $FL_PACKET,%cs:MEM_REL+flags-start // LBA support enabled?
+ jz ereturn // No, so return an error
+ mov $0x55aa,%bx // Magic
+ push %dx // Save
+ movb $0x41,%ah // BIOS: Check
+ int $0x13 // extensions present
+ pop %dx // Restore
+ jc return // If error, return an error
+ cmp $0xaa55,%bx // Magic?
+ jne ereturn // No, so return an error
+ testb $0x1,%cl // Packet interface?
+ jz ereturn // No, so return an error
+ mov %bp,%si // Disk packet
+ movb $0x42,%ah // BIOS: Extended
+ int $0x13 // read
+ retw // To caller
+
+// Messages
+
+msg_read: .asciz "Read"
+msg_part: .asciz "Boot"
+
+prompt: .asciz " error\r\n"
+
+flags: .byte FLAGS // Flags
+
+ .org PRT_OFF,0x90
+
+// Partition table
+
+ .fill 0x30,0x1,0x0
+part4: .byte 0x80, 0x00, 0x01, 0x00
+ .byte 0xa5, 0xfe, 0xff, 0xff
+ .byte 0x00, 0x00, 0x00, 0x00
+ .byte 0x50, 0xc3, 0x00, 0x00 // 50000 sectors long, bleh
+
+ .word 0xaa55 // Magic number
diff --git a/sys/boot/i386/boot2/boot1.s b/sys/boot/i386/boot2/boot1.s
new file mode 100644
index 0000000..f7d9df6
--- /dev/null
+++ b/sys/boot/i386/boot2/boot1.s
@@ -0,0 +1,365 @@
+//
+// Copyright (c) 1998 Robert Nordier
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms are freely
+// permitted provided that the above copyright notice and this
+// paragraph and the following disclaimer are duplicated in all
+// such forms.
+//
+// This software is provided "AS IS" and without any express or
+// implied warranties, including, without limitation, the implied
+// warranties of merchantability and fitness for a particular
+// purpose.
+//
+
+// $FreeBSD$
+
+// Memory Locations
+ .set MEM_REL,0x700 // Relocation address
+ .set MEM_ARG,0x900 // Arguments
+ .set MEM_ORG,0x7c00 // Origin
+ .set MEM_BUF,0x8cec // Load area
+ .set MEM_BTX,0x9000 // BTX start
+ .set MEM_JMP,0x9010 // BTX entry point
+ .set MEM_USR,0xa000 // Client start
+ .set BDA_BOOT,0x472 // Boot howto flag
+
+// Partition Constants
+ .set PRT_OFF,0x1be // Partition offset
+ .set PRT_NUM,0x4 // Partitions
+ .set PRT_BSD,0xa5 // Partition type
+
+// Flag Bits
+ .set FL_PACKET,0x80 // Packet mode
+
+// Misc. Constants
+ .set SIZ_PAG,0x1000 // Page size
+ .set SIZ_SEC,0x200 // Sector size
+
+ .set NSECT,0x10
+ .globl start
+ .globl xread
+ .code16
+
+start: jmp main // Start recognizably
+
+// This is the start of a standard BIOS Parameter Block (BPB). Most bootable
+// FAT disks have this at the start of their MBR. While normal BIOS's will
+// work fine without this section, IBM's El Torito emulation "fixes" up the
+// BPB by writing into the memory copy of the MBR. Rather than have data
+// written into our xread routine, we'll define a BPB to work around it.
+// The data marked with (T) indicates a field required for a ThinkPad to
+// recognize the disk and (W) indicates fields written from IBM BIOS code.
+// The use of the BPB is based on what OpenBSD and NetBSD implemented in
+// their boot code but the required fields were determined by trial and error.
+//
+// Note: If additional space is needed in boot1, one solution would be to
+// move the "prompt" message data (below) to replace the OEM ID.
+
+ .org 0x03, 0x00
+oemid: .space 0x08, 0x00 // OEM ID
+
+ .org 0x0b, 0x00
+bpb: .word 512 // sector size (T)
+ .byte 0 // sectors/clustor
+ .word 0 // reserved sectors
+ .byte 0 // number of FATs
+ .word 0 // root entries
+ .word 0 // small sectors
+ .byte 0 // media type (W)
+ .word 0 // sectors/fat
+ .word 18 // sectors per track (T)
+ .word 2 // number of heads (T)
+ .long 0 // hidden sectors (W)
+ .long 0 // large sectors
+
+ .org 0x24, 0x00
+ebpb: .byte 0 // BIOS physical drive number (W)
+
+ .org 0x25,0x90
+//
+// Trampoline used by boot2 to call read to read data from the disk via
+// the BIOS. Call with:
+//
+// %cx:%ax - long - LBA to read in
+// %es:(%bx) - caddr_t - buffer to read data into
+// %dl - byte - drive to read from
+// %dh - byte - num sectors to read
+//
+
+xread: push %ss // Address
+ pop %ds // data
+//
+// Setup an EDD disk packet and pass it to read
+//
+xread.1: // Starting
+ pushl $0x0 // absolute
+ push %cx // block
+ push %ax // number
+ push %es // Address of
+ push %bx // transfer buffer
+ xor %ax,%ax // Number of
+ movb %dh,%al // blocks to
+ push %ax // transfer
+ push $0x10 // Size of packet
+ mov %sp,%bp // Packet pointer
+ callw read // Read from disk
+ lea 0x10(%bp),%sp // Clear stack
+ lret // To far caller
+//
+// Load the rest of boot2 and BTX up, copy the parts to the right locations,
+// and start it all up.
+//
+
+//
+// Setup the segment registers to flat addressing (segment 0) and setup the
+// stack to end just below the start of our code.
+//
+main: cld // String ops inc
+ xor %cx,%cx // Zero
+ mov %cx,%es // Address
+ mov %cx,%ds // data
+ mov %cx,%ss // Set up
+ mov $start,%sp // stack
+//
+// Relocate ourself to MEM_REL. Since %cx == 0, the inc %ch sets
+// %cx == 0x100.
+//
+ mov %sp,%si // Source
+ mov $MEM_REL,%di // Destination
+ incb %ch // Word count
+ rep // Copy
+ movsw // code
+//
+// If we are on a hard drive, then load the MBR and look for the first
+// FreeBSD slice. We use the fake partition entry below that points to
+// the MBR when we call nread. The first pass looks for the first active
+// FreeBSD slice. The second pass looks for the first non-active FreeBSD
+// slice if the first one fails.
+//
+ mov $part4,%si // Partition
+ cmpb $0x80,%dl // Hard drive?
+ jb main.4 // No
+ movb $0x1,%dh // Block count
+ callw nread // Read MBR
+ mov $0x1,%cx // Two passes
+main.1: mov $MEM_BUF+PRT_OFF,%si // Partition table
+ movb $0x1,%dh // Partition
+main.2: cmpb $PRT_BSD,0x4(%si) // Our partition type?
+ jne main.3 // No
+ jcxz main.5 // If second pass
+ testb $0x80,(%si) // Active?
+ jnz main.5 // Yes
+main.3: add $0x10,%si // Next entry
+ incb %dh // Partition
+ cmpb $0x1+PRT_NUM,%dh // In table?
+ jb main.2 // Yes
+ dec %cx // Do two
+ jcxz main.1 // passes
+//
+// If we get here, we didn't find any FreeBSD slices at all, so print an
+// error message and die.
+//
+ mov $msg_part,%si // Message
+ jmp error // Error
+//
+// Floppies use partition 0 of drive 0.
+//
+main.4: xor %dx,%dx // Partition:drive
+//
+// Ok, we have a slice and drive in %dx now, so use that to locate and load
+// boot2. %si references the start of the slice we are looking for, so go
+// ahead and load up the first 16 sectors (boot1 + boot2) from that. When
+// we read it in, we conveniently use 0x8cec as our transfer buffer. Thus,
+// boot1 ends up at 0x8cec, and boot2 starts at 0x8cec + 0x200 = 0x8eec.
+// The first part of boot2 is the disklabel, which is 0x114 bytes long.
+// The second part is BTX, which is thus loaded into 0x9000, which is where
+// it also runs from. The boot2.bin binary starts right after the end of
+// BTX, so we have to figure out where the start of it is and then move the
+// binary to 0xc000. Normally, BTX clients start at MEM_USR, or 0xa000, but
+// when we use btxld to create boot2, we use an entry point of 0x2000. That
+// entry point is relative to MEM_USR; thus boot2.bin starts at 0xc000.
+//
+main.5: mov %dx,MEM_ARG // Save args
+ movb $NSECT,%dh // Sector count
+ callw nread // Read disk
+ mov $MEM_BTX,%bx // BTX
+ mov 0xa(%bx),%si // Get BTX length and set
+ add %bx,%si // %si to start of boot2.bin
+ mov $MEM_USR+SIZ_PAG*2,%di // Client page 2
+ mov $MEM_BTX+(NSECT-1)*SIZ_SEC,%cx // Byte
+ sub %si,%cx // count
+ rep // Relocate
+ movsb // client
+ sub %di,%cx // Byte count
+ xorb %al,%al // Zero assumed bss from
+ rep // the end of boot2.bin
+ stosb // up to 0x10000
+ callw seta20 // Enable A20
+ jmp start+MEM_JMP-MEM_ORG // Start BTX
+//
+// Enable A20 so we can access memory above 1 meg.
+//
+seta20: cli // Disable interrupts
+seta20.1: inb $0x64,%al // Get status
+ testb $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?
+ jnz seta20.2 // Yes
+ movb $0xdf,%al // Enable
+ outb %al,$0x60 // A20
+ sti // Enable interrupts
+ retw // To caller
+//
+// Trampoline used to call read from within boot1.
+//
+nread: mov $MEM_BUF,%bx // Transfer buffer
+ mov 0x8(%si),%ax // Get
+ mov 0xa(%si),%cx // LBA
+ push %cs // Read from
+ callw xread.1 // disk
+ jnc return // If success, return
+ mov $msg_read,%si // Otherwise, set the error
+ // message and fall through to
+ // the error routine
+//
+// Print out the error message pointed to by %ds:(%si) followed
+// by a prompt, wait for a keypress, and then reboot the machine.
+//
+error: callw putstr // Display message
+ mov $prompt,%si // Display
+ callw putstr // prompt
+ xorb %ah,%ah // BIOS: Get
+ int $0x16 // keypress
+ movw $0x1234, BDA_BOOT // Do a warm boot
+ ljmp $0xffff,$0x0 // reboot the machine
+//
+// Display a null-terminated string using the BIOS output.
+//
+putstr.0: mov $0x7,%bx // Page:attribute
+ movb $0xe,%ah // BIOS: Display
+ int $0x10 // character
+putstr: lodsb // Get char
+ testb %al,%al // End of string?
+ jne putstr.0 // No
+
+//
+// Overused return code. ereturn is used to return an error from the
+// read function. Since we assume putstr succeeds, we (ab)use the
+// same code when we return from putstr.
+//
+ereturn: movb $0x1,%ah // Invalid
+ stc // argument
+return: retw // To caller
+//
+// Reads sectors from the disk. If EDD is enabled, then check if it is
+// installed and use it if it is. If it is not installed or not enabled, then
+// fall back to using CHS. Since we use a LBA, if we are using CHS, we have to
+// fetch the drive parameters from the BIOS and divide it out ourselves.
+// Call with:
+//
+// %dl - byte - drive number
+// stack - 10 bytes - EDD Packet
+//
+read: push %dx // Save
+ movb $0x8,%ah // BIOS: Get drive
+ int $0x13 // parameters
+ movb %dh,%ch // Max head number
+ pop %dx // Restore
+ jc return // If error
+ andb $0x3f,%cl // Sectors per track
+ jz ereturn // If zero
+ cli // Disable interrupts
+ mov 0x8(%bp),%eax // Get LBA
+ push %dx // Save
+ movzbl %cl,%ebx // Divide by
+ xor %edx,%edx // sectors
+ div %ebx // per track
+ movb %ch,%bl // Max head number
+ movb %dl,%ch // Sector number
+ inc %bx // Divide by
+ xorb %dl,%dl // number
+ div %ebx // of heads
+ movb %dl,%bh // Head number
+ pop %dx // Restore
+ cmpl $0x3ff,%eax // Cylinder number supportable?
+ sti // Enable interrupts
+ ja read.7 // No, try EDD
+ xchgb %al,%ah // Set up cylinder
+ rorb $0x2,%al // number
+ orb %ch,%al // Merge
+ inc %ax // sector
+ xchg %ax,%cx // number
+ movb %bh,%dh // Head number
+ subb %ah,%al // Sectors this track
+ mov 0x2(%bp),%ah // Blocks to read
+ cmpb %ah,%al // To read
+ jb read.2 // this
+#ifdef TRACK_AT_A_TIME
+ movb %ah,%al // track
+#else
+ movb $1,%al // one sector
+#endif
+read.2: mov $0x5,%di // Try count
+read.3: les 0x4(%bp),%bx // Transfer buffer
+ push %ax // Save
+ movb $0x2,%ah // BIOS: Read
+ int $0x13 // from disk
+ pop %bx // Restore
+ jnc read.4 // If success
+ dec %di // Retry?
+ jz read.6 // No
+ xorb %ah,%ah // BIOS: Reset
+ int $0x13 // disk system
+ xchg %bx,%ax // Block count
+ jmp read.3 // Continue
+read.4: movzbw %bl,%ax // Sectors read
+ add %ax,0x8(%bp) // Adjust
+ jnc read.5 // LBA,
+ incw 0xa(%bp) // transfer
+read.5: shlb %bl // buffer
+ add %bl,0x5(%bp) // pointer,
+ sub %al,0x2(%bp) // block count
+ ja read // If not done
+read.6: retw // To caller
+read.7: testb $FL_PACKET,%cs:MEM_REL+flags-start // LBA support enabled?
+ jz ereturn // No, so return an error
+ mov $0x55aa,%bx // Magic
+ push %dx // Save
+ movb $0x41,%ah // BIOS: Check
+ int $0x13 // extensions present
+ pop %dx // Restore
+ jc return // If error, return an error
+ cmp $0xaa55,%bx // Magic?
+ jne ereturn // No, so return an error
+ testb $0x1,%cl // Packet interface?
+ jz ereturn // No, so return an error
+ mov %bp,%si // Disk packet
+ movb $0x42,%ah // BIOS: Extended
+ int $0x13 // read
+ retw // To caller
+
+// Messages
+
+msg_read: .asciz "Read"
+msg_part: .asciz "Boot"
+
+prompt: .asciz " error\r\n"
+
+flags: .byte FLAGS // Flags
+
+ .org PRT_OFF,0x90
+
+// Partition table
+
+ .fill 0x30,0x1,0x0
+part4: .byte 0x80, 0x00, 0x01, 0x00
+ .byte 0xa5, 0xfe, 0xff, 0xff
+ .byte 0x00, 0x00, 0x00, 0x00
+ .byte 0x50, 0xc3, 0x00, 0x00 // 50000 sectors long, bleh
+
+ .word 0xaa55 // Magic number
diff --git a/sys/boot/i386/boot2/boot2.c b/sys/boot/i386/boot2/boot2.c
new file mode 100644
index 0000000..bb58353
--- /dev/null
+++ b/sys/boot/i386/boot2/boot2.c
@@ -0,0 +1,645 @@
+/*-
+ * Copyright (c) 1998 Robert Nordier
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms are freely
+ * permitted provided that the above copyright notice and this
+ * paragraph and the following disclaimer are duplicated in all
+ * such forms.
+ *
+ * This software is provided "AS IS" and without any express or
+ * implied warranties, including, without limitation, the implied
+ * warranties of merchantability and fitness for a particular
+ * purpose.
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/param.h>
+#include <sys/disklabel.h>
+#include <sys/diskmbr.h>
+#include <sys/dirent.h>
+#include <sys/reboot.h>
+
+#include <machine/bootinfo.h>
+#include <machine/elf.h>
+
+#include <stdarg.h>
+
+#include <a.out.h>
+
+#include <btxv86.h>
+
+#include "boot2.h"
+#include "lib.h"
+
+#define IO_KEYBOARD 1
+#define IO_SERIAL 2
+
+#define SECOND 18 /* Circa that many ticks in a second. */
+
+#define RBX_ASKNAME 0x0 /* -a */
+#define RBX_SINGLE 0x1 /* -s */
+/* 0x2 is reserved for log2(RB_NOSYNC). */
+/* 0x3 is reserved for log2(RB_HALT). */
+/* 0x4 is reserved for log2(RB_INITNAME). */
+#define RBX_DFLTROOT 0x5 /* -r */
+#define RBX_KDB 0x6 /* -d */
+/* 0x7 is reserved for log2(RB_RDONLY). */
+/* 0x8 is reserved for log2(RB_DUMP). */
+/* 0x9 is reserved for log2(RB_MINIROOT). */
+#define RBX_CONFIG 0xa /* -c */
+#define RBX_VERBOSE 0xb /* -v */
+#define RBX_SERIAL 0xc /* -h */
+#define RBX_CDROM 0xd /* -C */
+/* 0xe is reserved for log2(RB_POWEROFF). */
+#define RBX_GDB 0xf /* -g */
+#define RBX_MUTE 0x10 /* -m */
+/* 0x11 is reserved for log2(RB_SELFTEST). */
+/* 0x12 is reserved for boot programs. */
+/* 0x13 is reserved for boot programs. */
+#define RBX_PAUSE 0x14 /* -p */
+#define RBX_NOINTR 0x1c /* -n */
+/* 0x1d is reserved for log2(RB_MULTIPLE) and is just misnamed here. */
+#define RBX_DUAL 0x1d /* -D */
+#define RBX_PROBEKBD 0x1e /* -P */
+/* 0x1f is reserved for log2(RB_BOOTINFO). */
+
+/* pass: -a, -s, -r, -d, -c, -v, -h, -C, -g, -m, -p, -D */
+#define RBX_MASK 0x2005ffff
+
+#define PATH_CONFIG "/boot.config"
+#define PATH_BOOT3 "/boot/loader"
+#define PATH_KERNEL "/kernel"
+
+#define ARGS 0x900
+#define NOPT 12
+#define NDEV 3
+#define MEM_BASE 0x12
+#define MEM_EXT 0x15
+#define V86_CY(x) ((x) & 1)
+#define V86_ZR(x) ((x) & 0x40)
+
+#define DRV_HARD 0x80
+#define DRV_MASK 0x7f
+
+#define TYPE_AD 0
+#define TYPE_DA 1
+#define TYPE_MAXHARD TYPE_DA
+#define TYPE_FD 2
+
+extern uint32_t _end;
+
+static const char optstr[NOPT] = "DhaCgmnPprsv";
+static const unsigned char flags[NOPT] = {
+ RBX_DUAL,
+ RBX_SERIAL,
+ RBX_ASKNAME,
+ RBX_CDROM,
+ RBX_GDB,
+ RBX_MUTE,
+ RBX_NOINTR,
+ RBX_PROBEKBD,
+ RBX_PAUSE,
+ RBX_DFLTROOT,
+ RBX_SINGLE,
+ RBX_VERBOSE
+};
+
+static const char *const dev_nm[NDEV] = {"ad", "da", "fd"};
+static const unsigned char dev_maj[NDEV] = {30, 4, 2};
+
+static struct dsk {
+ unsigned drive;
+ unsigned type;
+ unsigned unit;
+ unsigned slice;
+ unsigned part;
+ unsigned start;
+ int init;
+} dsk;
+static char cmd[512];
+static char kname[1024];
+static uint32_t opts;
+static struct bootinfo bootinfo;
+static uint8_t ioctrl = IO_KEYBOARD;
+
+void exit(int);
+static void load(void);
+static int parse(void);
+static int xfsread(ino_t, void *, size_t);
+static int dskread(void *, unsigned, unsigned);
+static void printf(const char *,...);
+static void putchar(int);
+static uint32_t memsize(void);
+static int drvread(void *, unsigned, unsigned);
+static int keyhit(unsigned);
+static int xputc(int);
+static int xgetc(int);
+static int getc(int);
+
+#define memcpy __builtin_memcpy
+
+static inline int
+strcmp(const char *s1, const char *s2)
+{
+ for (; *s1 == *s2 && *s1; s1++, s2++);
+ return (unsigned char)*s1 - (unsigned char)*s2;
+}
+
+#include "ufsread.c"
+
+static int
+xfsread(ino_t inode, void *buf, size_t nbyte)
+{
+ if ((size_t)fsread(inode, buf, nbyte) != nbyte) {
+ printf("Invalid %s\n", "format");
+ return -1;
+ }
+ return 0;
+}
+
+static inline uint32_t
+memsize(void)
+{
+ v86.addr = MEM_EXT;
+ v86.eax = 0x8800;
+ v86int();
+ return v86.eax;
+}
+
+static inline void
+getstr(void)
+{
+ char *s;
+ int c;
+
+ s = cmd;
+ for (;;) {
+ switch (c = xgetc(0)) {
+ case 0:
+ break;
+ case '\177':
+ case '\b':
+ if (s > cmd) {
+ s--;
+ printf("\b \b");
+ }
+ break;
+ case '\n':
+ case '\r':
+ *s = 0;
+ return;
+ default:
+ if (s - cmd < sizeof(cmd) - 1)
+ *s++ = c;
+ putchar(c);
+ }
+ }
+}
+
+static inline void
+putc(int c)
+{
+ v86.addr = 0x10;
+ v86.eax = 0xe00 | (c & 0xff);
+ v86.ebx = 0x7;
+ v86int();
+}
+
+int
+main(void)
+{
+ int autoboot;
+ ino_t ino;
+
+ dmadat = (void *)(roundup2(__base + (int32_t)&_end, 0x10000) - __base);
+ v86.ctl = V86_FLAGS;
+ dsk.drive = *(uint8_t *)PTOV(ARGS);
+ dsk.type = dsk.drive & DRV_HARD ? TYPE_AD : TYPE_FD;
+ dsk.unit = dsk.drive & DRV_MASK;
+ dsk.slice = *(uint8_t *)PTOV(ARGS + 1) + 1;
+ bootinfo.bi_version = BOOTINFO_VERSION;
+ bootinfo.bi_size = sizeof(bootinfo);
+ bootinfo.bi_basemem = 0; /* XXX will be filled by loader or kernel */
+ bootinfo.bi_extmem = memsize();
+ bootinfo.bi_memsizes_valid++;
+
+ /* Process configuration file */
+
+ autoboot = 1;
+
+ if ((ino = lookup(PATH_CONFIG)))
+ fsread(ino, cmd, sizeof(cmd));
+
+ if (*cmd) {
+ printf("%s: %s", PATH_CONFIG, cmd);
+ if (parse())
+ autoboot = 0;
+ /* Do not process this command twice */
+ *cmd = 0;
+ }
+
+ /*
+ * Try to exec stage 3 boot loader. If interrupted by a keypress,
+ * or in case of failure, try to load a kernel directly instead.
+ */
+
+ if (autoboot && !*kname) {
+ memcpy(kname, PATH_BOOT3, sizeof(PATH_BOOT3));
+ if (!keyhit(3*SECOND)) {
+ load();
+ memcpy(kname, PATH_KERNEL, sizeof(PATH_KERNEL));
+ }
+ }
+
+ /* Present the user with the boot2 prompt. */
+
+ for (;;) {
+ printf("\nFreeBSD/i386 boot\n"
+ "Default: %u:%s(%u,%c)%s\n"
+ "boot: ",
+ dsk.drive & DRV_MASK, dev_nm[dsk.type], dsk.unit,
+ 'a' + dsk.part, kname);
+ if (ioctrl & IO_SERIAL)
+ sio_flush();
+ if (!autoboot || keyhit(5*SECOND))
+ getstr();
+ else
+ putchar('\n');
+ autoboot = 0;
+ if (parse())
+ putchar('\a');
+ else
+ load();
+ }
+}
+
+/* XXX - Needed for btxld to link the boot2 binary; do not remove. */
+void
+exit(int x)
+{
+}
+
+static void
+load(void)
+{
+ union {
+ struct exec ex;
+ Elf32_Ehdr eh;
+ } hdr;
+ Elf32_Phdr ep[2];
+ Elf32_Shdr es[2];
+ caddr_t p;
+ ino_t ino;
+ uint32_t addr, x;
+ int fmt, i, j;
+
+ if (!(ino = lookup(kname))) {
+ if (!ls)
+ printf("No %s\n", kname);
+ return;
+ }
+ if (xfsread(ino, &hdr, sizeof(hdr)))
+ return;
+ if (N_GETMAGIC(hdr.ex) == ZMAGIC)
+ fmt = 0;
+ else if (IS_ELF(hdr.eh))
+ fmt = 1;
+ else {
+ printf("Invalid %s\n", "format");
+ return;
+ }
+ if (fmt == 0) {
+ addr = hdr.ex.a_entry & 0xffffff;
+ p = PTOV(addr);
+ fs_off = PAGE_SIZE;
+ if (xfsread(ino, p, hdr.ex.a_text))
+ return;
+ p += roundup2(hdr.ex.a_text, PAGE_SIZE);
+ if (xfsread(ino, p, hdr.ex.a_data))
+ return;
+ p += hdr.ex.a_data + roundup2(hdr.ex.a_bss, PAGE_SIZE);
+ bootinfo.bi_symtab = VTOP(p);
+ memcpy(p, &hdr.ex.a_syms, sizeof(hdr.ex.a_syms));
+ p += sizeof(hdr.ex.a_syms);
+ if (hdr.ex.a_syms) {
+ if (xfsread(ino, p, hdr.ex.a_syms))
+ return;
+ p += hdr.ex.a_syms;
+ if (xfsread(ino, p, sizeof(int)))
+ return;
+ x = *(uint32_t *)p;
+ p += sizeof(int);
+ x -= sizeof(int);
+ if (xfsread(ino, p, x))
+ return;
+ p += x;
+ }
+ } else {
+ fs_off = hdr.eh.e_phoff;
+ for (j = i = 0; i < hdr.eh.e_phnum && j < 2; i++) {
+ if (xfsread(ino, ep + j, sizeof(ep[0])))
+ return;
+ if (ep[j].p_type == PT_LOAD)
+ j++;
+ }
+ for (i = 0; i < 2; i++) {
+ p = PTOV(ep[i].p_paddr & 0xffffff);
+ fs_off = ep[i].p_offset;
+ if (xfsread(ino, p, ep[i].p_filesz))
+ return;
+ }
+ p += roundup2(ep[1].p_memsz, PAGE_SIZE);
+ bootinfo.bi_symtab = VTOP(p);
+ if (hdr.eh.e_shnum == hdr.eh.e_shstrndx + 3) {
+ fs_off = hdr.eh.e_shoff + sizeof(es[0]) *
+ (hdr.eh.e_shstrndx + 1);
+ if (xfsread(ino, &es, sizeof(es)))
+ return;
+ for (i = 0; i < 2; i++) {
+ memcpy(p, &es[i].sh_size, sizeof(es[i].sh_size));
+ p += sizeof(es[i].sh_size);
+ fs_off = es[i].sh_offset;
+ if (xfsread(ino, p, es[i].sh_size))
+ return;
+ p += es[i].sh_size;
+ }
+ }
+ addr = hdr.eh.e_entry & 0xffffff;
+ }
+ bootinfo.bi_esymtab = VTOP(p);
+ bootinfo.bi_kernelname = VTOP(kname);
+ bootinfo.bi_bios_dev = dsk.drive;
+ __exec((caddr_t)addr, RB_BOOTINFO | (opts & RBX_MASK),
+ MAKEBOOTDEV(dev_maj[dsk.type], 0, dsk.slice, dsk.unit, dsk.part),
+ 0, 0, 0, VTOP(&bootinfo));
+}
+
+static int
+parse()
+{
+ char *arg = cmd;
+ char *p, *q;
+ unsigned int drv;
+ int c, i;
+
+ while ((c = *arg++)) {
+ if (c == ' ' || c == '\t' || c == '\n')
+ continue;
+ for (p = arg; *p && *p != '\n' && *p != ' ' && *p != '\t'; p++);
+ if (*p)
+ *p++ = 0;
+ if (c == '-') {
+ while ((c = *arg++)) {
+ for (i = 0; c != optstr[i]; i++)
+ if (i == NOPT - 1)
+ return -1;
+ opts ^= 1 << flags[i];
+ }
+ if (opts & 1 << RBX_PROBEKBD) {
+ i = *(uint8_t *)PTOV(0x496) & 0x10;
+ printf("Keyboard: %s\n", i ? "yes" : "no");
+ if (!i)
+ opts |= 1 << RBX_DUAL | 1 << RBX_SERIAL;
+ opts &= ~(1 << RBX_PROBEKBD);
+ }
+ ioctrl = opts & 1 << RBX_DUAL ? (IO_SERIAL|IO_KEYBOARD) :
+ opts & 1 << RBX_SERIAL ? IO_SERIAL : IO_KEYBOARD;
+ if (ioctrl & IO_SERIAL)
+ sio_init();
+ } else {
+ for (q = arg--; *q && *q != '('; q++);
+ if (*q) {
+ drv = -1;
+ if (arg[1] == ':') {
+ drv = *arg - '0';
+ if (drv > 9)
+ return (-1);
+ arg += 2;
+ }
+ if (q - arg != 2)
+ return -1;
+ for (i = 0; arg[0] != dev_nm[i][0] ||
+ arg[1] != dev_nm[i][1]; i++)
+ if (i == NDEV - 1)
+ return -1;
+ dsk.type = i;
+ arg += 3;
+ dsk.unit = *arg - '0';
+ if (arg[1] != ',' || dsk.unit > 9)
+ return -1;
+ arg += 2;
+ dsk.slice = WHOLE_DISK_SLICE;
+ if (arg[1] == ',') {
+ dsk.slice = *arg - '0' + 1;
+ if (dsk.slice > NDOSPART)
+ return -1;
+ arg += 2;
+ }
+ if (arg[1] != ')')
+ return -1;
+ dsk.part = *arg - 'a';
+ if (dsk.part > 7)
+ return (-1);
+ arg += 2;
+ if (drv == -1)
+ drv = dsk.unit;
+ dsk.drive = (dsk.type <= TYPE_MAXHARD
+ ? DRV_HARD : 0) + drv;
+ dsk_meta = 0;
+ }
+ if ((i = p - arg - !*(p - 1))) {
+ if ((size_t)i >= sizeof(kname))
+ return -1;
+ memcpy(kname, arg, i + 1);
+ }
+ }
+ arg = p;
+ }
+ return 0;
+}
+
+static int
+dskread(void *buf, unsigned lba, unsigned nblk)
+{
+ struct dos_partition *dp;
+ struct disklabel *d;
+ char *sec;
+ unsigned sl, i;
+
+ if (!dsk_meta) {
+ sec = dmadat->secbuf;
+ dsk.start = 0;
+ if (drvread(sec, DOSBBSECTOR, 1))
+ return -1;
+ dp = (void *)(sec + DOSPARTOFF);
+ sl = dsk.slice;
+ if (sl < BASE_SLICE) {
+ for (i = 0; i < NDOSPART; i++)
+ if (dp[i].dp_typ == DOSPTYP_386BSD &&
+ (dp[i].dp_flag & 0x80 || sl < BASE_SLICE)) {
+ sl = BASE_SLICE + i;
+ if (dp[i].dp_flag & 0x80 ||
+ dsk.slice == COMPATIBILITY_SLICE)
+ break;
+ }
+ if (dsk.slice == WHOLE_DISK_SLICE)
+ dsk.slice = sl;
+ }
+ if (sl != WHOLE_DISK_SLICE) {
+ if (sl != COMPATIBILITY_SLICE)
+ dp += sl - BASE_SLICE;
+ if (dp->dp_typ != DOSPTYP_386BSD) {
+ printf("Invalid %s\n", "slice");
+ return -1;
+ }
+ dsk.start = dp->dp_start;
+ }
+ if (drvread(sec, dsk.start + LABELSECTOR, 1))
+ return -1;
+ d = (void *)(sec + LABELOFFSET);
+ if (d->d_magic != DISKMAGIC || d->d_magic2 != DISKMAGIC) {
+ if (dsk.part != RAW_PART) {
+ printf("Invalid %s\n", "label");
+ return -1;
+ }
+ } else {
+ if (!dsk.init) {
+ if (d->d_type == DTYPE_SCSI)
+ dsk.type = TYPE_DA;
+ dsk.init++;
+ }
+ if (dsk.part >= d->d_npartitions ||
+ !d->d_partitions[dsk.part].p_size) {
+ printf("Invalid %s\n", "partition");
+ return -1;
+ }
+ dsk.start += d->d_partitions[dsk.part].p_offset;
+ dsk.start -= d->d_partitions[RAW_PART].p_offset;
+ }
+ }
+ return drvread(buf, dsk.start + lba, nblk);
+}
+
+static void
+printf(const char *fmt,...)
+{
+ va_list ap;
+ char buf[10];
+ char *s;
+ unsigned u;
+ int c;
+
+ va_start(ap, fmt);
+ while ((c = *fmt++)) {
+ if (c == '%') {
+ c = *fmt++;
+ switch (c) {
+ case 'c':
+ putchar(va_arg(ap, int));
+ continue;
+ case 's':
+ for (s = va_arg(ap, char *); *s; s++)
+ putchar(*s);
+ continue;
+ case 'u':
+ u = va_arg(ap, unsigned);
+ s = buf;
+ do
+ *s++ = '0' + u % 10U;
+ while (u /= 10U);
+ while (--s >= buf)
+ putchar(*s);
+ continue;
+ }
+ }
+ putchar(c);
+ }
+ va_end(ap);
+ return;
+}
+
+static void
+putchar(int c)
+{
+ if (c == '\n')
+ xputc('\r');
+ xputc(c);
+}
+
+static int
+drvread(void *buf, unsigned lba, unsigned nblk)
+{
+ static unsigned c = 0x2d5c7c2f;
+
+ printf("%c\b", c = c << 8 | c >> 24);
+ v86.ctl = V86_ADDR | V86_CALLF | V86_FLAGS;
+ v86.addr = XREADORG; /* call to xread in boot1 */
+ v86.es = VTOPSEG(buf);
+ v86.eax = lba;
+ v86.ebx = VTOPOFF(buf);
+ v86.ecx = lba >> 16;
+ v86.edx = nblk << 8 | dsk.drive;
+ v86int();
+ v86.ctl = V86_FLAGS;
+ if (V86_CY(v86.efl)) {
+ printf("error %u lba %u\n", v86.eax >> 8 & 0xff, lba);
+ return -1;
+ }
+ return 0;
+}
+
+static int
+keyhit(unsigned ticks)
+{
+ uint32_t t0, t1;
+
+ if (opts & 1 << RBX_NOINTR)
+ return 0;
+ t0 = 0;
+ for (;;) {
+ if (xgetc(1))
+ return 1;
+ t1 = *(uint32_t *)PTOV(0x46c);
+ if (!t0)
+ t0 = t1;
+ if (t1 < t0 || t1 >= t0 + ticks)
+ return 0;
+ }
+}
+
+static int
+xputc(int c)
+{
+ if (ioctrl & IO_KEYBOARD)
+ putc(c);
+ if (ioctrl & IO_SERIAL)
+ sio_putc(c);
+ return c;
+}
+
+static int
+xgetc(int fn)
+{
+ if (opts & 1 << RBX_NOINTR)
+ return 0;
+ for (;;) {
+ if (ioctrl & IO_KEYBOARD && getc(1))
+ return fn ? 1 : getc(0);
+ if (ioctrl & IO_SERIAL && sio_ischar())
+ return fn ? 1 : sio_getc();
+ if (fn)
+ return 0;
+ }
+}
+
+static int
+getc(int fn)
+{
+ v86.addr = 0x16;
+ v86.eax = fn << 8;
+ v86int();
+ return fn == 0 ? v86.eax & 0xff : !V86_ZR(v86.efl);
+}
diff --git a/sys/boot/i386/boot2/lib.h b/sys/boot/i386/boot2/lib.h
new file mode 100644
index 0000000..4bb93cf
--- /dev/null
+++ b/sys/boot/i386/boot2/lib.h
@@ -0,0 +1,24 @@
+/*
+ * Copyright (c) 1998 Robert Nordier
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms are freely
+ * permitted provided that the above copyright notice and this
+ * paragraph and the following disclaimer are duplicated in all
+ * such forms.
+ *
+ * This software is provided "AS IS" and without any express or
+ * implied warranties, including, without limitation, the implied
+ * warranties of merchantability and fitness for a particular
+ * purpose.
+ */
+
+/*
+ * $FreeBSD$
+ */
+
+void sio_init(void);
+void sio_flush(void);
+void sio_putc(int);
+int sio_getc(void);
+int sio_ischar(void);
diff --git a/sys/boot/i386/boot2/sio.S b/sys/boot/i386/boot2/sio.S
new file mode 100644
index 0000000..e298205
--- /dev/null
+++ b/sys/boot/i386/boot2/sio.S
@@ -0,0 +1,80 @@
+#
+# Copyright (c) 1998 Robert Nordier
+# All rights reserved.
+#
+# Redistribution and use in source and binary forms are freely
+# permitted provided that the above copyright notice and this
+# paragraph and the following disclaimer are duplicated in all
+# such forms.
+#
+# This software is provided "AS IS" and without any express or
+# implied warranties, including, without limitation, the implied
+# warranties of merchantability and fitness for a particular
+# purpose.
+#
+
+# $FreeBSD$
+
+ .set SIO_PRT,SIOPRT # Base port
+ .set SIO_FMT,SIOFMT # 8N1
+ .set SIO_DIV,(115200/SIOSPD) # 115200 / SPD
+
+ .globl sio_init
+ .globl sio_flush
+ .globl sio_putc
+ .globl sio_getc
+ .globl sio_ischar
+
+# void sio_init(void)
+
+sio_init: movw $SIO_PRT+0x3,%dx # Data format reg
+ movb $SIO_FMT|0x80,%al # Set format
+ outb %al,(%dx) # and DLAB
+ pushl %edx # Save
+ subb $0x3,%dl # Divisor latch reg
+ movw $SIO_DIV,%ax # Set
+ outw %ax,(%dx) # BPS
+ popl %edx # Restore
+ movb $SIO_FMT,%al # Clear
+ outb %al,(%dx) # DLAB
+ incl %edx # Modem control reg
+ movb $0x3,%al # Set RTS,
+ outb %al,(%dx) # DTR
+ incl %edx # Line status reg
+
+# void sio_flush(void)
+
+sio_flush.0: call sio_getc.1 # Get character
+sio_flush: call sio_ischar # Check for character
+ jnz sio_flush.0 # Till none
+ ret # To caller
+
+# void sio_putc(int c)
+
+sio_putc: movw $SIO_PRT+0x5,%dx # Line status reg
+ xor %ecx,%ecx # Timeout
+ movb $0x40,%ch # counter
+sio_putc.1: inb (%dx),%al # Transmitter
+ testb $0x20,%al # buffer empty?
+ loopz sio_putc.1 # No
+ jz sio_putc.2 # If timeout
+ movb 0x4(%esp,1),%al # Get character
+ subb $0x5,%dl # Transmitter hold reg
+ outb %al,(%dx) # Write character
+sio_putc.2: ret $0x4 # To caller
+
+# int sio_getc(void)
+
+sio_getc: call sio_ischar # Character available?
+ jz sio_getc # No
+sio_getc.1: subb $0x5,%dl # Receiver buffer reg
+ inb (%dx),%al # Read character
+ ret # To caller
+
+# int sio_ischar(void)
+
+sio_ischar: movw $SIO_PRT+0x5,%dx # Line status register
+ xorl %eax,%eax # Zero
+ inb (%dx),%al # Received data
+ andb $0x1,%al # ready?
+ ret # To caller
diff --git a/sys/boot/i386/boot2/sio.s b/sys/boot/i386/boot2/sio.s
new file mode 100644
index 0000000..e298205
--- /dev/null
+++ b/sys/boot/i386/boot2/sio.s
@@ -0,0 +1,80 @@
+#
+# Copyright (c) 1998 Robert Nordier
+# All rights reserved.
+#
+# Redistribution and use in source and binary forms are freely
+# permitted provided that the above copyright notice and this
+# paragraph and the following disclaimer are duplicated in all
+# such forms.
+#
+# This software is provided "AS IS" and without any express or
+# implied warranties, including, without limitation, the implied
+# warranties of merchantability and fitness for a particular
+# purpose.
+#
+
+# $FreeBSD$
+
+ .set SIO_PRT,SIOPRT # Base port
+ .set SIO_FMT,SIOFMT # 8N1
+ .set SIO_DIV,(115200/SIOSPD) # 115200 / SPD
+
+ .globl sio_init
+ .globl sio_flush
+ .globl sio_putc
+ .globl sio_getc
+ .globl sio_ischar
+
+# void sio_init(void)
+
+sio_init: movw $SIO_PRT+0x3,%dx # Data format reg
+ movb $SIO_FMT|0x80,%al # Set format
+ outb %al,(%dx) # and DLAB
+ pushl %edx # Save
+ subb $0x3,%dl # Divisor latch reg
+ movw $SIO_DIV,%ax # Set
+ outw %ax,(%dx) # BPS
+ popl %edx # Restore
+ movb $SIO_FMT,%al # Clear
+ outb %al,(%dx) # DLAB
+ incl %edx # Modem control reg
+ movb $0x3,%al # Set RTS,
+ outb %al,(%dx) # DTR
+ incl %edx # Line status reg
+
+# void sio_flush(void)
+
+sio_flush.0: call sio_getc.1 # Get character
+sio_flush: call sio_ischar # Check for character
+ jnz sio_flush.0 # Till none
+ ret # To caller
+
+# void sio_putc(int c)
+
+sio_putc: movw $SIO_PRT+0x5,%dx # Line status reg
+ xor %ecx,%ecx # Timeout
+ movb $0x40,%ch # counter
+sio_putc.1: inb (%dx),%al # Transmitter
+ testb $0x20,%al # buffer empty?
+ loopz sio_putc.1 # No
+ jz sio_putc.2 # If timeout
+ movb 0x4(%esp,1),%al # Get character
+ subb $0x5,%dl # Transmitter hold reg
+ outb %al,(%dx) # Write character
+sio_putc.2: ret $0x4 # To caller
+
+# int sio_getc(void)
+
+sio_getc: call sio_ischar # Character available?
+ jz sio_getc # No
+sio_getc.1: subb $0x5,%dl # Receiver buffer reg
+ inb (%dx),%al # Read character
+ ret # To caller
+
+# int sio_ischar(void)
+
+sio_ischar: movw $SIO_PRT+0x5,%dx # Line status register
+ xorl %eax,%eax # Zero
+ inb (%dx),%al # Received data
+ andb $0x1,%al # ready?
+ ret # To caller
diff --git a/sys/boot/i386/btx/Makefile b/sys/boot/i386/btx/Makefile
new file mode 100644
index 0000000..39f78ed
--- /dev/null
+++ b/sys/boot/i386/btx/Makefile
@@ -0,0 +1,5 @@
+# $FreeBSD$
+
+SUBDIR= btx btxldr lib
+
+.include <bsd.subdir.mk>
diff --git a/sys/boot/i386/btx/Makefile.inc b/sys/boot/i386/btx/Makefile.inc
new file mode 100644
index 0000000..5a83d8d
--- /dev/null
+++ b/sys/boot/i386/btx/Makefile.inc
@@ -0,0 +1,2 @@
+# $FreeBSD$
+.include <${.CURDIR}/../../Makefile.inc>
diff --git a/sys/boot/i386/btx/btx/Makefile b/sys/boot/i386/btx/btx/Makefile
new file mode 100644
index 0000000..c2a33a6
--- /dev/null
+++ b/sys/boot/i386/btx/btx/Makefile
@@ -0,0 +1,40 @@
+# $FreeBSD$
+
+M4?= m4
+
+.if defined(PAGING)
+M4FLAGS+= -DPAGING
+.endif
+
+.if defined(BOOT_BTX_NOHANG)
+BOOT_BTX_FLAGS=0x1
+.else
+BOOT_BTX_FLAGS=0x0
+.endif
+
+AFLAGS+= --defsym BTX_FLAGS=${BOOT_BTX_FLAGS}
+
+.if defined(BTX_SERIAL)
+BOOT_COMCONSOLE_PORT?= 0x3f8
+BOOT_COMCONSOLE_SPEED?= 9600
+B2SIOFMT?= 0x3
+
+M4FLAGS+= -DBTX_SERIAL -DSIOPRT=${BOOT_COMCONSOLE_PORT} \
+ -DSIOFMT=${B2SIOFMT} -DSIOSPD=${BOOT_COMCONSOLE_SPEED}
+.endif
+
+ORG= 0x9000
+
+all: btx
+
+btx: btx.o
+ ${LD} -N -e start -Ttext ${ORG} -o btx.out btx.o
+ objcopy -S -O binary btx.out ${.TARGET}
+
+btx.o: btx.s
+ (cd ${.CURDIR}; ${M4} ${M4FLAGS} btx.s) | \
+ ${AS} ${AFLAGS} -o ${.TARGET}
+
+CLEANFILES+= btx btx.out btx.o
+
+.include <bsd.prog.mk>
diff --git a/sys/boot/i386/btx/btx/btx.S b/sys/boot/i386/btx/btx/btx.S
new file mode 100644
index 0000000..0215530
--- /dev/null
+++ b/sys/boot/i386/btx/btx/btx.S
@@ -0,0 +1,1173 @@
+#
+# Copyright (c) 1998 Robert Nordier
+# All rights reserved.
+#
+# Redistribution and use in source and binary forms are freely
+# permitted provided that the above copyright notice and this
+# paragraph and the following disclaimer are duplicated in all
+# such forms.
+#
+# This software is provided "AS IS" and without any express or
+# implied warranties, including, without limitation, the implied
+# warranties of merchantability and fitness for a particular
+# purpose.
+#
+
+# $FreeBSD$
+
+#
+# Memory layout.
+#
+ .set MEM_BTX,0x1000 # Start of BTX memory
+ .set MEM_ESP0,0x1800 # Supervisor stack
+ .set MEM_BUF,0x1800 # Scratch buffer
+ .set MEM_ESP1,0x1e00 # Link stack
+ .set MEM_IDT,0x1e00 # IDT
+ .set MEM_TSS,0x1f98 # TSS
+ .set MEM_MAP,0x2000 # I/O bit map
+ .set MEM_DIR,0x4000 # Page directory
+ .set MEM_TBL,0x5000 # Page tables
+ .set MEM_ORG,0x9000 # BTX code
+ .set MEM_USR,0xa000 # Start of user memory
+#
+# Paging control.
+#
+ .set PAG_SIZ,0x1000 # Page size
+ .set PAG_CNT,0x1000 # Pages to map
+#
+# Segment selectors.
+#
+ .set SEL_SCODE,0x8 # Supervisor code
+ .set SEL_SDATA,0x10 # Supervisor data
+ .set SEL_RCODE,0x18 # Real mode code
+ .set SEL_RDATA,0x20 # Real mode data
+ .set SEL_UCODE,0x28|3 # User code
+ .set SEL_UDATA,0x30|3 # User data
+ .set SEL_TSS,0x38 # TSS
+#
+# Task state segment fields.
+#
+ .set TSS_ESP0,0x4 # PL 0 ESP
+ .set TSS_SS0,0x8 # PL 0 SS
+ .set TSS_ESP1,0xc # PL 1 ESP
+ .set TSS_MAP,0x66 # I/O bit map base
+#
+# System calls.
+#
+ .set SYS_EXIT,0x0 # Exit
+ .set SYS_EXEC,0x1 # Exec
+#
+# V86 constants.
+#
+ .set V86_FLG,0x208eff # V86 flag mask
+ .set V86_STK,0x400 # V86 stack allowance
+#
+# Dump format control bytes.
+#
+ .set DMP_X16,0x1 # Word
+ .set DMP_X32,0x2 # Long
+ .set DMP_MEM,0x4 # Memory
+ .set DMP_EOL,0x8 # End of line
+#
+# Screen defaults and assumptions.
+#
+ .set SCR_MAT,0x7 # Mode/attribute
+ .set SCR_COL,0x50 # Columns per row
+ .set SCR_ROW,0x19 # Rows per screen
+#
+# BIOS Data Area locations.
+#
+ .set BDA_MEM,0x413 # Free memory
+ .set BDA_KEYFLAGS,0x417 # Keyboard shift-state flags
+ .set BDA_SCR,0x449 # Video mode
+ .set BDA_POS,0x450 # Cursor position
+ .set BDA_BOOT,0x472 # Boot howto flag
+#
+# Derivations, for brevity.
+#
+ .set _ESP0H,MEM_ESP0>>0x8 # Byte 1 of ESP0
+ .set _ESP1H,MEM_ESP1>>0x8 # Byte 1 of ESP1
+ .set _TSSIO,MEM_MAP-MEM_TSS # TSS I/O base
+ .set _TSSLM,MEM_DIR-MEM_TSS-1 # TSS limit
+ .set _IDTLM,MEM_TSS-MEM_IDT-1 # IDT limit
+#
+# Code segment.
+#
+ .globl start
+ .code16
+start: # Start of code
+#
+# BTX header.
+#
+btx_hdr: .byte 0xeb # Machine ID
+ .byte 0xe # Header size
+ .ascii "BTX" # Magic
+ .byte 0x1 # Major version
+ .byte 0x1 # Minor version
+ .byte BTX_FLAGS # Flags
+ .word PAG_CNT-MEM_ORG>>0xc # Paging control
+ .word break-start # Text size
+ .long 0x0 # Entry address
+#
+# Initialization routine.
+#
+init: cli # Disable interrupts
+ xor %ax,%ax # Zero/segment
+ mov %ax,%ss # Set up
+ mov $MEM_ESP0,%sp # stack
+ mov %ax,%es # Address
+ mov %ax,%ds # data
+ pushl $0x2 # Clear
+ popfl # flags
+#
+# Initialize memory.
+#
+ mov $MEM_IDT,%di # Memory to initialize
+ mov $(MEM_ORG-MEM_IDT)/2,%cx # Words to zero
+ push %di # Save
+ rep # Zero-fill
+ stosw # memory
+ pop %di # Restore
+#
+# Create IDT.
+#
+ mov $idtctl,%si # Control string
+init.1: lodsb # Get entry
+ cbw # count
+ xchg %ax,%cx # as word
+ jcxz init.4 # If done
+ lodsb # Get segment
+ xchg %ax,%dx # P:DPL:type
+ lodsw # Get control
+ xchg %ax,%bx # set
+ lodsw # Get handler offset
+ mov $SEL_SCODE,%dh # Segment selector
+init.2: shr %bx # Handle this int?
+ jnc init.3 # No
+ mov %ax,(%di) # Set handler offset
+ mov %dh,0x2(%di) # and selector
+ mov %dl,0x5(%di) # Set P:DPL:type
+ add $0x4,%ax # Next handler
+init.3: lea 0x8(%di),%di # Next entry
+ loop init.2 # Till set done
+ jmp init.1 # Continue
+#
+# Initialize TSS.
+#
+init.4: movb $_ESP0H,TSS_ESP0+1(%di) # Set ESP0
+ movb $SEL_SDATA,TSS_SS0(%di) # Set SS0
+ movb $_ESP1H,TSS_ESP1+1(%di) # Set ESP1
+ movb $_TSSIO,TSS_MAP(%di) # Set I/O bit map base
+ifdef(`PAGING',`
+#
+# Create page directory.
+#
+ xor %edx,%edx # Page
+ mov $PAG_SIZ>>0x8,%dh # size
+ xor %eax,%eax # Zero
+ mov $MEM_DIR,%di # Page directory
+ mov $PAG_CNT>>0xa,%cl # Entries
+ mov $MEM_TBL|0x7,%ax # First entry
+init.5: stosl # Write entry
+ add %dx,%ax # To next
+ loop init.5 # Till done
+#
+# Create page tables.
+#
+ mov $MEM_TBL,%di # Page table
+ mov $PAG_CNT>>0x8,%ch # Entries
+ xor %ax,%ax # Start address
+init.6: mov $0x7,%al # Set U:W:P flags
+ cmp btx_hdr+0x8,%cx # Standard user page?
+ jb init.7 # Yes
+ cmp $PAG_CNT-MEM_BTX>>0xc,%cx # BTX memory?
+ jae init.7 # No or first page
+ and $~0x2,%al # Clear W flag
+ cmp $PAG_CNT-MEM_USR>>0xc,%cx # User page zero?
+ jne init.7 # No
+ testb $0x80,btx_hdr+0x7 # Unmap it?
+ jz init.7 # No
+ and $~0x1,%al # Clear P flag
+init.7: stosl # Set entry
+ add %edx,%eax # Next address
+ loop init.6 # Till done
+')
+#
+# Bring up the system.
+#
+ mov $0x2820,%bx # Set protected mode
+ callw setpic # IRQ offsets
+ lidt idtdesc # Set IDT
+ifdef(`PAGING',`
+ xor %eax,%eax # Set base
+ mov $MEM_DIR>>0x8,%ah # of page
+ mov %eax,%cr3 # directory
+')
+ lgdt gdtdesc # Set GDT
+ mov %cr0,%eax # Switch to protected
+ifdef(`PAGING',`
+ or $0x80000001,%eax # mode and enable paging
+',`
+ or $0x01,%eax # mode
+')
+ mov %eax,%cr0 #
+ ljmp $SEL_SCODE,$init.8 # To 32-bit code
+ .code32
+init.8: xorl %ecx,%ecx # Zero
+ movb $SEL_SDATA,%cl # To 32-bit
+ movw %cx,%ss # stack
+#
+# Launch user task.
+#
+ movb $SEL_TSS,%cl # Set task
+ ltr %cx # register
+ movl $MEM_USR,%edx # User base address
+ movzwl %ss:BDA_MEM,%eax # Get free memory
+ shll $0xa,%eax # To bytes
+ subl $0x1000,%eax # Less arg space
+ subl %edx,%eax # Less base
+ movb $SEL_UDATA,%cl # User data selector
+ pushl %ecx # Set SS
+ pushl %eax # Set ESP
+ push $0x202 # Set flags (IF set)
+ push $SEL_UCODE # Set CS
+ pushl btx_hdr+0xc # Set EIP
+ pushl %ecx # Set GS
+ pushl %ecx # Set FS
+ pushl %ecx # Set DS
+ pushl %ecx # Set ES
+ pushl %edx # Set EAX
+ movb $0x7,%cl # Set remaining
+init.9: push $0x0 # general
+ loop init.9 # registers
+ifdef(`BTX_SERIAL',`
+ call sio_init # setup the serial console
+')
+ popa # and initialize
+ popl %es # Initialize
+ popl %ds # user
+ popl %fs # segment
+ popl %gs # registers
+ iret # To user mode
+#
+# Exit routine.
+#
+exit: cli # Disable interrupts
+ movl $MEM_ESP0,%esp # Clear stack
+#
+# Turn off paging.
+#
+ movl %cr0,%eax # Get CR0
+ andl $~0x80000000,%eax # Disable
+ movl %eax,%cr0 # paging
+ xorl %ecx,%ecx # Zero
+ movl %ecx,%cr3 # Flush TLB
+#
+# Restore the GDT in case we caught a kernel trap.
+#
+ lgdt gdtdesc # Set GDT
+#
+# To 16 bits.
+#
+ ljmpw $SEL_RCODE,$exit.1 # Reload CS
+ .code16
+exit.1: mov $SEL_RDATA,%cl # 16-bit selector
+ mov %cx,%ss # Reload SS
+ mov %cx,%ds # Load
+ mov %cx,%es # remaining
+ mov %cx,%fs # segment
+ mov %cx,%gs # registers
+#
+# To real-address mode.
+#
+ dec %ax # Switch to
+ mov %eax,%cr0 # real mode
+ ljmp $0x0,$exit.2 # Reload CS
+exit.2: xor %ax,%ax # Real mode segment
+ mov %ax,%ss # Reload SS
+ mov %ax,%ds # Address data
+ mov $0x7008,%bx # Set real mode
+ callw setpic # IRQ offsets
+ lidt ivtdesc # Set IVT
+#
+# Reboot or await reset.
+#
+ sti # Enable interrupts
+ testb $0x1,btx_hdr+0x7 # Reboot?
+exit.3: jz exit.3 # No
+ movw $0x1234, BDA_BOOT # Do a warm boot
+ ljmp $0xf000,$0xfff0 # reboot the machine
+#
+# Set IRQ offsets by reprogramming 8259A PICs.
+#
+setpic: in $0x21,%al # Save master
+ push %ax # IMR
+ in $0xa1,%al # Save slave
+ push %ax # IMR
+ movb $0x11,%al # ICW1 to
+ outb %al,$0x20 # master,
+ outb %al,$0xa0 # slave
+ movb %bl,%al # ICW2 to
+ outb %al,$0x21 # master
+ movb %bh,%al # ICW2 to
+ outb %al,$0xa1 # slave
+ movb $0x4,%al # ICW3 to
+ outb %al,$0x21 # master
+ movb $0x2,%al # ICW3 to
+ outb %al,$0xa1 # slave
+ movb $0x1,%al # ICW4 to
+ outb %al,$0x21 # master,
+ outb %al,$0xa1 # slave
+ pop %ax # Restore slave
+ outb %al,$0xa1 # IMR
+ pop %ax # Restore master
+ outb %al,$0x21 # IMR
+ retw # To caller
+ .code32
+#
+# Initiate return from V86 mode to user mode.
+#
+inthlt: hlt # To supervisor mode
+#
+# Exception jump table.
+#
+intx00: push $0x0 # Int 0x0: #DE
+ jmp ex_noc # Divide error
+ push $0x1 # Int 0x1: #DB
+ jmp ex_noc # Debug
+ push $0x3 # Int 0x3: #BP
+ jmp ex_noc # Breakpoint
+ push $0x4 # Int 0x4: #OF
+ jmp ex_noc # Overflow
+ push $0x5 # Int 0x5: #BR
+ jmp ex_noc # BOUND range exceeded
+ push $0x6 # Int 0x6: #UD
+ jmp ex_noc # Invalid opcode
+ push $0x7 # Int 0x7: #NM
+ jmp ex_noc # Device not available
+ push $0x8 # Int 0x8: #DF
+ jmp except # Double fault
+ push $0xa # Int 0xa: #TS
+ jmp except # Invalid TSS
+ push $0xb # Int 0xb: #NP
+ jmp except # Segment not present
+ push $0xc # Int 0xc: #SS
+ jmp except # Stack segment fault
+ push $0xd # Int 0xd: #GP
+ jmp ex_v86 # General protection
+ push $0xe # Int 0xe: #PF
+ jmp except # Page fault
+intx10: push $0x10 # Int 0x10: #MF
+ jmp ex_noc # Floating-point error
+#
+# Handle #GP exception.
+#
+ex_v86: testb $0x2,0x12(%esp,1) # V86 mode?
+ jz except # No
+ jmp v86mon # To monitor
+#
+# Save a zero error code.
+#
+ex_noc: pushl (%esp,1) # Duplicate int no
+ movb $0x0,0x4(%esp,1) # Fake error code
+#
+# Handle exception.
+#
+except: cld # String ops inc
+ pushl %ds # Save
+ pushl %es # most
+ pusha # registers
+ movb $0x6,%al # Push loop count
+ testb $0x2,0x3a(%esp,1) # V86 mode?
+ jnz except.1 # Yes
+ pushl %gs # Set GS
+ pushl %fs # Set FS
+ pushl %ds # Set DS
+ pushl %es # Set ES
+ movb $0x2,%al # Push loop count
+ cmpw $SEL_SCODE,0x44(%esp,1) # Supervisor mode?
+ jne except.1 # No
+ pushl %ss # Set SS
+ leal 0x50(%esp,1),%eax # Set
+ pushl %eax # ESP
+ jmp except.2 # Join common code
+except.1: pushl 0x50(%esp,1) # Set GS, FS, DS, ES
+ decb %al # (if V86 mode), and
+ jne except.1 # SS, ESP
+except.2: push $SEL_SDATA # Set up
+ popl %ds # to
+ pushl %ds # address
+ popl %es # data
+ movl %esp,%ebx # Stack frame
+ movl $dmpfmt,%esi # Dump format string
+ movl $MEM_BUF,%edi # Buffer
+ pushl %edi # Dump to
+ call dump # buffer
+ popl %esi # and
+ call putstr # display
+ leal 0x18(%esp,1),%esp # Discard frame
+ popa # Restore
+ popl %es # registers
+ popl %ds # saved
+ cmpb $0x3,(%esp,1) # Breakpoint?
+ je except.3 # Yes
+ cmpb $0x1,(%esp,1) # Debug?
+ jne except.2a # No
+ testl $0x100,0x10(%esp,1) # Trap flag set?
+ jnz except.3 # Yes
+except.2a: jmp exit # Exit
+except.3: leal 0x8(%esp,1),%esp # Discard err, int no
+ iret # From interrupt
+#
+# Return to user mode from V86 mode.
+#
+intrtn: cld # String ops inc
+ pushl %ds # Address
+ popl %es # data
+ leal 0x3c(%ebp),%edx # V86 Segment registers
+ movl MEM_TSS+TSS_ESP1,%esi # Link stack pointer
+ lodsl # INT_V86 args pointer
+ movl %esi,%ebx # Saved exception frame
+ testl %eax,%eax # INT_V86 args?
+ jz intrtn.2 # No
+ movl $MEM_USR,%edi # User base
+ movl 0x1c(%esi),%ebx # User ESP
+ movl %eax,(%edi,%ebx,1) # Restore to user stack
+ leal 0x8(%edi,%eax,1),%edi # Arg segment registers
+ testb $0x4,-0x6(%edi) # Return flags?
+ jz intrtn.1 # No
+ movl 0x30(%ebp),%eax # Get V86 flags
+ movw %ax,0x18(%esi) # Set user flags
+intrtn.1: leal 0x10(%esi),%ebx # Saved exception frame
+ xchgl %edx,%esi # Segment registers
+ movb $0x4,%cl # Update seg regs
+ rep # in INT_V86
+ movsl # args
+intrtn.2: movl %edx,%esi # Segment registers
+ leal 0x28(%ebp),%edi # Set up seg
+ movb $0x4,%cl # regs for
+ rep # later
+ movsl # pop
+ movl %ebx,%esi # Restore exception
+ movb $0x5,%cl # frame to
+ rep # supervisor
+ movsl # stack
+ movl %esi,MEM_TSS+TSS_ESP1 # Link stack pointer
+ popa # Restore
+ leal 0x8(%esp,1),%esp # Discard err, int no
+ popl %es # Restore
+ popl %ds # user
+ popl %fs # segment
+ popl %gs # registers
+ iret # To user mode
+#
+# V86 monitor.
+#
+v86mon: cld # String ops inc
+ pushl $SEL_SDATA # Set up for
+ popl %ds # flat addressing
+ pusha # Save registers
+ movl %esp,%ebp # Address stack frame
+ movzwl 0x2c(%ebp),%edi # Load V86 CS
+ shll $0x4,%edi # To linear
+ movl 0x28(%ebp),%esi # Load V86 IP
+ addl %edi,%esi # Code pointer
+ xorl %ecx,%ecx # Zero
+ movb $0x2,%cl # 16-bit operands
+ xorl %eax,%eax # Zero
+v86mon.1: lodsb # Get opcode
+ cmpb $0x66,%al # Operand size prefix?
+ jne v86mon.2 # No
+ movb $0x4,%cl # 32-bit operands
+ jmp v86mon.1 # Continue
+v86mon.2: cmpb $0xf4,%al # HLT?
+ jne v86mon.3 # No
+ cmpl $inthlt+0x1,%esi # Is inthlt?
+ jne v86mon.7 # No (ignore)
+ jmp intrtn # Return to user mode
+v86mon.3: cmpb $0xf,%al # Prefixed instruction?
+ jne v86mon.4 # No
+ cmpb $0x09,(%esi) # Is it a WBINVD?
+ je v86wbinvd # Yes
+ cmpb $0x30,(%esi) # Is it a WRMSR?
+ je v86wrmsr # Yes
+ cmpb $0x32,(%esi) # Is it a RDMSR?
+ je v86rdmsr # Yes
+ cmpb $0x20,(%esi) # Is this a
+ jne v86mon.4 # MOV EAX,CR0
+ cmpb $0xc0,0x1(%esi) # instruction?
+ je v86mov # Yes
+v86mon.4: cmpb $0xfa,%al # CLI?
+ je v86cli # Yes
+ cmpb $0xfb,%al # STI?
+ je v86sti # Yes
+ movzwl 0x38(%ebp),%ebx # Load V86 SS
+ shll $0x4,%ebx # To offset
+ pushl %ebx # Save
+ addl 0x34(%ebp),%ebx # Add V86 SP
+ movl 0x30(%ebp),%edx # Load V86 flags
+ cmpb $0x9c,%al # PUSHF/PUSHFD?
+ je v86pushf # Yes
+ cmpb $0x9d,%al # POPF/POPFD?
+ je v86popf # Yes
+ cmpb $0xcd,%al # INT imm8?
+ je v86intn # Yes
+ cmpb $0xcf,%al # IRET/IRETD?
+ je v86iret # Yes
+ popl %ebx # Restore
+ popa # Restore
+ jmp except # Handle exception
+v86mon.5: movl %edx,0x30(%ebp) # Save V86 flags
+v86mon.6: popl %edx # V86 SS adjustment
+ subl %edx,%ebx # Save V86
+ movl %ebx,0x34(%ebp) # SP
+v86mon.7: subl %edi,%esi # From linear
+ movl %esi,0x28(%ebp) # Save V86 IP
+ popa # Restore
+ leal 0x8(%esp,1),%esp # Discard int no, error
+ iret # To V86 mode
+#
+# Emulate MOV EAX,CR0.
+#
+v86mov: movl %cr0,%eax # CR0 to
+ movl %eax,0x1c(%ebp) # saved EAX
+ incl %esi # Adjust IP
+#
+# Return from emulating a 0x0f prefixed instruction
+#
+v86preret: incl %esi # Adjust IP
+ jmp v86mon.7 # Finish up
+#
+# Emulate WBINVD
+#
+v86wbinvd: wbinvd # Write back and invalidate
+ # cache
+ jmp v86preret # Finish up
+#
+# Emulate WRMSR
+#
+v86wrmsr: movl 0x18(%ebp),%ecx # Get user's %ecx (MSR to write)
+ movl 0x14(%ebp),%edx # Load the value
+ movl 0x1c(%ebp),%eax # to write
+ wrmsr # Write MSR
+ jmp v86preret # Finish up
+#
+# Emulate RDMSR
+#
+v86rdmsr: movl 0x18(%ebp),%ecx # MSR to read
+ rdmsr # Read the MSR
+ movl %eax,0x1c(%ebp) # Return the value of
+ movl %edx,0x14(%ebp) # the MSR to the user
+ jmp v86preret # Finish up
+#
+# Emulate CLI.
+#
+v86cli: andb $~0x2,0x31(%ebp) # Clear IF
+ jmp v86mon.7 # Finish up
+#
+# Emulate STI.
+#
+v86sti: orb $0x2,0x31(%ebp) # Set IF
+ jmp v86mon.7 # Finish up
+#
+# Emulate PUSHF/PUSHFD.
+#
+v86pushf: subl %ecx,%ebx # Adjust SP
+ cmpb $0x4,%cl # 32-bit
+ je v86pushf.1 # Yes
+ data16 # 16-bit
+v86pushf.1: movl %edx,(%ebx) # Save flags
+ jmp v86mon.6 # Finish up
+#
+# Emulate IRET/IRETD.
+#
+v86iret: movzwl (%ebx),%esi # Load V86 IP
+ movzwl 0x2(%ebx),%edi # Load V86 CS
+ leal 0x4(%ebx),%ebx # Adjust SP
+ movl %edi,0x2c(%ebp) # Save V86 CS
+ xorl %edi,%edi # No ESI adjustment
+#
+# Emulate POPF/POPFD (and remainder of IRET/IRETD).
+#
+v86popf: cmpb $0x4,%cl # 32-bit?
+ je v86popf.1 # Yes
+ movl %edx,%eax # Initialize
+ data16 # 16-bit
+v86popf.1: movl (%ebx),%eax # Load flags
+ addl %ecx,%ebx # Adjust SP
+ andl $V86_FLG,%eax # Merge
+ andl $~V86_FLG,%edx # the
+ orl %eax,%edx # flags
+ jmp v86mon.5 # Finish up
+#
+# trap int 15, function 87
+# reads %es:%si from saved registers on stack to find a GDT containing
+# source and destination locations
+# reads count of words from saved %cx
+# returns success by setting %ah to 0
+#
+int15_87: pushl %eax # Save
+ pushl %ebx # some information
+ pushl %esi # onto the stack.
+ pushl %edi
+ xorl %eax,%eax # clean EAX
+ xorl %ebx,%ebx # clean EBX
+ movl 0x4(%ebp),%esi # Get user's ESI
+ movl 0x3C(%ebp),%ebx # store ES
+ movw %si,%ax # store SI
+ shll $0x4,%ebx # Make it a seg.
+ addl %eax,%ebx # ebx=(es<<4)+si
+ movb 0x14(%ebx),%al # Grab the
+ movb 0x17(%ebx),%ah # necessary
+ shll $0x10,%eax # information
+ movw 0x12(%ebx),%ax # from
+ movl %eax,%esi # the
+ movb 0x1c(%ebx),%al # GDT in order to
+ movb 0x1f(%ebx),%ah # have %esi offset
+ shll $0x10,%eax # of source and %edi
+ movw 0x1a(%ebx),%ax # of destination.
+ movl %eax,%edi
+ pushl %ds # Make:
+ popl %es # es = ds
+ pushl %ecx # stash ECX
+ xorl %ecx,%ecx # highw of ECX is clear
+ movw 0x18(%ebp),%cx # Get user's ECX
+ shll $0x1,%ecx # Convert from num words to num
+ # bytes
+ rep # repeat...
+ movsb # perform copy.
+ popl %ecx # Restore
+ popl %edi
+ popl %esi # previous
+ popl %ebx # register
+ popl %eax # values.
+ movb $0x0,0x1d(%ebp) # set ah = 0 to indicate
+ # success
+ andb $0xfe,%dl # clear CF
+ jmp v86mon.5 # Finish up
+
+#
+# Reboot the machine by setting the reboot flag and exiting
+#
+reboot: orb $0x1,btx_hdr+0x7 # Set the reboot flag
+ jmp exit # Terminate BTX and reboot
+
+#
+# Emulate INT imm8... also make sure to check if it's int 15/87
+#
+v86intn: lodsb # Get int no
+ cmpb $0x19,%al # is it int 19?
+ je reboot # yes, reboot the machine
+ cmpb $0x15,%al # is it int 15?
+ jne v86intn.3 # no, skip parse
+ pushl %eax # stash EAX
+ movl 0x1c(%ebp),%eax # user's saved EAX
+ cmpb $0x87,%ah # is it the memcpy subfunction?
+ jne v86intn.1 # no, keep checking
+ popl %eax # get the stack straight
+ jmp int15_87 # it's our cue
+v86intn.1: cmpw $0x4f53,%ax # is it the delete key callout?
+ jne v86intn.2 # no, handle the int normally
+ movb BDA_KEYFLAGS,%al # get the shift key state
+ andb $0xc,%al # mask off just Ctrl and Alt
+ cmpb $0xc,%al # are both Ctrl and Alt down?
+ jne v86intn.2 # no, handle the int normally
+ popl %eax # restore EAX
+ jmp reboot # reboot the machine
+v86intn.2: popl %eax # restore EAX
+v86intn.3: subl %edi,%esi # From
+ shrl $0x4,%edi # linear
+ movw %dx,-0x2(%ebx) # Save flags
+ movw %di,-0x4(%ebx) # Save CS
+ leal -0x6(%ebx),%ebx # Adjust SP
+ movw %si,(%ebx) # Save IP
+ shll $0x2,%eax # Scale
+ movzwl (%eax),%esi # Load IP
+ movzwl 0x2(%eax),%edi # Load CS
+ movl %edi,0x2c(%ebp) # Save CS
+ xorl %edi,%edi # No ESI adjustment
+ andb $~0x1,%dh # Clear TF
+ jmp v86mon.5 # Finish up
+#
+# Hardware interrupt jump table.
+#
+intx20: push $0x8 # Int 0x20: IRQ0
+ jmp int_hw # V86 int 0x8
+ push $0x9 # Int 0x21: IRQ1
+ jmp int_hw # V86 int 0x9
+ push $0xa # Int 0x22: IRQ2
+ jmp int_hw # V86 int 0xa
+ push $0xb # Int 0x23: IRQ3
+ jmp int_hw # V86 int 0xb
+ push $0xc # Int 0x24: IRQ4
+ jmp int_hw # V86 int 0xc
+ push $0xd # Int 0x25: IRQ5
+ jmp int_hw # V86 int 0xd
+ push $0xe # Int 0x26: IRQ6
+ jmp int_hw # V86 int 0xe
+ push $0xf # Int 0x27: IRQ7
+ jmp int_hw # V86 int 0xf
+ push $0x70 # Int 0x28: IRQ8
+ jmp int_hw # V86 int 0x70
+ push $0x71 # Int 0x29: IRQ9
+ jmp int_hw # V86 int 0x71
+ push $0x72 # Int 0x2a: IRQ10
+ jmp int_hw # V86 int 0x72
+ push $0x73 # Int 0x2b: IRQ11
+ jmp int_hw # V86 int 0x73
+ push $0x74 # Int 0x2c: IRQ12
+ jmp int_hw # V86 int 0x74
+ push $0x75 # Int 0x2d: IRQ13
+ jmp int_hw # V86 int 0x75
+ push $0x76 # Int 0x2e: IRQ14
+ jmp int_hw # V86 int 0x76
+ push $0x77 # Int 0x2f: IRQ15
+ jmp int_hw # V86 int 0x77
+#
+# Reflect hardware interrupts.
+#
+int_hw: testb $0x2,0xe(%esp,1) # V86 mode?
+ jz intusr # No
+ pushl $SEL_SDATA # Address
+ popl %ds # data
+ xchgl %eax,(%esp,1) # Swap EAX, int no
+ pushl %ebp # Address
+ movl %esp,%ebp # stack frame
+ pushl %ebx # Save
+ shll $0x2,%eax # Get int
+ movl (%eax),%eax # vector
+ subl $0x6,0x14(%ebp) # Adjust V86 ESP
+ movzwl 0x18(%ebp),%ebx # V86 SS
+ shll $0x4,%ebx # * 0x10
+ addl 0x14(%ebp),%ebx # + V86 ESP
+ xchgw %ax,0x8(%ebp) # Swap V86 IP
+ rorl $0x10,%eax # Swap words
+ xchgw %ax,0xc(%ebp) # Swap V86 CS
+ roll $0x10,%eax # Swap words
+ movl %eax,(%ebx) # CS:IP for IRET
+ movl 0x10(%ebp),%eax # V86 flags
+ movw %ax,0x4(%ebx) # Flags for IRET
+ andb $~0x3,0x11(%ebp) # Clear IF, TF
+ popl %ebx # Restore
+ popl %ebp # saved
+ popl %eax # registers
+ iret # To V86 mode
+#
+# Invoke V86 interrupt from user mode, with arguments.
+#
+intx31: stc # Have btx_v86
+ pushl %eax # Missing int no
+#
+# Invoke V86 interrupt from user mode.
+#
+intusr: std # String ops dec
+ pushl %eax # Expand
+ pushl %eax # stack
+ pushl %eax # frame
+ pusha # Save
+ pushl %gs # Save
+ movl %esp,%eax # seg regs
+ pushl %fs # and
+ pushl %ds # point
+ pushl %es # to them
+ push $SEL_SDATA # Set up
+ popl %ds # to
+ pushl %ds # address
+ popl %es # data
+ movl $MEM_USR,%ebx # User base
+ movl %ebx,%edx # address
+ jc intusr.1 # If btx_v86
+ xorl %edx,%edx # Control flags
+ xorl %ebp,%ebp # btx_v86 pointer
+intusr.1: leal 0x50(%esp,1),%esi # Base of frame
+ pushl %esi # Save
+ addl -0x4(%esi),%ebx # User ESP
+ movl MEM_TSS+TSS_ESP1,%edi # Link stack pointer
+ leal -0x4(%edi),%edi # Adjust for push
+ xorl %ecx,%ecx # Zero
+ movb $0x5,%cl # Push exception
+ rep # frame on
+ movsl # link stack
+ xchgl %eax,%esi # Saved seg regs
+ movl 0x40(%esp,1),%eax # Get int no
+ testl %edx,%edx # Have btx_v86?
+ jz intusr.2 # No
+ movl (%ebx),%ebp # btx_v86 pointer
+ movb $0x4,%cl # Count
+ addl %ecx,%ebx # Adjust for pop
+ rep # Push saved seg regs
+ movsl # on link stack
+ addl %ebp,%edx # Flatten btx_v86 ptr
+ leal 0x14(%edx),%esi # Seg regs pointer
+ movl 0x4(%edx),%eax # Get int no/address
+ movzwl 0x2(%edx),%edx # Get control flags
+intusr.2: movl %ebp,(%edi) # Push btx_v86 and
+ movl %edi,MEM_TSS+TSS_ESP1 # save link stack ptr
+ popl %edi # Base of frame
+ xchgl %eax,%ebp # Save intno/address
+ movl 0x48(%esp,1),%eax # Get flags
+ testb $0x2,%dl # Simulate CALLF?
+ jnz intusr.3 # Yes
+ decl %ebx # Push flags
+ decl %ebx # on V86
+ movw %ax,(%ebx) # stack
+intusr.3: movb $0x4,%cl # Count
+ subl %ecx,%ebx # Push return address
+ movl $inthlt,(%ebx) # on V86 stack
+ rep # Copy seg regs to
+ movsl # exception frame
+ xchgl %eax,%ecx # Save flags
+ movl %ebx,%eax # User ESP
+ subl $V86_STK,%eax # Less bytes
+ ja intusr.4 # to
+ xorl %eax,%eax # keep
+intusr.4: shrl $0x4,%eax # Gives segment
+ stosl # Set SS
+ shll $0x4,%eax # To bytes
+ xchgl %eax,%ebx # Swap
+ subl %ebx,%eax # Gives offset
+ stosl # Set ESP
+ xchgl %eax,%ecx # Get flags
+ btsl $0x11,%eax # Set VM
+ andb $~0x1,%ah # Clear TF
+ stosl # Set EFL
+ xchgl %eax,%ebp # Get int no/address
+ testb $0x1,%dl # Address?
+ jnz intusr.5 # Yes
+ shll $0x2,%eax # Scale
+ movl (%eax),%eax # Load int vector
+intusr.5: movl %eax,%ecx # Save
+ shrl $0x10,%eax # Gives segment
+ stosl # Set CS
+ movw %cx,%ax # Restore
+ stosl # Set EIP
+ leal 0x10(%esp,1),%esp # Discard seg regs
+ popa # Restore
+ iret # To V86 mode
+#
+# System Call.
+#
+intx30: cmpl $SYS_EXEC,%eax # Exec system call?
+ jne intx30.1 # No
+ pushl %ss # Set up
+ popl %es # all
+ pushl %es # segment
+ popl %ds # registers
+ pushl %ds # for the
+ popl %fs # program
+ pushl %fs # we're
+ popl %gs # invoking
+ movl $MEM_USR,%eax # User base address
+ addl 0xc(%esp,1),%eax # Change to user
+ leal 0x4(%eax),%esp # stack
+ifdef(`PAGING',`
+ movl %cr0,%eax # Turn
+ andl $~0x80000000,%eax # off
+ movl %eax,%cr0 # paging
+ xorl %eax,%eax # Flush
+ movl %eax,%cr3 # TLB
+')
+ popl %eax # Call
+ call *%eax # program
+intx30.1: orb $0x1,%ss:btx_hdr+0x7 # Flag reboot
+ jmp exit # Exit
+#
+# Dump structure [EBX] to [EDI], using format string [ESI].
+#
+dump.0: stosb # Save char
+dump: lodsb # Load char
+ testb %al,%al # End of string?
+ jz dump.10 # Yes
+ testb $0x80,%al # Control?
+ jz dump.0 # No
+ movb %al,%ch # Save control
+ movb $'=',%al # Append
+ stosb # '='
+ lodsb # Get offset
+ pushl %esi # Save
+ movsbl %al,%esi # To
+ addl %ebx,%esi # pointer
+ testb $DMP_X16,%ch # Dump word?
+ jz dump.1 # No
+ lodsw # Get and
+ call hex16 # dump it
+dump.1: testb $DMP_X32,%ch # Dump long?
+ jz dump.2 # No
+ lodsl # Get and
+ call hex32 # dump it
+dump.2: testb $DMP_MEM,%ch # Dump memory?
+ jz dump.8 # No
+ pushl %ds # Save
+ testb $0x2,0x52(%ebx) # V86 mode?
+ jnz dump.3 # Yes
+ verr 0x4(%esi) # Readable selector?
+ jnz dump.3 # No
+ ldsl (%esi),%esi # Load pointer
+ jmp dump.4 # Join common code
+dump.3: lodsl # Set offset
+ xchgl %eax,%edx # Save
+ lodsl # Get segment
+ shll $0x4,%eax # * 0x10
+ addl %edx,%eax # + offset
+ xchgl %eax,%esi # Set pointer
+dump.4: movb $2,%dl # Num lines
+dump.4a: movb $0x10,%cl # Bytes to dump
+dump.5: lodsb # Get byte and
+ call hex8 # dump it
+ decb %cl # Keep count
+ jz dump.6a # If done
+ movb $'-',%al # Separator
+ cmpb $0x8,%cl # Half way?
+ je dump.6 # Yes
+ movb $' ',%al # Use space
+dump.6: stosb # Save separator
+ jmp dump.5 # Continue
+dump.6a: decb %dl # Keep count
+ jz dump.7 # If done
+ movb $0xa,%al # Line feed
+ stosb # Save one
+ movb $7,%cl # Leading
+ movb $' ',%al # spaces
+dump.6b: stosb # Dump
+ decb %cl # spaces
+ jnz dump.6b
+ jmp dump.4a # Next line
+dump.7: popl %ds # Restore
+dump.8: popl %esi # Restore
+ movb $0xa,%al # Line feed
+ testb $DMP_EOL,%ch # End of line?
+ jnz dump.9 # Yes
+ movb $' ',%al # Use spaces
+ stosb # Save one
+dump.9: jmp dump.0 # Continue
+dump.10: stosb # Terminate string
+ 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)
+#
+# Output zero-terminated string [ESI] to the console.
+#
+putstr.0: call putchr # Output char
+putstr: lodsb # Load char
+ testb %al,%al # End of string?
+ jnz putstr.0 # No
+ ret # To caller
+ifdef(`BTX_SERIAL',`
+ .set SIO_PRT,SIOPRT # Base port
+ .set SIO_FMT,SIOFMT # 8N1
+ .set SIO_DIV,(115200/SIOSPD) # 115200 / SPD
+
+# void sio_init(void)
+
+sio_init: movw $SIO_PRT+0x3,%dx # Data format reg
+ movb $SIO_FMT|0x80,%al # Set format
+ outb %al,(%dx) # and DLAB
+ pushl %edx # Save
+ subb $0x3,%dl # Divisor latch reg
+ movw $SIO_DIV,%ax # Set
+ outw %ax,(%dx) # BPS
+ popl %edx # Restore
+ movb $SIO_FMT,%al # Clear
+ outb %al,(%dx) # DLAB
+ incl %edx # Modem control reg
+ movb $0x3,%al # Set RTS,
+ outb %al,(%dx) # DTR
+ incl %edx # Line status reg
+
+# void sio_flush(void)
+
+sio_flush.0: call sio_getc.1 # Get character
+sio_flush: call sio_ischar # Check for character
+ jnz sio_flush.0 # Till none
+ ret # To caller
+
+# void sio_putc(int c)
+
+sio_putc: movw $SIO_PRT+0x5,%dx # Line status reg
+ xor %ecx,%ecx # Timeout
+ movb $0x40,%ch # counter
+sio_putc.1: inb (%dx),%al # Transmitter
+ testb $0x20,%al # buffer empty?
+ loopz sio_putc.1 # No
+ jz sio_putc.2 # If timeout
+ movb 0x4(%esp,1),%al # Get character
+ subb $0x5,%dl # Transmitter hold reg
+ outb %al,(%dx) # Write character
+sio_putc.2: ret $0x4 # To caller
+
+# int sio_getc(void)
+
+sio_getc: call sio_ischar # Character available?
+ jz sio_getc # No
+sio_getc.1: subb $0x5,%dl # Receiver buffer reg
+ inb (%dx),%al # Read character
+ ret # To caller
+
+# int sio_ischar(void)
+
+sio_ischar: movw $SIO_PRT+0x5,%dx # Line status register
+ xorl %eax,%eax # Zero
+ inb (%dx),%al # Received data
+ andb $0x1,%al # ready?
+ ret # To caller
+
+#
+# Output character AL to the serial console.
+#
+putchr: pusha # Save
+ cmpb $10, %al # is it a newline?
+ jne putchr.1 # no?, then leave
+ push $13 # output a carriage
+ call sio_putc # return first
+ movb $10, %al # restore %al
+putchr.1: pushl %eax # Push the character
+ # onto the stack
+ call sio_putc # Output the character
+ popa # Restore
+ ret # To caller
+',`
+#
+# Output character AL to the console.
+#
+putchr: pusha # Save
+ xorl %ecx,%ecx # Zero for loops
+ movb $SCR_MAT,%ah # Mode/attribute
+ movl $BDA_POS,%ebx # BDA pointer
+ movw (%ebx),%dx # Cursor position
+ movl $0xb8000,%edi # Regen buffer (color)
+ cmpb %ah,BDA_SCR-BDA_POS(%ebx) # Mono mode?
+ jne putchr.1 # No
+ xorw %di,%di # Regen buffer (mono)
+putchr.1: cmpb $0xa,%al # New line?
+ je putchr.2 # Yes
+ xchgl %eax,%ecx # Save char
+ movb $SCR_COL,%al # Columns per row
+ mulb %dh # * row position
+ addb %dl,%al # + column
+ adcb $0x0,%ah # position
+ shll %eax # * 2
+ xchgl %eax,%ecx # Swap char, offset
+ movw %ax,(%edi,%ecx,1) # Write attr:char
+ incl %edx # Bump cursor
+ cmpb $SCR_COL,%dl # Beyond row?
+ jb putchr.3 # No
+putchr.2: xorb %dl,%dl # Zero column
+ incb %dh # Bump row
+putchr.3: cmpb $SCR_ROW,%dh # Beyond screen?
+ jb putchr.4 # No
+ leal 2*SCR_COL(%edi),%esi # New top line
+ movw $(SCR_ROW-1)*SCR_COL/2,%cx # Words to move
+ rep # Scroll
+ movsl # screen
+ movb $0x20,%al # Space
+ movb $SCR_COL,%cl # Columns to clear
+ rep # Clear
+ stosw # line
+ movb $SCR_ROW-1,%dh # Bottom line
+putchr.4: movw %dx,(%ebx) # Update position
+ popa # Restore
+ ret # To caller
+')
+
+ .p2align 4
+#
+# Global descriptor table.
+#
+gdt: .word 0x0,0x0,0x0,0x0 # Null entry
+ .word 0xffff,0x0,0x9a00,0xcf # SEL_SCODE
+ .word 0xffff,0x0,0x9200,0xcf # SEL_SDATA
+ .word 0xffff,0x0,0x9a00,0x0 # SEL_RCODE
+ .word 0xffff,0x0,0x9200,0x0 # SEL_RDATA
+ .word 0xffff,MEM_USR,0xfa00,0xcf# SEL_UCODE
+ .word 0xffff,MEM_USR,0xf200,0xcf# SEL_UDATA
+ .word _TSSLM,MEM_TSS,0x8900,0x0 # SEL_TSS
+gdt.1:
+#
+# Pseudo-descriptors.
+#
+gdtdesc: .word gdt.1-gdt-1,gdt,0x0 # GDT
+idtdesc: .word _IDTLM,MEM_IDT,0x0 # IDT
+ivtdesc: .word 0x400-0x0-1,0x0,0x0 # IVT
+#
+# IDT construction control string.
+#
+idtctl: .byte 0x10, 0x8e # Int 0x0-0xf
+ .word 0x7dfb,intx00 # (exceptions)
+ .byte 0x10, 0x8e # Int 0x10
+ .word 0x1, intx10 # (exception)
+ .byte 0x10, 0x8e # Int 0x20-0x2f
+ .word 0xffff,intx20 # (hardware)
+ .byte 0x1, 0xee # int 0x30
+ .word 0x1, intx30 # (system call)
+ .byte 0x2, 0xee # Int 0x31-0x32
+ .word 0x1, intx31 # (V86, null)
+ .byte 0x0 # End of string
+#
+# Dump format string.
+#
+dmpfmt: .byte '\n' # "\n"
+ .ascii "int" # "int="
+ .byte 0x80|DMP_X32, 0x40 # "00000000 "
+ .ascii "err" # "err="
+ .byte 0x80|DMP_X32, 0x44 # "00000000 "
+ .ascii "efl" # "efl="
+ .byte 0x80|DMP_X32, 0x50 # "00000000 "
+ .ascii "eip" # "eip="
+ .byte 0x80|DMP_X32|DMP_EOL,0x48 # "00000000\n"
+ .ascii "eax" # "eax="
+ .byte 0x80|DMP_X32, 0x34 # "00000000 "
+ .ascii "ebx" # "ebx="
+ .byte 0x80|DMP_X32, 0x28 # "00000000 "
+ .ascii "ecx" # "ecx="
+ .byte 0x80|DMP_X32, 0x30 # "00000000 "
+ .ascii "edx" # "edx="
+ .byte 0x80|DMP_X32|DMP_EOL,0x2c # "00000000\n"
+ .ascii "esi" # "esi="
+ .byte 0x80|DMP_X32, 0x1c # "00000000 "
+ .ascii "edi" # "edi="
+ .byte 0x80|DMP_X32, 0x18 # "00000000 "
+ .ascii "ebp" # "ebp="
+ .byte 0x80|DMP_X32, 0x20 # "00000000 "
+ .ascii "esp" # "esp="
+ .byte 0x80|DMP_X32|DMP_EOL,0x0 # "00000000\n"
+ .ascii "cs" # "cs="
+ .byte 0x80|DMP_X16, 0x4c # "0000 "
+ .ascii "ds" # "ds="
+ .byte 0x80|DMP_X16, 0xc # "0000 "
+ .ascii "es" # "es="
+ .byte 0x80|DMP_X16, 0x8 # "0000 "
+ .ascii " " # " "
+ .ascii "fs" # "fs="
+ .byte 0x80|DMP_X16, 0x10 # "0000 "
+ .ascii "gs" # "gs="
+ .byte 0x80|DMP_X16, 0x14 # "0000 "
+ .ascii "ss" # "ss="
+ .byte 0x80|DMP_X16|DMP_EOL,0x4 # "0000\n"
+ .ascii "cs:eip" # "cs:eip="
+ .byte 0x80|DMP_MEM|DMP_EOL,0x48 # "00 00 ... 00 00\n"
+ .ascii "ss:esp" # "ss:esp="
+ .byte 0x80|DMP_MEM|DMP_EOL,0x0 # "00 00 ... 00 00\n"
+ .asciz "BTX halted\n" # End
+#
+# End of BTX memory.
+#
+ .p2align 4
+break:
diff --git a/sys/boot/i386/btx/btx/btx.s b/sys/boot/i386/btx/btx/btx.s
new file mode 100644
index 0000000..0215530
--- /dev/null
+++ b/sys/boot/i386/btx/btx/btx.s
@@ -0,0 +1,1173 @@
+#
+# Copyright (c) 1998 Robert Nordier
+# All rights reserved.
+#
+# Redistribution and use in source and binary forms are freely
+# permitted provided that the above copyright notice and this
+# paragraph and the following disclaimer are duplicated in all
+# such forms.
+#
+# This software is provided "AS IS" and without any express or
+# implied warranties, including, without limitation, the implied
+# warranties of merchantability and fitness for a particular
+# purpose.
+#
+
+# $FreeBSD$
+
+#
+# Memory layout.
+#
+ .set MEM_BTX,0x1000 # Start of BTX memory
+ .set MEM_ESP0,0x1800 # Supervisor stack
+ .set MEM_BUF,0x1800 # Scratch buffer
+ .set MEM_ESP1,0x1e00 # Link stack
+ .set MEM_IDT,0x1e00 # IDT
+ .set MEM_TSS,0x1f98 # TSS
+ .set MEM_MAP,0x2000 # I/O bit map
+ .set MEM_DIR,0x4000 # Page directory
+ .set MEM_TBL,0x5000 # Page tables
+ .set MEM_ORG,0x9000 # BTX code
+ .set MEM_USR,0xa000 # Start of user memory
+#
+# Paging control.
+#
+ .set PAG_SIZ,0x1000 # Page size
+ .set PAG_CNT,0x1000 # Pages to map
+#
+# Segment selectors.
+#
+ .set SEL_SCODE,0x8 # Supervisor code
+ .set SEL_SDATA,0x10 # Supervisor data
+ .set SEL_RCODE,0x18 # Real mode code
+ .set SEL_RDATA,0x20 # Real mode data
+ .set SEL_UCODE,0x28|3 # User code
+ .set SEL_UDATA,0x30|3 # User data
+ .set SEL_TSS,0x38 # TSS
+#
+# Task state segment fields.
+#
+ .set TSS_ESP0,0x4 # PL 0 ESP
+ .set TSS_SS0,0x8 # PL 0 SS
+ .set TSS_ESP1,0xc # PL 1 ESP
+ .set TSS_MAP,0x66 # I/O bit map base
+#
+# System calls.
+#
+ .set SYS_EXIT,0x0 # Exit
+ .set SYS_EXEC,0x1 # Exec
+#
+# V86 constants.
+#
+ .set V86_FLG,0x208eff # V86 flag mask
+ .set V86_STK,0x400 # V86 stack allowance
+#
+# Dump format control bytes.
+#
+ .set DMP_X16,0x1 # Word
+ .set DMP_X32,0x2 # Long
+ .set DMP_MEM,0x4 # Memory
+ .set DMP_EOL,0x8 # End of line
+#
+# Screen defaults and assumptions.
+#
+ .set SCR_MAT,0x7 # Mode/attribute
+ .set SCR_COL,0x50 # Columns per row
+ .set SCR_ROW,0x19 # Rows per screen
+#
+# BIOS Data Area locations.
+#
+ .set BDA_MEM,0x413 # Free memory
+ .set BDA_KEYFLAGS,0x417 # Keyboard shift-state flags
+ .set BDA_SCR,0x449 # Video mode
+ .set BDA_POS,0x450 # Cursor position
+ .set BDA_BOOT,0x472 # Boot howto flag
+#
+# Derivations, for brevity.
+#
+ .set _ESP0H,MEM_ESP0>>0x8 # Byte 1 of ESP0
+ .set _ESP1H,MEM_ESP1>>0x8 # Byte 1 of ESP1
+ .set _TSSIO,MEM_MAP-MEM_TSS # TSS I/O base
+ .set _TSSLM,MEM_DIR-MEM_TSS-1 # TSS limit
+ .set _IDTLM,MEM_TSS-MEM_IDT-1 # IDT limit
+#
+# Code segment.
+#
+ .globl start
+ .code16
+start: # Start of code
+#
+# BTX header.
+#
+btx_hdr: .byte 0xeb # Machine ID
+ .byte 0xe # Header size
+ .ascii "BTX" # Magic
+ .byte 0x1 # Major version
+ .byte 0x1 # Minor version
+ .byte BTX_FLAGS # Flags
+ .word PAG_CNT-MEM_ORG>>0xc # Paging control
+ .word break-start # Text size
+ .long 0x0 # Entry address
+#
+# Initialization routine.
+#
+init: cli # Disable interrupts
+ xor %ax,%ax # Zero/segment
+ mov %ax,%ss # Set up
+ mov $MEM_ESP0,%sp # stack
+ mov %ax,%es # Address
+ mov %ax,%ds # data
+ pushl $0x2 # Clear
+ popfl # flags
+#
+# Initialize memory.
+#
+ mov $MEM_IDT,%di # Memory to initialize
+ mov $(MEM_ORG-MEM_IDT)/2,%cx # Words to zero
+ push %di # Save
+ rep # Zero-fill
+ stosw # memory
+ pop %di # Restore
+#
+# Create IDT.
+#
+ mov $idtctl,%si # Control string
+init.1: lodsb # Get entry
+ cbw # count
+ xchg %ax,%cx # as word
+ jcxz init.4 # If done
+ lodsb # Get segment
+ xchg %ax,%dx # P:DPL:type
+ lodsw # Get control
+ xchg %ax,%bx # set
+ lodsw # Get handler offset
+ mov $SEL_SCODE,%dh # Segment selector
+init.2: shr %bx # Handle this int?
+ jnc init.3 # No
+ mov %ax,(%di) # Set handler offset
+ mov %dh,0x2(%di) # and selector
+ mov %dl,0x5(%di) # Set P:DPL:type
+ add $0x4,%ax # Next handler
+init.3: lea 0x8(%di),%di # Next entry
+ loop init.2 # Till set done
+ jmp init.1 # Continue
+#
+# Initialize TSS.
+#
+init.4: movb $_ESP0H,TSS_ESP0+1(%di) # Set ESP0
+ movb $SEL_SDATA,TSS_SS0(%di) # Set SS0
+ movb $_ESP1H,TSS_ESP1+1(%di) # Set ESP1
+ movb $_TSSIO,TSS_MAP(%di) # Set I/O bit map base
+ifdef(`PAGING',`
+#
+# Create page directory.
+#
+ xor %edx,%edx # Page
+ mov $PAG_SIZ>>0x8,%dh # size
+ xor %eax,%eax # Zero
+ mov $MEM_DIR,%di # Page directory
+ mov $PAG_CNT>>0xa,%cl # Entries
+ mov $MEM_TBL|0x7,%ax # First entry
+init.5: stosl # Write entry
+ add %dx,%ax # To next
+ loop init.5 # Till done
+#
+# Create page tables.
+#
+ mov $MEM_TBL,%di # Page table
+ mov $PAG_CNT>>0x8,%ch # Entries
+ xor %ax,%ax # Start address
+init.6: mov $0x7,%al # Set U:W:P flags
+ cmp btx_hdr+0x8,%cx # Standard user page?
+ jb init.7 # Yes
+ cmp $PAG_CNT-MEM_BTX>>0xc,%cx # BTX memory?
+ jae init.7 # No or first page
+ and $~0x2,%al # Clear W flag
+ cmp $PAG_CNT-MEM_USR>>0xc,%cx # User page zero?
+ jne init.7 # No
+ testb $0x80,btx_hdr+0x7 # Unmap it?
+ jz init.7 # No
+ and $~0x1,%al # Clear P flag
+init.7: stosl # Set entry
+ add %edx,%eax # Next address
+ loop init.6 # Till done
+')
+#
+# Bring up the system.
+#
+ mov $0x2820,%bx # Set protected mode
+ callw setpic # IRQ offsets
+ lidt idtdesc # Set IDT
+ifdef(`PAGING',`
+ xor %eax,%eax # Set base
+ mov $MEM_DIR>>0x8,%ah # of page
+ mov %eax,%cr3 # directory
+')
+ lgdt gdtdesc # Set GDT
+ mov %cr0,%eax # Switch to protected
+ifdef(`PAGING',`
+ or $0x80000001,%eax # mode and enable paging
+',`
+ or $0x01,%eax # mode
+')
+ mov %eax,%cr0 #
+ ljmp $SEL_SCODE,$init.8 # To 32-bit code
+ .code32
+init.8: xorl %ecx,%ecx # Zero
+ movb $SEL_SDATA,%cl # To 32-bit
+ movw %cx,%ss # stack
+#
+# Launch user task.
+#
+ movb $SEL_TSS,%cl # Set task
+ ltr %cx # register
+ movl $MEM_USR,%edx # User base address
+ movzwl %ss:BDA_MEM,%eax # Get free memory
+ shll $0xa,%eax # To bytes
+ subl $0x1000,%eax # Less arg space
+ subl %edx,%eax # Less base
+ movb $SEL_UDATA,%cl # User data selector
+ pushl %ecx # Set SS
+ pushl %eax # Set ESP
+ push $0x202 # Set flags (IF set)
+ push $SEL_UCODE # Set CS
+ pushl btx_hdr+0xc # Set EIP
+ pushl %ecx # Set GS
+ pushl %ecx # Set FS
+ pushl %ecx # Set DS
+ pushl %ecx # Set ES
+ pushl %edx # Set EAX
+ movb $0x7,%cl # Set remaining
+init.9: push $0x0 # general
+ loop init.9 # registers
+ifdef(`BTX_SERIAL',`
+ call sio_init # setup the serial console
+')
+ popa # and initialize
+ popl %es # Initialize
+ popl %ds # user
+ popl %fs # segment
+ popl %gs # registers
+ iret # To user mode
+#
+# Exit routine.
+#
+exit: cli # Disable interrupts
+ movl $MEM_ESP0,%esp # Clear stack
+#
+# Turn off paging.
+#
+ movl %cr0,%eax # Get CR0
+ andl $~0x80000000,%eax # Disable
+ movl %eax,%cr0 # paging
+ xorl %ecx,%ecx # Zero
+ movl %ecx,%cr3 # Flush TLB
+#
+# Restore the GDT in case we caught a kernel trap.
+#
+ lgdt gdtdesc # Set GDT
+#
+# To 16 bits.
+#
+ ljmpw $SEL_RCODE,$exit.1 # Reload CS
+ .code16
+exit.1: mov $SEL_RDATA,%cl # 16-bit selector
+ mov %cx,%ss # Reload SS
+ mov %cx,%ds # Load
+ mov %cx,%es # remaining
+ mov %cx,%fs # segment
+ mov %cx,%gs # registers
+#
+# To real-address mode.
+#
+ dec %ax # Switch to
+ mov %eax,%cr0 # real mode
+ ljmp $0x0,$exit.2 # Reload CS
+exit.2: xor %ax,%ax # Real mode segment
+ mov %ax,%ss # Reload SS
+ mov %ax,%ds # Address data
+ mov $0x7008,%bx # Set real mode
+ callw setpic # IRQ offsets
+ lidt ivtdesc # Set IVT
+#
+# Reboot or await reset.
+#
+ sti # Enable interrupts
+ testb $0x1,btx_hdr+0x7 # Reboot?
+exit.3: jz exit.3 # No
+ movw $0x1234, BDA_BOOT # Do a warm boot
+ ljmp $0xf000,$0xfff0 # reboot the machine
+#
+# Set IRQ offsets by reprogramming 8259A PICs.
+#
+setpic: in $0x21,%al # Save master
+ push %ax # IMR
+ in $0xa1,%al # Save slave
+ push %ax # IMR
+ movb $0x11,%al # ICW1 to
+ outb %al,$0x20 # master,
+ outb %al,$0xa0 # slave
+ movb %bl,%al # ICW2 to
+ outb %al,$0x21 # master
+ movb %bh,%al # ICW2 to
+ outb %al,$0xa1 # slave
+ movb $0x4,%al # ICW3 to
+ outb %al,$0x21 # master
+ movb $0x2,%al # ICW3 to
+ outb %al,$0xa1 # slave
+ movb $0x1,%al # ICW4 to
+ outb %al,$0x21 # master,
+ outb %al,$0xa1 # slave
+ pop %ax # Restore slave
+ outb %al,$0xa1 # IMR
+ pop %ax # Restore master
+ outb %al,$0x21 # IMR
+ retw # To caller
+ .code32
+#
+# Initiate return from V86 mode to user mode.
+#
+inthlt: hlt # To supervisor mode
+#
+# Exception jump table.
+#
+intx00: push $0x0 # Int 0x0: #DE
+ jmp ex_noc # Divide error
+ push $0x1 # Int 0x1: #DB
+ jmp ex_noc # Debug
+ push $0x3 # Int 0x3: #BP
+ jmp ex_noc # Breakpoint
+ push $0x4 # Int 0x4: #OF
+ jmp ex_noc # Overflow
+ push $0x5 # Int 0x5: #BR
+ jmp ex_noc # BOUND range exceeded
+ push $0x6 # Int 0x6: #UD
+ jmp ex_noc # Invalid opcode
+ push $0x7 # Int 0x7: #NM
+ jmp ex_noc # Device not available
+ push $0x8 # Int 0x8: #DF
+ jmp except # Double fault
+ push $0xa # Int 0xa: #TS
+ jmp except # Invalid TSS
+ push $0xb # Int 0xb: #NP
+ jmp except # Segment not present
+ push $0xc # Int 0xc: #SS
+ jmp except # Stack segment fault
+ push $0xd # Int 0xd: #GP
+ jmp ex_v86 # General protection
+ push $0xe # Int 0xe: #PF
+ jmp except # Page fault
+intx10: push $0x10 # Int 0x10: #MF
+ jmp ex_noc # Floating-point error
+#
+# Handle #GP exception.
+#
+ex_v86: testb $0x2,0x12(%esp,1) # V86 mode?
+ jz except # No
+ jmp v86mon # To monitor
+#
+# Save a zero error code.
+#
+ex_noc: pushl (%esp,1) # Duplicate int no
+ movb $0x0,0x4(%esp,1) # Fake error code
+#
+# Handle exception.
+#
+except: cld # String ops inc
+ pushl %ds # Save
+ pushl %es # most
+ pusha # registers
+ movb $0x6,%al # Push loop count
+ testb $0x2,0x3a(%esp,1) # V86 mode?
+ jnz except.1 # Yes
+ pushl %gs # Set GS
+ pushl %fs # Set FS
+ pushl %ds # Set DS
+ pushl %es # Set ES
+ movb $0x2,%al # Push loop count
+ cmpw $SEL_SCODE,0x44(%esp,1) # Supervisor mode?
+ jne except.1 # No
+ pushl %ss # Set SS
+ leal 0x50(%esp,1),%eax # Set
+ pushl %eax # ESP
+ jmp except.2 # Join common code
+except.1: pushl 0x50(%esp,1) # Set GS, FS, DS, ES
+ decb %al # (if V86 mode), and
+ jne except.1 # SS, ESP
+except.2: push $SEL_SDATA # Set up
+ popl %ds # to
+ pushl %ds # address
+ popl %es # data
+ movl %esp,%ebx # Stack frame
+ movl $dmpfmt,%esi # Dump format string
+ movl $MEM_BUF,%edi # Buffer
+ pushl %edi # Dump to
+ call dump # buffer
+ popl %esi # and
+ call putstr # display
+ leal 0x18(%esp,1),%esp # Discard frame
+ popa # Restore
+ popl %es # registers
+ popl %ds # saved
+ cmpb $0x3,(%esp,1) # Breakpoint?
+ je except.3 # Yes
+ cmpb $0x1,(%esp,1) # Debug?
+ jne except.2a # No
+ testl $0x100,0x10(%esp,1) # Trap flag set?
+ jnz except.3 # Yes
+except.2a: jmp exit # Exit
+except.3: leal 0x8(%esp,1),%esp # Discard err, int no
+ iret # From interrupt
+#
+# Return to user mode from V86 mode.
+#
+intrtn: cld # String ops inc
+ pushl %ds # Address
+ popl %es # data
+ leal 0x3c(%ebp),%edx # V86 Segment registers
+ movl MEM_TSS+TSS_ESP1,%esi # Link stack pointer
+ lodsl # INT_V86 args pointer
+ movl %esi,%ebx # Saved exception frame
+ testl %eax,%eax # INT_V86 args?
+ jz intrtn.2 # No
+ movl $MEM_USR,%edi # User base
+ movl 0x1c(%esi),%ebx # User ESP
+ movl %eax,(%edi,%ebx,1) # Restore to user stack
+ leal 0x8(%edi,%eax,1),%edi # Arg segment registers
+ testb $0x4,-0x6(%edi) # Return flags?
+ jz intrtn.1 # No
+ movl 0x30(%ebp),%eax # Get V86 flags
+ movw %ax,0x18(%esi) # Set user flags
+intrtn.1: leal 0x10(%esi),%ebx # Saved exception frame
+ xchgl %edx,%esi # Segment registers
+ movb $0x4,%cl # Update seg regs
+ rep # in INT_V86
+ movsl # args
+intrtn.2: movl %edx,%esi # Segment registers
+ leal 0x28(%ebp),%edi # Set up seg
+ movb $0x4,%cl # regs for
+ rep # later
+ movsl # pop
+ movl %ebx,%esi # Restore exception
+ movb $0x5,%cl # frame to
+ rep # supervisor
+ movsl # stack
+ movl %esi,MEM_TSS+TSS_ESP1 # Link stack pointer
+ popa # Restore
+ leal 0x8(%esp,1),%esp # Discard err, int no
+ popl %es # Restore
+ popl %ds # user
+ popl %fs # segment
+ popl %gs # registers
+ iret # To user mode
+#
+# V86 monitor.
+#
+v86mon: cld # String ops inc
+ pushl $SEL_SDATA # Set up for
+ popl %ds # flat addressing
+ pusha # Save registers
+ movl %esp,%ebp # Address stack frame
+ movzwl 0x2c(%ebp),%edi # Load V86 CS
+ shll $0x4,%edi # To linear
+ movl 0x28(%ebp),%esi # Load V86 IP
+ addl %edi,%esi # Code pointer
+ xorl %ecx,%ecx # Zero
+ movb $0x2,%cl # 16-bit operands
+ xorl %eax,%eax # Zero
+v86mon.1: lodsb # Get opcode
+ cmpb $0x66,%al # Operand size prefix?
+ jne v86mon.2 # No
+ movb $0x4,%cl # 32-bit operands
+ jmp v86mon.1 # Continue
+v86mon.2: cmpb $0xf4,%al # HLT?
+ jne v86mon.3 # No
+ cmpl $inthlt+0x1,%esi # Is inthlt?
+ jne v86mon.7 # No (ignore)
+ jmp intrtn # Return to user mode
+v86mon.3: cmpb $0xf,%al # Prefixed instruction?
+ jne v86mon.4 # No
+ cmpb $0x09,(%esi) # Is it a WBINVD?
+ je v86wbinvd # Yes
+ cmpb $0x30,(%esi) # Is it a WRMSR?
+ je v86wrmsr # Yes
+ cmpb $0x32,(%esi) # Is it a RDMSR?
+ je v86rdmsr # Yes
+ cmpb $0x20,(%esi) # Is this a
+ jne v86mon.4 # MOV EAX,CR0
+ cmpb $0xc0,0x1(%esi) # instruction?
+ je v86mov # Yes
+v86mon.4: cmpb $0xfa,%al # CLI?
+ je v86cli # Yes
+ cmpb $0xfb,%al # STI?
+ je v86sti # Yes
+ movzwl 0x38(%ebp),%ebx # Load V86 SS
+ shll $0x4,%ebx # To offset
+ pushl %ebx # Save
+ addl 0x34(%ebp),%ebx # Add V86 SP
+ movl 0x30(%ebp),%edx # Load V86 flags
+ cmpb $0x9c,%al # PUSHF/PUSHFD?
+ je v86pushf # Yes
+ cmpb $0x9d,%al # POPF/POPFD?
+ je v86popf # Yes
+ cmpb $0xcd,%al # INT imm8?
+ je v86intn # Yes
+ cmpb $0xcf,%al # IRET/IRETD?
+ je v86iret # Yes
+ popl %ebx # Restore
+ popa # Restore
+ jmp except # Handle exception
+v86mon.5: movl %edx,0x30(%ebp) # Save V86 flags
+v86mon.6: popl %edx # V86 SS adjustment
+ subl %edx,%ebx # Save V86
+ movl %ebx,0x34(%ebp) # SP
+v86mon.7: subl %edi,%esi # From linear
+ movl %esi,0x28(%ebp) # Save V86 IP
+ popa # Restore
+ leal 0x8(%esp,1),%esp # Discard int no, error
+ iret # To V86 mode
+#
+# Emulate MOV EAX,CR0.
+#
+v86mov: movl %cr0,%eax # CR0 to
+ movl %eax,0x1c(%ebp) # saved EAX
+ incl %esi # Adjust IP
+#
+# Return from emulating a 0x0f prefixed instruction
+#
+v86preret: incl %esi # Adjust IP
+ jmp v86mon.7 # Finish up
+#
+# Emulate WBINVD
+#
+v86wbinvd: wbinvd # Write back and invalidate
+ # cache
+ jmp v86preret # Finish up
+#
+# Emulate WRMSR
+#
+v86wrmsr: movl 0x18(%ebp),%ecx # Get user's %ecx (MSR to write)
+ movl 0x14(%ebp),%edx # Load the value
+ movl 0x1c(%ebp),%eax # to write
+ wrmsr # Write MSR
+ jmp v86preret # Finish up
+#
+# Emulate RDMSR
+#
+v86rdmsr: movl 0x18(%ebp),%ecx # MSR to read
+ rdmsr # Read the MSR
+ movl %eax,0x1c(%ebp) # Return the value of
+ movl %edx,0x14(%ebp) # the MSR to the user
+ jmp v86preret # Finish up
+#
+# Emulate CLI.
+#
+v86cli: andb $~0x2,0x31(%ebp) # Clear IF
+ jmp v86mon.7 # Finish up
+#
+# Emulate STI.
+#
+v86sti: orb $0x2,0x31(%ebp) # Set IF
+ jmp v86mon.7 # Finish up
+#
+# Emulate PUSHF/PUSHFD.
+#
+v86pushf: subl %ecx,%ebx # Adjust SP
+ cmpb $0x4,%cl # 32-bit
+ je v86pushf.1 # Yes
+ data16 # 16-bit
+v86pushf.1: movl %edx,(%ebx) # Save flags
+ jmp v86mon.6 # Finish up
+#
+# Emulate IRET/IRETD.
+#
+v86iret: movzwl (%ebx),%esi # Load V86 IP
+ movzwl 0x2(%ebx),%edi # Load V86 CS
+ leal 0x4(%ebx),%ebx # Adjust SP
+ movl %edi,0x2c(%ebp) # Save V86 CS
+ xorl %edi,%edi # No ESI adjustment
+#
+# Emulate POPF/POPFD (and remainder of IRET/IRETD).
+#
+v86popf: cmpb $0x4,%cl # 32-bit?
+ je v86popf.1 # Yes
+ movl %edx,%eax # Initialize
+ data16 # 16-bit
+v86popf.1: movl (%ebx),%eax # Load flags
+ addl %ecx,%ebx # Adjust SP
+ andl $V86_FLG,%eax # Merge
+ andl $~V86_FLG,%edx # the
+ orl %eax,%edx # flags
+ jmp v86mon.5 # Finish up
+#
+# trap int 15, function 87
+# reads %es:%si from saved registers on stack to find a GDT containing
+# source and destination locations
+# reads count of words from saved %cx
+# returns success by setting %ah to 0
+#
+int15_87: pushl %eax # Save
+ pushl %ebx # some information
+ pushl %esi # onto the stack.
+ pushl %edi
+ xorl %eax,%eax # clean EAX
+ xorl %ebx,%ebx # clean EBX
+ movl 0x4(%ebp),%esi # Get user's ESI
+ movl 0x3C(%ebp),%ebx # store ES
+ movw %si,%ax # store SI
+ shll $0x4,%ebx # Make it a seg.
+ addl %eax,%ebx # ebx=(es<<4)+si
+ movb 0x14(%ebx),%al # Grab the
+ movb 0x17(%ebx),%ah # necessary
+ shll $0x10,%eax # information
+ movw 0x12(%ebx),%ax # from
+ movl %eax,%esi # the
+ movb 0x1c(%ebx),%al # GDT in order to
+ movb 0x1f(%ebx),%ah # have %esi offset
+ shll $0x10,%eax # of source and %edi
+ movw 0x1a(%ebx),%ax # of destination.
+ movl %eax,%edi
+ pushl %ds # Make:
+ popl %es # es = ds
+ pushl %ecx # stash ECX
+ xorl %ecx,%ecx # highw of ECX is clear
+ movw 0x18(%ebp),%cx # Get user's ECX
+ shll $0x1,%ecx # Convert from num words to num
+ # bytes
+ rep # repeat...
+ movsb # perform copy.
+ popl %ecx # Restore
+ popl %edi
+ popl %esi # previous
+ popl %ebx # register
+ popl %eax # values.
+ movb $0x0,0x1d(%ebp) # set ah = 0 to indicate
+ # success
+ andb $0xfe,%dl # clear CF
+ jmp v86mon.5 # Finish up
+
+#
+# Reboot the machine by setting the reboot flag and exiting
+#
+reboot: orb $0x1,btx_hdr+0x7 # Set the reboot flag
+ jmp exit # Terminate BTX and reboot
+
+#
+# Emulate INT imm8... also make sure to check if it's int 15/87
+#
+v86intn: lodsb # Get int no
+ cmpb $0x19,%al # is it int 19?
+ je reboot # yes, reboot the machine
+ cmpb $0x15,%al # is it int 15?
+ jne v86intn.3 # no, skip parse
+ pushl %eax # stash EAX
+ movl 0x1c(%ebp),%eax # user's saved EAX
+ cmpb $0x87,%ah # is it the memcpy subfunction?
+ jne v86intn.1 # no, keep checking
+ popl %eax # get the stack straight
+ jmp int15_87 # it's our cue
+v86intn.1: cmpw $0x4f53,%ax # is it the delete key callout?
+ jne v86intn.2 # no, handle the int normally
+ movb BDA_KEYFLAGS,%al # get the shift key state
+ andb $0xc,%al # mask off just Ctrl and Alt
+ cmpb $0xc,%al # are both Ctrl and Alt down?
+ jne v86intn.2 # no, handle the int normally
+ popl %eax # restore EAX
+ jmp reboot # reboot the machine
+v86intn.2: popl %eax # restore EAX
+v86intn.3: subl %edi,%esi # From
+ shrl $0x4,%edi # linear
+ movw %dx,-0x2(%ebx) # Save flags
+ movw %di,-0x4(%ebx) # Save CS
+ leal -0x6(%ebx),%ebx # Adjust SP
+ movw %si,(%ebx) # Save IP
+ shll $0x2,%eax # Scale
+ movzwl (%eax),%esi # Load IP
+ movzwl 0x2(%eax),%edi # Load CS
+ movl %edi,0x2c(%ebp) # Save CS
+ xorl %edi,%edi # No ESI adjustment
+ andb $~0x1,%dh # Clear TF
+ jmp v86mon.5 # Finish up
+#
+# Hardware interrupt jump table.
+#
+intx20: push $0x8 # Int 0x20: IRQ0
+ jmp int_hw # V86 int 0x8
+ push $0x9 # Int 0x21: IRQ1
+ jmp int_hw # V86 int 0x9
+ push $0xa # Int 0x22: IRQ2
+ jmp int_hw # V86 int 0xa
+ push $0xb # Int 0x23: IRQ3
+ jmp int_hw # V86 int 0xb
+ push $0xc # Int 0x24: IRQ4
+ jmp int_hw # V86 int 0xc
+ push $0xd # Int 0x25: IRQ5
+ jmp int_hw # V86 int 0xd
+ push $0xe # Int 0x26: IRQ6
+ jmp int_hw # V86 int 0xe
+ push $0xf # Int 0x27: IRQ7
+ jmp int_hw # V86 int 0xf
+ push $0x70 # Int 0x28: IRQ8
+ jmp int_hw # V86 int 0x70
+ push $0x71 # Int 0x29: IRQ9
+ jmp int_hw # V86 int 0x71
+ push $0x72 # Int 0x2a: IRQ10
+ jmp int_hw # V86 int 0x72
+ push $0x73 # Int 0x2b: IRQ11
+ jmp int_hw # V86 int 0x73
+ push $0x74 # Int 0x2c: IRQ12
+ jmp int_hw # V86 int 0x74
+ push $0x75 # Int 0x2d: IRQ13
+ jmp int_hw # V86 int 0x75
+ push $0x76 # Int 0x2e: IRQ14
+ jmp int_hw # V86 int 0x76
+ push $0x77 # Int 0x2f: IRQ15
+ jmp int_hw # V86 int 0x77
+#
+# Reflect hardware interrupts.
+#
+int_hw: testb $0x2,0xe(%esp,1) # V86 mode?
+ jz intusr # No
+ pushl $SEL_SDATA # Address
+ popl %ds # data
+ xchgl %eax,(%esp,1) # Swap EAX, int no
+ pushl %ebp # Address
+ movl %esp,%ebp # stack frame
+ pushl %ebx # Save
+ shll $0x2,%eax # Get int
+ movl (%eax),%eax # vector
+ subl $0x6,0x14(%ebp) # Adjust V86 ESP
+ movzwl 0x18(%ebp),%ebx # V86 SS
+ shll $0x4,%ebx # * 0x10
+ addl 0x14(%ebp),%ebx # + V86 ESP
+ xchgw %ax,0x8(%ebp) # Swap V86 IP
+ rorl $0x10,%eax # Swap words
+ xchgw %ax,0xc(%ebp) # Swap V86 CS
+ roll $0x10,%eax # Swap words
+ movl %eax,(%ebx) # CS:IP for IRET
+ movl 0x10(%ebp),%eax # V86 flags
+ movw %ax,0x4(%ebx) # Flags for IRET
+ andb $~0x3,0x11(%ebp) # Clear IF, TF
+ popl %ebx # Restore
+ popl %ebp # saved
+ popl %eax # registers
+ iret # To V86 mode
+#
+# Invoke V86 interrupt from user mode, with arguments.
+#
+intx31: stc # Have btx_v86
+ pushl %eax # Missing int no
+#
+# Invoke V86 interrupt from user mode.
+#
+intusr: std # String ops dec
+ pushl %eax # Expand
+ pushl %eax # stack
+ pushl %eax # frame
+ pusha # Save
+ pushl %gs # Save
+ movl %esp,%eax # seg regs
+ pushl %fs # and
+ pushl %ds # point
+ pushl %es # to them
+ push $SEL_SDATA # Set up
+ popl %ds # to
+ pushl %ds # address
+ popl %es # data
+ movl $MEM_USR,%ebx # User base
+ movl %ebx,%edx # address
+ jc intusr.1 # If btx_v86
+ xorl %edx,%edx # Control flags
+ xorl %ebp,%ebp # btx_v86 pointer
+intusr.1: leal 0x50(%esp,1),%esi # Base of frame
+ pushl %esi # Save
+ addl -0x4(%esi),%ebx # User ESP
+ movl MEM_TSS+TSS_ESP1,%edi # Link stack pointer
+ leal -0x4(%edi),%edi # Adjust for push
+ xorl %ecx,%ecx # Zero
+ movb $0x5,%cl # Push exception
+ rep # frame on
+ movsl # link stack
+ xchgl %eax,%esi # Saved seg regs
+ movl 0x40(%esp,1),%eax # Get int no
+ testl %edx,%edx # Have btx_v86?
+ jz intusr.2 # No
+ movl (%ebx),%ebp # btx_v86 pointer
+ movb $0x4,%cl # Count
+ addl %ecx,%ebx # Adjust for pop
+ rep # Push saved seg regs
+ movsl # on link stack
+ addl %ebp,%edx # Flatten btx_v86 ptr
+ leal 0x14(%edx),%esi # Seg regs pointer
+ movl 0x4(%edx),%eax # Get int no/address
+ movzwl 0x2(%edx),%edx # Get control flags
+intusr.2: movl %ebp,(%edi) # Push btx_v86 and
+ movl %edi,MEM_TSS+TSS_ESP1 # save link stack ptr
+ popl %edi # Base of frame
+ xchgl %eax,%ebp # Save intno/address
+ movl 0x48(%esp,1),%eax # Get flags
+ testb $0x2,%dl # Simulate CALLF?
+ jnz intusr.3 # Yes
+ decl %ebx # Push flags
+ decl %ebx # on V86
+ movw %ax,(%ebx) # stack
+intusr.3: movb $0x4,%cl # Count
+ subl %ecx,%ebx # Push return address
+ movl $inthlt,(%ebx) # on V86 stack
+ rep # Copy seg regs to
+ movsl # exception frame
+ xchgl %eax,%ecx # Save flags
+ movl %ebx,%eax # User ESP
+ subl $V86_STK,%eax # Less bytes
+ ja intusr.4 # to
+ xorl %eax,%eax # keep
+intusr.4: shrl $0x4,%eax # Gives segment
+ stosl # Set SS
+ shll $0x4,%eax # To bytes
+ xchgl %eax,%ebx # Swap
+ subl %ebx,%eax # Gives offset
+ stosl # Set ESP
+ xchgl %eax,%ecx # Get flags
+ btsl $0x11,%eax # Set VM
+ andb $~0x1,%ah # Clear TF
+ stosl # Set EFL
+ xchgl %eax,%ebp # Get int no/address
+ testb $0x1,%dl # Address?
+ jnz intusr.5 # Yes
+ shll $0x2,%eax # Scale
+ movl (%eax),%eax # Load int vector
+intusr.5: movl %eax,%ecx # Save
+ shrl $0x10,%eax # Gives segment
+ stosl # Set CS
+ movw %cx,%ax # Restore
+ stosl # Set EIP
+ leal 0x10(%esp,1),%esp # Discard seg regs
+ popa # Restore
+ iret # To V86 mode
+#
+# System Call.
+#
+intx30: cmpl $SYS_EXEC,%eax # Exec system call?
+ jne intx30.1 # No
+ pushl %ss # Set up
+ popl %es # all
+ pushl %es # segment
+ popl %ds # registers
+ pushl %ds # for the
+ popl %fs # program
+ pushl %fs # we're
+ popl %gs # invoking
+ movl $MEM_USR,%eax # User base address
+ addl 0xc(%esp,1),%eax # Change to user
+ leal 0x4(%eax),%esp # stack
+ifdef(`PAGING',`
+ movl %cr0,%eax # Turn
+ andl $~0x80000000,%eax # off
+ movl %eax,%cr0 # paging
+ xorl %eax,%eax # Flush
+ movl %eax,%cr3 # TLB
+')
+ popl %eax # Call
+ call *%eax # program
+intx30.1: orb $0x1,%ss:btx_hdr+0x7 # Flag reboot
+ jmp exit # Exit
+#
+# Dump structure [EBX] to [EDI], using format string [ESI].
+#
+dump.0: stosb # Save char
+dump: lodsb # Load char
+ testb %al,%al # End of string?
+ jz dump.10 # Yes
+ testb $0x80,%al # Control?
+ jz dump.0 # No
+ movb %al,%ch # Save control
+ movb $'=',%al # Append
+ stosb # '='
+ lodsb # Get offset
+ pushl %esi # Save
+ movsbl %al,%esi # To
+ addl %ebx,%esi # pointer
+ testb $DMP_X16,%ch # Dump word?
+ jz dump.1 # No
+ lodsw # Get and
+ call hex16 # dump it
+dump.1: testb $DMP_X32,%ch # Dump long?
+ jz dump.2 # No
+ lodsl # Get and
+ call hex32 # dump it
+dump.2: testb $DMP_MEM,%ch # Dump memory?
+ jz dump.8 # No
+ pushl %ds # Save
+ testb $0x2,0x52(%ebx) # V86 mode?
+ jnz dump.3 # Yes
+ verr 0x4(%esi) # Readable selector?
+ jnz dump.3 # No
+ ldsl (%esi),%esi # Load pointer
+ jmp dump.4 # Join common code
+dump.3: lodsl # Set offset
+ xchgl %eax,%edx # Save
+ lodsl # Get segment
+ shll $0x4,%eax # * 0x10
+ addl %edx,%eax # + offset
+ xchgl %eax,%esi # Set pointer
+dump.4: movb $2,%dl # Num lines
+dump.4a: movb $0x10,%cl # Bytes to dump
+dump.5: lodsb # Get byte and
+ call hex8 # dump it
+ decb %cl # Keep count
+ jz dump.6a # If done
+ movb $'-',%al # Separator
+ cmpb $0x8,%cl # Half way?
+ je dump.6 # Yes
+ movb $' ',%al # Use space
+dump.6: stosb # Save separator
+ jmp dump.5 # Continue
+dump.6a: decb %dl # Keep count
+ jz dump.7 # If done
+ movb $0xa,%al # Line feed
+ stosb # Save one
+ movb $7,%cl # Leading
+ movb $' ',%al # spaces
+dump.6b: stosb # Dump
+ decb %cl # spaces
+ jnz dump.6b
+ jmp dump.4a # Next line
+dump.7: popl %ds # Restore
+dump.8: popl %esi # Restore
+ movb $0xa,%al # Line feed
+ testb $DMP_EOL,%ch # End of line?
+ jnz dump.9 # Yes
+ movb $' ',%al # Use spaces
+ stosb # Save one
+dump.9: jmp dump.0 # Continue
+dump.10: stosb # Terminate string
+ 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)
+#
+# Output zero-terminated string [ESI] to the console.
+#
+putstr.0: call putchr # Output char
+putstr: lodsb # Load char
+ testb %al,%al # End of string?
+ jnz putstr.0 # No
+ ret # To caller
+ifdef(`BTX_SERIAL',`
+ .set SIO_PRT,SIOPRT # Base port
+ .set SIO_FMT,SIOFMT # 8N1
+ .set SIO_DIV,(115200/SIOSPD) # 115200 / SPD
+
+# void sio_init(void)
+
+sio_init: movw $SIO_PRT+0x3,%dx # Data format reg
+ movb $SIO_FMT|0x80,%al # Set format
+ outb %al,(%dx) # and DLAB
+ pushl %edx # Save
+ subb $0x3,%dl # Divisor latch reg
+ movw $SIO_DIV,%ax # Set
+ outw %ax,(%dx) # BPS
+ popl %edx # Restore
+ movb $SIO_FMT,%al # Clear
+ outb %al,(%dx) # DLAB
+ incl %edx # Modem control reg
+ movb $0x3,%al # Set RTS,
+ outb %al,(%dx) # DTR
+ incl %edx # Line status reg
+
+# void sio_flush(void)
+
+sio_flush.0: call sio_getc.1 # Get character
+sio_flush: call sio_ischar # Check for character
+ jnz sio_flush.0 # Till none
+ ret # To caller
+
+# void sio_putc(int c)
+
+sio_putc: movw $SIO_PRT+0x5,%dx # Line status reg
+ xor %ecx,%ecx # Timeout
+ movb $0x40,%ch # counter
+sio_putc.1: inb (%dx),%al # Transmitter
+ testb $0x20,%al # buffer empty?
+ loopz sio_putc.1 # No
+ jz sio_putc.2 # If timeout
+ movb 0x4(%esp,1),%al # Get character
+ subb $0x5,%dl # Transmitter hold reg
+ outb %al,(%dx) # Write character
+sio_putc.2: ret $0x4 # To caller
+
+# int sio_getc(void)
+
+sio_getc: call sio_ischar # Character available?
+ jz sio_getc # No
+sio_getc.1: subb $0x5,%dl # Receiver buffer reg
+ inb (%dx),%al # Read character
+ ret # To caller
+
+# int sio_ischar(void)
+
+sio_ischar: movw $SIO_PRT+0x5,%dx # Line status register
+ xorl %eax,%eax # Zero
+ inb (%dx),%al # Received data
+ andb $0x1,%al # ready?
+ ret # To caller
+
+#
+# Output character AL to the serial console.
+#
+putchr: pusha # Save
+ cmpb $10, %al # is it a newline?
+ jne putchr.1 # no?, then leave
+ push $13 # output a carriage
+ call sio_putc # return first
+ movb $10, %al # restore %al
+putchr.1: pushl %eax # Push the character
+ # onto the stack
+ call sio_putc # Output the character
+ popa # Restore
+ ret # To caller
+',`
+#
+# Output character AL to the console.
+#
+putchr: pusha # Save
+ xorl %ecx,%ecx # Zero for loops
+ movb $SCR_MAT,%ah # Mode/attribute
+ movl $BDA_POS,%ebx # BDA pointer
+ movw (%ebx),%dx # Cursor position
+ movl $0xb8000,%edi # Regen buffer (color)
+ cmpb %ah,BDA_SCR-BDA_POS(%ebx) # Mono mode?
+ jne putchr.1 # No
+ xorw %di,%di # Regen buffer (mono)
+putchr.1: cmpb $0xa,%al # New line?
+ je putchr.2 # Yes
+ xchgl %eax,%ecx # Save char
+ movb $SCR_COL,%al # Columns per row
+ mulb %dh # * row position
+ addb %dl,%al # + column
+ adcb $0x0,%ah # position
+ shll %eax # * 2
+ xchgl %eax,%ecx # Swap char, offset
+ movw %ax,(%edi,%ecx,1) # Write attr:char
+ incl %edx # Bump cursor
+ cmpb $SCR_COL,%dl # Beyond row?
+ jb putchr.3 # No
+putchr.2: xorb %dl,%dl # Zero column
+ incb %dh # Bump row
+putchr.3: cmpb $SCR_ROW,%dh # Beyond screen?
+ jb putchr.4 # No
+ leal 2*SCR_COL(%edi),%esi # New top line
+ movw $(SCR_ROW-1)*SCR_COL/2,%cx # Words to move
+ rep # Scroll
+ movsl # screen
+ movb $0x20,%al # Space
+ movb $SCR_COL,%cl # Columns to clear
+ rep # Clear
+ stosw # line
+ movb $SCR_ROW-1,%dh # Bottom line
+putchr.4: movw %dx,(%ebx) # Update position
+ popa # Restore
+ ret # To caller
+')
+
+ .p2align 4
+#
+# Global descriptor table.
+#
+gdt: .word 0x0,0x0,0x0,0x0 # Null entry
+ .word 0xffff,0x0,0x9a00,0xcf # SEL_SCODE
+ .word 0xffff,0x0,0x9200,0xcf # SEL_SDATA
+ .word 0xffff,0x0,0x9a00,0x0 # SEL_RCODE
+ .word 0xffff,0x0,0x9200,0x0 # SEL_RDATA
+ .word 0xffff,MEM_USR,0xfa00,0xcf# SEL_UCODE
+ .word 0xffff,MEM_USR,0xf200,0xcf# SEL_UDATA
+ .word _TSSLM,MEM_TSS,0x8900,0x0 # SEL_TSS
+gdt.1:
+#
+# Pseudo-descriptors.
+#
+gdtdesc: .word gdt.1-gdt-1,gdt,0x0 # GDT
+idtdesc: .word _IDTLM,MEM_IDT,0x0 # IDT
+ivtdesc: .word 0x400-0x0-1,0x0,0x0 # IVT
+#
+# IDT construction control string.
+#
+idtctl: .byte 0x10, 0x8e # Int 0x0-0xf
+ .word 0x7dfb,intx00 # (exceptions)
+ .byte 0x10, 0x8e # Int 0x10
+ .word 0x1, intx10 # (exception)
+ .byte 0x10, 0x8e # Int 0x20-0x2f
+ .word 0xffff,intx20 # (hardware)
+ .byte 0x1, 0xee # int 0x30
+ .word 0x1, intx30 # (system call)
+ .byte 0x2, 0xee # Int 0x31-0x32
+ .word 0x1, intx31 # (V86, null)
+ .byte 0x0 # End of string
+#
+# Dump format string.
+#
+dmpfmt: .byte '\n' # "\n"
+ .ascii "int" # "int="
+ .byte 0x80|DMP_X32, 0x40 # "00000000 "
+ .ascii "err" # "err="
+ .byte 0x80|DMP_X32, 0x44 # "00000000 "
+ .ascii "efl" # "efl="
+ .byte 0x80|DMP_X32, 0x50 # "00000000 "
+ .ascii "eip" # "eip="
+ .byte 0x80|DMP_X32|DMP_EOL,0x48 # "00000000\n"
+ .ascii "eax" # "eax="
+ .byte 0x80|DMP_X32, 0x34 # "00000000 "
+ .ascii "ebx" # "ebx="
+ .byte 0x80|DMP_X32, 0x28 # "00000000 "
+ .ascii "ecx" # "ecx="
+ .byte 0x80|DMP_X32, 0x30 # "00000000 "
+ .ascii "edx" # "edx="
+ .byte 0x80|DMP_X32|DMP_EOL,0x2c # "00000000\n"
+ .ascii "esi" # "esi="
+ .byte 0x80|DMP_X32, 0x1c # "00000000 "
+ .ascii "edi" # "edi="
+ .byte 0x80|DMP_X32, 0x18 # "00000000 "
+ .ascii "ebp" # "ebp="
+ .byte 0x80|DMP_X32, 0x20 # "00000000 "
+ .ascii "esp" # "esp="
+ .byte 0x80|DMP_X32|DMP_EOL,0x0 # "00000000\n"
+ .ascii "cs" # "cs="
+ .byte 0x80|DMP_X16, 0x4c # "0000 "
+ .ascii "ds" # "ds="
+ .byte 0x80|DMP_X16, 0xc # "0000 "
+ .ascii "es" # "es="
+ .byte 0x80|DMP_X16, 0x8 # "0000 "
+ .ascii " " # " "
+ .ascii "fs" # "fs="
+ .byte 0x80|DMP_X16, 0x10 # "0000 "
+ .ascii "gs" # "gs="
+ .byte 0x80|DMP_X16, 0x14 # "0000 "
+ .ascii "ss" # "ss="
+ .byte 0x80|DMP_X16|DMP_EOL,0x4 # "0000\n"
+ .ascii "cs:eip" # "cs:eip="
+ .byte 0x80|DMP_MEM|DMP_EOL,0x48 # "00 00 ... 00 00\n"
+ .ascii "ss:esp" # "ss:esp="
+ .byte 0x80|DMP_MEM|DMP_EOL,0x0 # "00 00 ... 00 00\n"
+ .asciz "BTX halted\n" # End
+#
+# End of BTX memory.
+#
+ .p2align 4
+break:
diff --git a/sys/boot/i386/btx/btxldr/Makefile b/sys/boot/i386/btx/btxldr/Makefile
new file mode 100644
index 0000000..c89af62
--- /dev/null
+++ b/sys/boot/i386/btx/btxldr/Makefile
@@ -0,0 +1,22 @@
+# $FreeBSD$
+
+M4?= m4
+M4FLAGS+= -DLOADER_ADDRESS=${LOADER_ADDRESS}
+
+.if defined(BTXLDR_VERBOSE)
+M4FLAGS+= -DBTXLDR_VERBOSE
+.endif
+
+all: btxldr
+
+btxldr: btxldr.o
+ ${LD} -N -e start -Ttext ${LOADER_ADDRESS} -o btxldr.out btxldr.o
+ objcopy -S -O binary btxldr.out ${.TARGET}
+
+btxldr.o: btxldr.s
+ (cd ${.CURDIR}; ${M4} ${M4FLAGS} btxldr.s ) | \
+ ${AS} ${AFLAGS} -o ${.TARGET}
+
+CLEANFILES+= btxldr btxldr.out btxldr.o
+
+.include <bsd.prog.mk>
diff --git a/sys/boot/i386/btx/btxldr/btxldr.S b/sys/boot/i386/btx/btxldr/btxldr.S
new file mode 100644
index 0000000..7d4b121
--- /dev/null
+++ b/sys/boot/i386/btx/btxldr/btxldr.S
@@ -0,0 +1,396 @@
+#
+# Copyright (c) 1998 Robert Nordier
+# All rights reserved.
+#
+# Redistribution and use in source and binary forms are freely
+# permitted provided that the above copyright notice and this
+# paragraph and the following disclaimer are duplicated in all
+# such forms.
+#
+# This software is provided "AS IS" and without any express or
+# implied warranties, including, without limitation, the implied
+# warranties of merchantability and fitness for a particular
+# purpose.
+#
+
+# $FreeBSD$
+
+#
+# Prototype BTX loader program, written in a couple of hours. The
+# real thing should probably be more flexible, and in C.
+#
+
+#
+# Memory locations.
+#
+ .set MEM_STUB,0x600 # Real mode stub
+ .set MEM_ESP,0x1000 # New stack pointer
+ .set MEM_TBL,0x5000 # BTX page tables
+ .set MEM_ENTRY,0x9010 # BTX entry point
+ .set MEM_DATA,start+0x1000 # Data segment
+#
+# Segment selectors.
+#
+ .set SEL_SCODE,0x8 # 4GB code
+ .set SEL_SDATA,0x10 # 4GB data
+ .set SEL_RCODE,0x18 # 64K code
+ .set SEL_RDATA,0x20 # 64K data
+#
+# Paging constants.
+#
+ .set PAG_SIZ,0x1000 # Page size
+ .set PAG_ENT,0x4 # Page entry size
+#
+# Screen constants.
+#
+ .set SCR_MAT,0x7 # Mode/attribute
+ .set SCR_COL,0x50 # Columns per row
+ .set SCR_ROW,0x19 # Rows per screen
+#
+# BIOS Data Area locations.
+#
+ .set BDA_MEM,0x413 # Free memory
+ .set BDA_SCR,0x449 # Video mode
+ .set BDA_POS,0x450 # Cursor position
+#
+# Required by aout gas inadequacy.
+#
+ .set SIZ_STUB,0x1a # Size of stub
+#
+# We expect to be loaded by boot2 at the origin defined in ./Makefile.
+#
+ .globl start
+#
+# BTX program loader for ELF clients.
+#
+start: cld # String ops inc
+ movl $m_logo,%esi # Identify
+ call putstr # ourselves
+ movzwl BDA_MEM,%eax # Get base memory
+ shll $0xa,%eax # in bytes
+ movl %eax,%ebp # Base of user stack
+ifdef(`BTXLDR_VERBOSE',`
+ movl $m_mem,%esi # Display
+ call hexout # amount of
+ call putstr # base memory
+')
+ lgdt gdtdesc # Load new GDT
+#
+# Relocate caller's arguments.
+#
+ifdef(`BTXLDR_VERBOSE',`
+ movl $m_esp,%esi # Display
+ movl %esp,%eax # caller
+ call hexout # stack
+ call putstr # pointer
+ movl $m_args,%esi # Format string
+ leal 0x4(%esp,1),%ebx # First argument
+ movl $0x6,%ecx # Count
+start.1: movl (%ebx),%eax # Get argument and
+ addl $0x4,%ebx # bump pointer
+ call hexout # Display it
+ loop start.1 # Till done
+ call putstr # End message
+')
+ movl $0x48,%ecx # Allocate space
+ subl %ecx,%ebp # for bootinfo
+ movl 0x18(%esp,1),%esi # Source: bootinfo
+ cmpl $0x0, %esi # If the bootinfo pointer
+ je start_null_bi # is null, don't copy it
+ movl %ebp,%edi # Destination
+ rep # Copy
+ movsb # it
+ movl %ebp,0x18(%esp,1) # Update pointer
+ifdef(`BTXLDR_VERBOSE',`
+ movl $m_rel_bi,%esi # Display
+ movl %ebp,%eax # bootinfo
+ call hexout # relocation
+ call putstr # message
+')
+start_null_bi: movl $0x18,%ecx # Allocate space
+ subl %ecx,%ebp # for arguments
+ leal 0x4(%esp,1),%esi # Source
+ movl %ebp,%edi # Destination
+ rep # Copy
+ movsb # them
+ifdef(`BTXLDR_VERBOSE',`
+ movl $m_rel_args,%esi # Display
+ movl %ebp,%eax # argument
+ call hexout # relocation
+ call putstr # message
+')
+#
+# Set up BTX kernel.
+#
+ movl $MEM_ESP,%esp # Set up new stack
+ movl $MEM_DATA,%ebx # Data segment
+ movl $m_vers,%esi # Display BTX
+ call putstr # version message
+ movb 0x5(%ebx),%al # Get major version
+ addb $'0',%al # Display
+ call putchr # it
+ movb $'.',%al # And a
+ call putchr # dot
+ movb 0x6(%ebx),%al # Get minor
+ xorb %ah,%ah # version
+ movb $0xa,%dl # Divide
+ divb %dl,%al # by 10
+ addb $'0',%al # Display
+ call putchr # tens
+ movb %ah,%al # Get units
+ addb $'0',%al # Display
+ call putchr # units
+ call putstr # End message
+ movl %ebx,%esi # BTX image
+ movzwl 0x8(%ebx),%edi # Compute
+ orl $PAG_SIZ/PAG_ENT-1,%edi # the
+ incl %edi # BTX
+ shll $0x2,%edi # load
+ addl $MEM_TBL,%edi # address
+ pushl %edi # Save load address
+ movzwl 0xa(%ebx),%ecx # Image size
+ifdef(`BTXLDR_VERBOSE',`
+ pushl %ecx # Save image size
+')
+ rep # Relocate
+ movsb # BTX
+ movl %esi,%ebx # Keep place
+ifdef(`BTXLDR_VERBOSE',`
+ movl $m_rel_btx,%esi # Restore
+ popl %eax # parameters
+ call hexout # and
+')
+ popl %ebp # display
+ifdef(`BTXLDR_VERBOSE',`
+ movl %ebp,%eax # the
+ call hexout # relocation
+ call putstr # message
+')
+ addl $PAG_SIZ,%ebp # Display
+ifdef(`BTXLDR_VERBOSE',`
+ movl $m_base,%esi # the
+ movl %ebp,%eax # user
+ call hexout # base
+ call putstr # address
+')
+#
+# Set up ELF-format client program.
+#
+ cmpl $0x464c457f,(%ebx) # ELF magic number?
+ je start.3 # Yes
+ movl $e_fmt,%esi # Display error
+ call putstr # message
+start.2: jmp start.2 # Hang
+start.3:
+ifdef(`BTXLDR_VERBOSE',`
+ movl $m_elf,%esi # Display ELF
+ call putstr # message
+ movl $m_segs,%esi # Format string
+')
+ movl $0x2,%edi # Segment count
+ movl 0x1c(%ebx),%edx # Get e_phoff
+ addl %ebx,%edx # To pointer
+ movzwl 0x2c(%ebx),%ecx # Get e_phnum
+start.4: cmpl $0x1,(%edx) # Is p_type PT_LOAD?
+ jne start.6 # No
+ifdef(`BTXLDR_VERBOSE',`
+ movl 0x4(%edx),%eax # Display
+ call hexout # p_offset
+ movl 0x8(%edx),%eax # Display
+ call hexout # p_vaddr
+ movl 0x10(%edx),%eax # Display
+ call hexout # p_filesz
+ movl 0x14(%edx),%eax # Display
+ call hexout # p_memsz
+ call putstr # End message
+')
+ pushl %esi # Save
+ pushl %edi # working
+ pushl %ecx # registers
+ movl 0x4(%edx),%esi # Get p_offset
+ addl %ebx,%esi # as pointer
+ movl 0x8(%edx),%edi # Get p_vaddr
+ addl %ebp,%edi # as pointer
+ movl 0x10(%edx),%ecx # Get p_filesz
+ rep # Set up
+ movsb # segment
+ movl 0x14(%edx),%ecx # Any bytes
+ subl 0x10(%edx),%ecx # to zero?
+ jz start.5 # No
+ xorb %al,%al # Then
+ rep # zero
+ stosb # them
+start.5: popl %ecx # Restore
+ popl %edi # working
+ popl %esi # registers
+ decl %edi # Segments to do
+ je start.7 # If none
+start.6: addl $0x20,%edx # To next entry
+ loop start.4 # Till done
+start.7:
+ifdef(`BTXLDR_VERBOSE',`
+ movl $m_done,%esi # Display done
+ call putstr # message
+')
+ movl $start.8,%esi # Real mode stub
+ movl $MEM_STUB,%edi # Destination
+ movl $start.9-start.8,%ecx # Size
+ rep # Relocate
+ movsb # it
+ ljmp $SEL_RCODE,$MEM_STUB # To 16-bit code
+ .code16
+start.8: xorw %ax,%ax # Data
+ movb $SEL_RDATA,%al # selector
+ movw %ax,%ss # Reload SS
+ movw %ax,%ds # Reset
+ movw %ax,%es # other
+ movw %ax,%fs # segment
+ movw %ax,%gs # limits
+ movl %cr0,%eax # Switch to
+ decw %ax # real
+ movl %eax,%cr0 # mode
+ ljmp $0,$MEM_ENTRY # Jump to BTX entry point
+start.9:
+ .code32
+#
+# Output message [ESI] followed by EAX in hex.
+#
+hexout: pushl %eax # Save
+ call putstr # Display message
+ popl %eax # Restore
+ pushl %esi # Save
+ pushl %edi # caller's
+ movl $buf,%edi # Buffer
+ pushl %edi # Save
+ call hex32 # To hex
+ xorb %al,%al # Terminate
+ stosb # string
+ popl %esi # Restore
+hexout.1: lodsb # Get a char
+ cmpb $'0',%al # Leading zero?
+ je hexout.1 # Yes
+ testb %al,%al # End of string?
+ jne hexout.2 # No
+ decl %esi # Undo
+hexout.2: decl %esi # Adjust for inc
+ call putstr # Display hex
+ popl %edi # Restore
+ popl %esi # caller's
+ ret # To caller
+#
+# Output zero-terminated string [ESI] to the console.
+#
+putstr.0: call putchr # Output char
+putstr: lodsb # Load char
+ testb %al,%al # End of string?
+ jne putstr.0 # No
+ ret # To caller
+#
+# Output character AL to the console.
+#
+putchr: pusha # Save
+ xorl %ecx,%ecx # Zero for loops
+ movb $SCR_MAT,%ah # Mode/attribute
+ movl $BDA_POS,%ebx # BDA pointer
+ movw (%ebx),%dx # Cursor position
+ movl $0xb8000,%edi # Regen buffer (color)
+ cmpb %ah,BDA_SCR-BDA_POS(%ebx) # Mono mode?
+ jne putchr.1 # No
+ xorw %di,%di # Regen buffer (mono)
+putchr.1: cmpb $0xa,%al # New line?
+ je putchr.2 # Yes
+ xchgl %eax,%ecx # Save char
+ movb $SCR_COL,%al # Columns per row
+ mulb %dh # * row position
+ addb %dl,%al # + column
+ adcb $0x0,%ah # position
+ shll %eax # * 2
+ xchgl %eax,%ecx # Swap char, offset
+ movw %ax,(%edi,%ecx,1) # Write attr:char
+ incl %edx # Bump cursor
+ cmpb $SCR_COL,%dl # Beyond row?
+ jb putchr.3 # No
+putchr.2: xorb %dl,%dl # Zero column
+ incb %dh # Bump row
+putchr.3: cmpb $SCR_ROW,%dh # Beyond screen?
+ jb putchr.4 # No
+ leal 2*SCR_COL(%edi),%esi # New top line
+ movw $(SCR_ROW-1)*SCR_COL/2,%cx # Words to move
+ rep # Scroll
+ movsl # screen
+ movb $' ',%al # Space
+ movb $SCR_COL,%cl # Columns to clear
+ rep # Clear
+ stosw # line
+ movb $SCR_ROW-1,%dh # Bottom line
+putchr.4: movw %dx,(%ebx) # Update position
+ popa # Restore
+ 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)
+
+ .data
+ .p2align 4
+#
+# Global descriptor table.
+#
+gdt: .word 0x0,0x0,0x0,0x0 # Null entry
+ .word 0xffff,0x0,0x9a00,0xcf # SEL_SCODE
+ .word 0xffff,0x0,0x9200,0xcf # SEL_SDATA
+ .word 0xffff,0x0,0x9a00,0x0 # SEL_RCODE
+ .word 0xffff,0x0,0x9200,0x0 # SEL_RDATA
+gdt.1:
+gdtdesc: .word gdt.1-gdt-1 # Limit
+ .long gdt # Base
+#
+# Messages.
+#
+m_logo: .asciz " \nBTX loader 1.00 "
+m_vers: .asciz "BTX version is \0\n"
+e_fmt: .asciz "Error: Client format not supported\n"
+ifdef(`BTXLDR_VERBOSE',`
+m_mem: .asciz "Starting in protected mode (base mem=\0)\n"
+m_esp: .asciz "Arguments passed (esp=\0):\n"
+m_args: .asciz"<howto="
+ .asciz" bootdev="
+ .asciz" junk="
+ .asciz" "
+ .asciz" "
+ .asciz" bootinfo=\0>\n"
+m_rel_bi: .asciz "Relocated bootinfo (size=48) to \0\n"
+m_rel_args: .asciz "Relocated arguments (size=18) to \0\n"
+m_rel_btx: .asciz "Relocated kernel (size=\0) to \0\n"
+m_base: .asciz "Client base address is \0\n"
+m_elf: .asciz "Client format is ELF\n"
+m_segs: .asciz "text segment: offset="
+ .asciz " vaddr="
+ .asciz " filesz="
+ .asciz " memsz=\0\n"
+ .asciz "data segment: offset="
+ .asciz " vaddr="
+ .asciz " filesz="
+ .asciz " memsz=\0\n"
+m_done: .asciz "Loading complete\n"
+')
+#
+# Uninitialized data area.
+#
+buf: # Scratch buffer
diff --git a/sys/boot/i386/btx/btxldr/btxldr.s b/sys/boot/i386/btx/btxldr/btxldr.s
new file mode 100644
index 0000000..7d4b121
--- /dev/null
+++ b/sys/boot/i386/btx/btxldr/btxldr.s
@@ -0,0 +1,396 @@
+#
+# Copyright (c) 1998 Robert Nordier
+# All rights reserved.
+#
+# Redistribution and use in source and binary forms are freely
+# permitted provided that the above copyright notice and this
+# paragraph and the following disclaimer are duplicated in all
+# such forms.
+#
+# This software is provided "AS IS" and without any express or
+# implied warranties, including, without limitation, the implied
+# warranties of merchantability and fitness for a particular
+# purpose.
+#
+
+# $FreeBSD$
+
+#
+# Prototype BTX loader program, written in a couple of hours. The
+# real thing should probably be more flexible, and in C.
+#
+
+#
+# Memory locations.
+#
+ .set MEM_STUB,0x600 # Real mode stub
+ .set MEM_ESP,0x1000 # New stack pointer
+ .set MEM_TBL,0x5000 # BTX page tables
+ .set MEM_ENTRY,0x9010 # BTX entry point
+ .set MEM_DATA,start+0x1000 # Data segment
+#
+# Segment selectors.
+#
+ .set SEL_SCODE,0x8 # 4GB code
+ .set SEL_SDATA,0x10 # 4GB data
+ .set SEL_RCODE,0x18 # 64K code
+ .set SEL_RDATA,0x20 # 64K data
+#
+# Paging constants.
+#
+ .set PAG_SIZ,0x1000 # Page size
+ .set PAG_ENT,0x4 # Page entry size
+#
+# Screen constants.
+#
+ .set SCR_MAT,0x7 # Mode/attribute
+ .set SCR_COL,0x50 # Columns per row
+ .set SCR_ROW,0x19 # Rows per screen
+#
+# BIOS Data Area locations.
+#
+ .set BDA_MEM,0x413 # Free memory
+ .set BDA_SCR,0x449 # Video mode
+ .set BDA_POS,0x450 # Cursor position
+#
+# Required by aout gas inadequacy.
+#
+ .set SIZ_STUB,0x1a # Size of stub
+#
+# We expect to be loaded by boot2 at the origin defined in ./Makefile.
+#
+ .globl start
+#
+# BTX program loader for ELF clients.
+#
+start: cld # String ops inc
+ movl $m_logo,%esi # Identify
+ call putstr # ourselves
+ movzwl BDA_MEM,%eax # Get base memory
+ shll $0xa,%eax # in bytes
+ movl %eax,%ebp # Base of user stack
+ifdef(`BTXLDR_VERBOSE',`
+ movl $m_mem,%esi # Display
+ call hexout # amount of
+ call putstr # base memory
+')
+ lgdt gdtdesc # Load new GDT
+#
+# Relocate caller's arguments.
+#
+ifdef(`BTXLDR_VERBOSE',`
+ movl $m_esp,%esi # Display
+ movl %esp,%eax # caller
+ call hexout # stack
+ call putstr # pointer
+ movl $m_args,%esi # Format string
+ leal 0x4(%esp,1),%ebx # First argument
+ movl $0x6,%ecx # Count
+start.1: movl (%ebx),%eax # Get argument and
+ addl $0x4,%ebx # bump pointer
+ call hexout # Display it
+ loop start.1 # Till done
+ call putstr # End message
+')
+ movl $0x48,%ecx # Allocate space
+ subl %ecx,%ebp # for bootinfo
+ movl 0x18(%esp,1),%esi # Source: bootinfo
+ cmpl $0x0, %esi # If the bootinfo pointer
+ je start_null_bi # is null, don't copy it
+ movl %ebp,%edi # Destination
+ rep # Copy
+ movsb # it
+ movl %ebp,0x18(%esp,1) # Update pointer
+ifdef(`BTXLDR_VERBOSE',`
+ movl $m_rel_bi,%esi # Display
+ movl %ebp,%eax # bootinfo
+ call hexout # relocation
+ call putstr # message
+')
+start_null_bi: movl $0x18,%ecx # Allocate space
+ subl %ecx,%ebp # for arguments
+ leal 0x4(%esp,1),%esi # Source
+ movl %ebp,%edi # Destination
+ rep # Copy
+ movsb # them
+ifdef(`BTXLDR_VERBOSE',`
+ movl $m_rel_args,%esi # Display
+ movl %ebp,%eax # argument
+ call hexout # relocation
+ call putstr # message
+')
+#
+# Set up BTX kernel.
+#
+ movl $MEM_ESP,%esp # Set up new stack
+ movl $MEM_DATA,%ebx # Data segment
+ movl $m_vers,%esi # Display BTX
+ call putstr # version message
+ movb 0x5(%ebx),%al # Get major version
+ addb $'0',%al # Display
+ call putchr # it
+ movb $'.',%al # And a
+ call putchr # dot
+ movb 0x6(%ebx),%al # Get minor
+ xorb %ah,%ah # version
+ movb $0xa,%dl # Divide
+ divb %dl,%al # by 10
+ addb $'0',%al # Display
+ call putchr # tens
+ movb %ah,%al # Get units
+ addb $'0',%al # Display
+ call putchr # units
+ call putstr # End message
+ movl %ebx,%esi # BTX image
+ movzwl 0x8(%ebx),%edi # Compute
+ orl $PAG_SIZ/PAG_ENT-1,%edi # the
+ incl %edi # BTX
+ shll $0x2,%edi # load
+ addl $MEM_TBL,%edi # address
+ pushl %edi # Save load address
+ movzwl 0xa(%ebx),%ecx # Image size
+ifdef(`BTXLDR_VERBOSE',`
+ pushl %ecx # Save image size
+')
+ rep # Relocate
+ movsb # BTX
+ movl %esi,%ebx # Keep place
+ifdef(`BTXLDR_VERBOSE',`
+ movl $m_rel_btx,%esi # Restore
+ popl %eax # parameters
+ call hexout # and
+')
+ popl %ebp # display
+ifdef(`BTXLDR_VERBOSE',`
+ movl %ebp,%eax # the
+ call hexout # relocation
+ call putstr # message
+')
+ addl $PAG_SIZ,%ebp # Display
+ifdef(`BTXLDR_VERBOSE',`
+ movl $m_base,%esi # the
+ movl %ebp,%eax # user
+ call hexout # base
+ call putstr # address
+')
+#
+# Set up ELF-format client program.
+#
+ cmpl $0x464c457f,(%ebx) # ELF magic number?
+ je start.3 # Yes
+ movl $e_fmt,%esi # Display error
+ call putstr # message
+start.2: jmp start.2 # Hang
+start.3:
+ifdef(`BTXLDR_VERBOSE',`
+ movl $m_elf,%esi # Display ELF
+ call putstr # message
+ movl $m_segs,%esi # Format string
+')
+ movl $0x2,%edi # Segment count
+ movl 0x1c(%ebx),%edx # Get e_phoff
+ addl %ebx,%edx # To pointer
+ movzwl 0x2c(%ebx),%ecx # Get e_phnum
+start.4: cmpl $0x1,(%edx) # Is p_type PT_LOAD?
+ jne start.6 # No
+ifdef(`BTXLDR_VERBOSE',`
+ movl 0x4(%edx),%eax # Display
+ call hexout # p_offset
+ movl 0x8(%edx),%eax # Display
+ call hexout # p_vaddr
+ movl 0x10(%edx),%eax # Display
+ call hexout # p_filesz
+ movl 0x14(%edx),%eax # Display
+ call hexout # p_memsz
+ call putstr # End message
+')
+ pushl %esi # Save
+ pushl %edi # working
+ pushl %ecx # registers
+ movl 0x4(%edx),%esi # Get p_offset
+ addl %ebx,%esi # as pointer
+ movl 0x8(%edx),%edi # Get p_vaddr
+ addl %ebp,%edi # as pointer
+ movl 0x10(%edx),%ecx # Get p_filesz
+ rep # Set up
+ movsb # segment
+ movl 0x14(%edx),%ecx # Any bytes
+ subl 0x10(%edx),%ecx # to zero?
+ jz start.5 # No
+ xorb %al,%al # Then
+ rep # zero
+ stosb # them
+start.5: popl %ecx # Restore
+ popl %edi # working
+ popl %esi # registers
+ decl %edi # Segments to do
+ je start.7 # If none
+start.6: addl $0x20,%edx # To next entry
+ loop start.4 # Till done
+start.7:
+ifdef(`BTXLDR_VERBOSE',`
+ movl $m_done,%esi # Display done
+ call putstr # message
+')
+ movl $start.8,%esi # Real mode stub
+ movl $MEM_STUB,%edi # Destination
+ movl $start.9-start.8,%ecx # Size
+ rep # Relocate
+ movsb # it
+ ljmp $SEL_RCODE,$MEM_STUB # To 16-bit code
+ .code16
+start.8: xorw %ax,%ax # Data
+ movb $SEL_RDATA,%al # selector
+ movw %ax,%ss # Reload SS
+ movw %ax,%ds # Reset
+ movw %ax,%es # other
+ movw %ax,%fs # segment
+ movw %ax,%gs # limits
+ movl %cr0,%eax # Switch to
+ decw %ax # real
+ movl %eax,%cr0 # mode
+ ljmp $0,$MEM_ENTRY # Jump to BTX entry point
+start.9:
+ .code32
+#
+# Output message [ESI] followed by EAX in hex.
+#
+hexout: pushl %eax # Save
+ call putstr # Display message
+ popl %eax # Restore
+ pushl %esi # Save
+ pushl %edi # caller's
+ movl $buf,%edi # Buffer
+ pushl %edi # Save
+ call hex32 # To hex
+ xorb %al,%al # Terminate
+ stosb # string
+ popl %esi # Restore
+hexout.1: lodsb # Get a char
+ cmpb $'0',%al # Leading zero?
+ je hexout.1 # Yes
+ testb %al,%al # End of string?
+ jne hexout.2 # No
+ decl %esi # Undo
+hexout.2: decl %esi # Adjust for inc
+ call putstr # Display hex
+ popl %edi # Restore
+ popl %esi # caller's
+ ret # To caller
+#
+# Output zero-terminated string [ESI] to the console.
+#
+putstr.0: call putchr # Output char
+putstr: lodsb # Load char
+ testb %al,%al # End of string?
+ jne putstr.0 # No
+ ret # To caller
+#
+# Output character AL to the console.
+#
+putchr: pusha # Save
+ xorl %ecx,%ecx # Zero for loops
+ movb $SCR_MAT,%ah # Mode/attribute
+ movl $BDA_POS,%ebx # BDA pointer
+ movw (%ebx),%dx # Cursor position
+ movl $0xb8000,%edi # Regen buffer (color)
+ cmpb %ah,BDA_SCR-BDA_POS(%ebx) # Mono mode?
+ jne putchr.1 # No
+ xorw %di,%di # Regen buffer (mono)
+putchr.1: cmpb $0xa,%al # New line?
+ je putchr.2 # Yes
+ xchgl %eax,%ecx # Save char
+ movb $SCR_COL,%al # Columns per row
+ mulb %dh # * row position
+ addb %dl,%al # + column
+ adcb $0x0,%ah # position
+ shll %eax # * 2
+ xchgl %eax,%ecx # Swap char, offset
+ movw %ax,(%edi,%ecx,1) # Write attr:char
+ incl %edx # Bump cursor
+ cmpb $SCR_COL,%dl # Beyond row?
+ jb putchr.3 # No
+putchr.2: xorb %dl,%dl # Zero column
+ incb %dh # Bump row
+putchr.3: cmpb $SCR_ROW,%dh # Beyond screen?
+ jb putchr.4 # No
+ leal 2*SCR_COL(%edi),%esi # New top line
+ movw $(SCR_ROW-1)*SCR_COL/2,%cx # Words to move
+ rep # Scroll
+ movsl # screen
+ movb $' ',%al # Space
+ movb $SCR_COL,%cl # Columns to clear
+ rep # Clear
+ stosw # line
+ movb $SCR_ROW-1,%dh # Bottom line
+putchr.4: movw %dx,(%ebx) # Update position
+ popa # Restore
+ 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)
+
+ .data
+ .p2align 4
+#
+# Global descriptor table.
+#
+gdt: .word 0x0,0x0,0x0,0x0 # Null entry
+ .word 0xffff,0x0,0x9a00,0xcf # SEL_SCODE
+ .word 0xffff,0x0,0x9200,0xcf # SEL_SDATA
+ .word 0xffff,0x0,0x9a00,0x0 # SEL_RCODE
+ .word 0xffff,0x0,0x9200,0x0 # SEL_RDATA
+gdt.1:
+gdtdesc: .word gdt.1-gdt-1 # Limit
+ .long gdt # Base
+#
+# Messages.
+#
+m_logo: .asciz " \nBTX loader 1.00 "
+m_vers: .asciz "BTX version is \0\n"
+e_fmt: .asciz "Error: Client format not supported\n"
+ifdef(`BTXLDR_VERBOSE',`
+m_mem: .asciz "Starting in protected mode (base mem=\0)\n"
+m_esp: .asciz "Arguments passed (esp=\0):\n"
+m_args: .asciz"<howto="
+ .asciz" bootdev="
+ .asciz" junk="
+ .asciz" "
+ .asciz" "
+ .asciz" bootinfo=\0>\n"
+m_rel_bi: .asciz "Relocated bootinfo (size=48) to \0\n"
+m_rel_args: .asciz "Relocated arguments (size=18) to \0\n"
+m_rel_btx: .asciz "Relocated kernel (size=\0) to \0\n"
+m_base: .asciz "Client base address is \0\n"
+m_elf: .asciz "Client format is ELF\n"
+m_segs: .asciz "text segment: offset="
+ .asciz " vaddr="
+ .asciz " filesz="
+ .asciz " memsz=\0\n"
+ .asciz "data segment: offset="
+ .asciz " vaddr="
+ .asciz " filesz="
+ .asciz " memsz=\0\n"
+m_done: .asciz "Loading complete\n"
+')
+#
+# Uninitialized data area.
+#
+buf: # Scratch buffer
diff --git a/sys/boot/i386/btx/lib/Makefile b/sys/boot/i386/btx/lib/Makefile
new file mode 100644
index 0000000..fb56ca1
--- /dev/null
+++ b/sys/boot/i386/btx/lib/Makefile
@@ -0,0 +1,11 @@
+# $FreeBSD$
+
+OBJS= btxcsu.o btxsys.o btxv86.o
+CLEANFILES= crt0.o
+
+all: crt0.o
+
+crt0.o: ${OBJS}
+ ${LD} ${LDFLAGS} -i -o ${.TARGET} ${OBJS}
+
+.include <bsd.prog.mk>
diff --git a/sys/boot/i386/btx/lib/btxcsu.s b/sys/boot/i386/btx/lib/btxcsu.s
new file mode 100644
index 0000000..f08ae75
--- /dev/null
+++ b/sys/boot/i386/btx/lib/btxcsu.s
@@ -0,0 +1,43 @@
+#
+# Copyright (c) 1998 Robert Nordier
+# All rights reserved.
+#
+# Redistribution and use in source and binary forms are freely
+# permitted provided that the above copyright notice and this
+# paragraph and the following disclaimer are duplicated in all
+# such forms.
+#
+# This software is provided "AS IS" and without any express or
+# implied warranties, including, without limitation, the implied
+# warranties of merchantability and fitness for a particular
+# purpose.
+#
+
+# $FreeBSD$
+
+#
+# BTX C startup code (ELF).
+#
+
+#
+# Globals.
+#
+ .global _start
+#
+# Constants.
+#
+ .set ARGADJ,0xfa0 # Argument adjustment
+#
+# Client entry point.
+#
+_start: movl %eax,__base # Set base address
+ movl %esp,%eax # Set
+ addl $ARGADJ,%eax # argument
+ movl %eax,__args # pointer
+ call main # Invoke client main()
+ call exit # Invoke client exit()
+#
+# Data.
+#
+ .comm __base,4 # Client base address
+ .comm __args,4 # Client arguments
diff --git a/sys/boot/i386/btx/lib/btxsys.s b/sys/boot/i386/btx/lib/btxsys.s
new file mode 100644
index 0000000..9c77b42
--- /dev/null
+++ b/sys/boot/i386/btx/lib/btxsys.s
@@ -0,0 +1,40 @@
+#
+# Copyright (c) 1998 Robert Nordier
+# All rights reserved.
+#
+# Redistribution and use in source and binary forms are freely
+# permitted provided that the above copyright notice and this
+# paragraph and the following disclaimer are duplicated in all
+# such forms.
+#
+# This software is provided "AS IS" and without any express or
+# implied warranties, including, without limitation, the implied
+# warranties of merchantability and fitness for a particular
+# purpose.
+#
+
+# $FreeBSD$
+
+#
+# BTX system calls.
+#
+
+#
+# Globals.
+#
+ .global __exit
+ .global __exec
+#
+# Constants.
+#
+ .set INT_SYS,0x30 # Interrupt number
+#
+# System call: exit
+#
+__exit: xorl %eax,%eax # BTX system
+ int $INT_SYS # call 0x0
+#
+# System call: exec
+#
+__exec: movl $0x1,%eax # BTX system
+ int $INT_SYS # call 0x1
diff --git a/sys/boot/i386/btx/lib/btxv86.h b/sys/boot/i386/btx/lib/btxv86.h
new file mode 100644
index 0000000..1152f8b
--- /dev/null
+++ b/sys/boot/i386/btx/lib/btxv86.h
@@ -0,0 +1,63 @@
+/*
+ * Copyright (c) 1998 Robert Nordier
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms are freely
+ * permitted provided that the above copyright notice and this
+ * paragraph and the following disclaimer are duplicated in all
+ * such forms.
+ *
+ * This software is provided "AS IS" and without any express or
+ * implied warranties, including, without limitation, the implied
+ * warranties of merchantability and fitness for a particular
+ * purpose.
+ */
+
+/*
+ * $FreeBSD$
+ */
+
+#ifndef _BTXV86_H_
+#define _BTXV86_H_
+
+#include <sys/types.h>
+
+#define V86_ADDR 0x10000 /* Segment:offset address */
+#define V86_CALLF 0x20000 /* Emulate far call */
+#define V86_FLAGS 0x40000 /* Return flags */
+
+struct __v86 {
+ uint32_t ctl; /* Control flags */
+ uint32_t addr; /* Interrupt number or address */
+ uint32_t es; /* V86 ES register */
+ uint32_t ds; /* V86 DS register */
+ uint32_t fs; /* V86 FS register */
+ uint32_t gs; /* V86 GS register */
+ uint32_t eax; /* V86 EAX register */
+ uint32_t ecx; /* V86 ECX register */
+ uint32_t edx; /* V86 EDX register */
+ uint32_t ebx; /* V86 EBX register */
+ uint32_t efl; /* V86 eflags register */
+ uint32_t ebp; /* V86 EBP register */
+ uint32_t esi; /* V86 ESI register */
+ uint32_t edi; /* V86 EDI register */
+};
+
+extern struct __v86 __v86; /* V86 interface structure */
+void __v86int(void);
+
+#define v86 __v86
+#define v86int __v86int
+
+extern u_int32_t __base;
+extern u_int32_t __args;
+
+#define PTOV(pa) ((caddr_t)(pa) - __base)
+#define VTOP(va) ((vm_offset_t)(va) + __base)
+#define VTOPSEG(va) (u_int16_t)(VTOP((caddr_t)va) >> 4)
+#define VTOPOFF(va) (u_int16_t)(VTOP((caddr_t)va) & 0xf)
+
+void __exit(int) __attribute__((__noreturn__));
+void __exec(caddr_t, ...);
+
+#endif /* !_BTXV86_H_ */
diff --git a/sys/boot/i386/btx/lib/btxv86.s b/sys/boot/i386/btx/lib/btxv86.s
new file mode 100644
index 0000000..0d7d111
--- /dev/null
+++ b/sys/boot/i386/btx/lib/btxv86.s
@@ -0,0 +1,85 @@
+#
+# Copyright (c) 1998 Robert Nordier
+# All rights reserved.
+#
+# Redistribution and use in source and binary forms are freely
+# permitted provided that the above copyright notice and this
+# paragraph and the following disclaimer are duplicated in all
+# such forms.
+#
+# This software is provided "AS IS" and without any express or
+# implied warranties, including, without limitation, the implied
+# warranties of merchantability and fitness for a particular
+# purpose.
+#
+
+# $FreeBSD$
+
+#
+# BTX V86 interface.
+#
+
+#
+# Globals.
+#
+ .global __v86int
+#
+# Fields in V86 interface structure.
+#
+ .set V86_CTL,0x0 # Control flags
+ .set V86_ADDR,0x4 # Int number/address
+ .set V86_ES,0x8 # V86 ES
+ .set V86_DS,0xc # V86 DS
+ .set V86_FS,0x10 # V86 FS
+ .set V86_GS,0x14 # V86 GS
+ .set V86_EAX,0x18 # V86 EAX
+ .set V86_ECX,0x1c # V86 ECX
+ .set V86_EDX,0x20 # V86 EDX
+ .set V86_EBX,0x24 # V86 EBX
+ .set V86_EFL,0x28 # V86 eflags
+ .set V86_EBP,0x2c # V86 EBP
+ .set V86_ESI,0x30 # V86 ESI
+ .set V86_EDI,0x34 # V86 EDI
+#
+# Other constants.
+#
+ .set INT_V86,0x31 # Interrupt number
+ .set SIZ_V86,0x38 # Size of V86 structure
+#
+# V86 interface function.
+#
+__v86int: popl __v86ret # Save return address
+ pushl $__v86 # Push pointer
+ call __v86_swap # Load V86 registers
+ int $INT_V86 # To BTX
+ call __v86_swap # Load user registers
+ addl $0x4,%esp # Discard pointer
+ pushl __v86ret # Restore return address
+ ret # To user
+#
+# Swap V86 and user registers.
+#
+__v86_swap: xchgl %ebp,0x4(%esp,1) # Swap pointer, EBP
+ xchgl %eax,V86_EAX(%ebp) # Swap EAX
+ xchgl %ecx,V86_ECX(%ebp) # Swap ECX
+ xchgl %edx,V86_EDX(%ebp) # Swap EDX
+ xchgl %ebx,V86_EBX(%ebp) # Swap EBX
+ pushl %eax # Save
+ pushf # Put eflags
+ popl %eax # in EAX
+ xchgl %eax,V86_EFL(%ebp) # Swap
+ pushl %eax # Put EAX
+ popf # in eflags
+ movl 0x8(%esp,1),%eax # Load EBP
+ xchgl %eax,V86_EBP(%ebp) # Swap
+ movl %eax,0x8(%esp,1) # Save EBP
+ popl %eax # Restore
+ xchgl %esi,V86_ESI(%ebp) # Swap ESI
+ xchgl %edi,V86_EDI(%ebp) # Swap EDI
+ xchgl %ebp,0x4(%esp,1) # Swap pointer, EBP
+ ret # To caller
+#
+# V86 interface structure.
+#
+ .comm __v86,SIZ_V86
+ .comm __v86ret,4
diff --git a/sys/boot/i386/cdboot/Makefile b/sys/boot/i386/cdboot/Makefile
new file mode 100644
index 0000000..9e8e813
--- /dev/null
+++ b/sys/boot/i386/cdboot/Makefile
@@ -0,0 +1,18 @@
+# $FreeBSD$
+
+MAINTAINER=jhb@FreeBSD.org
+
+ORG= 0x7c00
+
+PROG= cdboot
+NOMAN=
+STRIP=
+BINDIR?= /boot
+
+${PROG}: ${PROG}.o
+ ${LD} -N -e start -Ttext ${ORG} -o ${PROG}.out ${PROG}.o
+ objcopy -S -O binary ${PROG}.out ${.TARGET}
+
+CLEANFILES+= ${PROG}.o ${PROG}.out
+
+.include <bsd.prog.mk>
diff --git a/sys/boot/i386/cdboot/cdboot.s b/sys/boot/i386/cdboot/cdboot.s
new file mode 100644
index 0000000..bf30bff
--- /dev/null
+++ b/sys/boot/i386/cdboot/cdboot.s
@@ -0,0 +1,553 @@
+#
+# Copyright (c) 2001 John Baldwin
+# All rights reserved.
+#
+# Redistribution and use in source and binary forms are freely
+# permitted provided that the above copyright notice and this
+# paragraph and the following disclaimer are duplicated in all
+# such forms.
+#
+# This software is provided "AS IS" and without any express or
+# implied warranties, including, without limitation, the implied
+# warranties of merchantability and fitness for a particular
+# purpose.
+#
+
+# $FreeBSD$
+
+#
+# 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.
+#
+
+#
+# Memory locations.
+#
+ .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
+#
+# 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,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)
+#
+ .code16
+ .globl start
+ .org 0x0, 0x0
+#
+# Program start.
+#
+start: cld # string ops inc
+ 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 $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,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
+ 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: # Have Primary VD
+#
+# Lookup the loader binary.
+#
+ mov $loader_path,%si # File to lookup
+ call lookup # Try to find it
+#
+# 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
+#
+ call seta20 # Turn A20 on
+#
+# Relocate the loader and BTX using a very lazy protected mode
+#
+ 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
+ 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
+ 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 $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
+#
+ 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
+#
+ 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: 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: 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
+ 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
+ push %si # Save
+ movzbw DIR_NAMELEN(%bx),%si # Find end of string
+ff.checkver: 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
+ 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
+ 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
+ decb DIR_NAMELEN(%bx) # Adjust length
+ff.checklen: pop %si # Restore
+ 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: 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
+ 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
+
+#
+# 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
+
+#
+# Display a single char.
+#
+putc: mov $0x7,%bx # attribute for output
+ mov $0xe,%ah # BIOS: put_char
+ int $0x10 # call BIOS, print char in %al
+ 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: in $0x64,%al # Get status
+ test $0x2,%al # Busy?
+ jnz seta20.1 # Yes
+ 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
+ mov $0xdf,%al # Enable
+ out %al,$0x60 # A20
+ sti # Enable interrupts
+ ret # To caller
+
+#
+# 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
+ stosb # Save char
+ 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,0x0,0x9200,0xcf # SEL_SDATA
+ .word 0xffff,0x0,0x9200,0x0 # SEL_RDATA
+ .word 0xffff,0x0,0x9a00,0xcf # SEL_SCODE (32-bit)
+ .word 0xffff,0x0,0x9a00,0x8f # SEL_SCODE16 (16-bit)
+gdt.1:
+#
+# Pseudo-descriptors.
+#
+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
+
+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_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 "|/-\\"
+
diff --git a/sys/boot/i386/gptboot/Makefile b/sys/boot/i386/gptboot/Makefile
new file mode 100644
index 0000000..aa8c0cd
--- /dev/null
+++ b/sys/boot/i386/gptboot/Makefile
@@ -0,0 +1,123 @@
+# $FreeBSD$
+
+PROG= boot2
+NOMAN=
+STRIP=
+BINDIR?= /boot
+BINMODE= 444
+CLEANFILES= boot boot1 boot1.out boot1.o \
+ boot2.ldr boot2.bin boot2.ld boot2.out boot2.o boot2.h \
+ boot2.s sio.o
+
+NM?= nm
+
+# A value of 0x80 enables LBA support.
+B1FLAGS= 0x80
+
+BOOT_COMCONSOLE_PORT?= 0x3f8
+BOOT_COMCONSOLE_SPEED?= 9600
+B2SIOFMT?= 0x3
+
+.if exists(${.OBJDIR}/../btx)
+BTX= ${.OBJDIR}/../btx
+.else
+BTX= ${.CURDIR}/../btx
+.endif
+
+REL1= 0x700
+ORG1= 0x7c00
+ORG2= 0x2000
+
+# Decide Level of UFS support. UFS1_AND_UFS2 doesn't fit.
+
+# BOOT2_UFS?= UFS2_ONLY
+BOOT2_UFS?= UFS1_AND_UFS2
+# BOOT2_UFS?= UFS1_ONLY
+
+CFLAGS= -elf -ffreestanding -Os -fno-builtin \
+ -fno-guess-branch-probability -fomit-frame-pointer\
+ -mno-align-long-strings \
+ -mrtd \
+ -D${BOOT2_UFS} \
+ -I${.CURDIR}/../../common \
+ -I${.CURDIR}/../btx/lib -I. \
+ -Wall -Waggregate-return -Wbad-function-cast -Wcast-align \
+ -Wmissing-declarations -Wmissing-prototypes -Wnested-externs \
+ -Wpointer-arith -Wshadow -Wstrict-prototypes -Wwrite-strings
+
+LDFLAGS=-nostdlib -static -N --gc-sections
+
+all: boot1 boot2 boot
+
+boot: boot1 boot2
+ cat boot1 boot2 > boot
+
+boot1: boot1.out
+ objcopy -S -O binary boot1.out ${.TARGET}
+
+boot1.out: boot1.o
+ ${LD} ${LDFLAGS} -e start -Ttext ${ORG1} -o ${.TARGET} boot1.o
+
+boot1.o: boot1.s
+ ${CPP} ${CFLAGS} ${.IMPSRC} | \
+ ${AS} ${AFLAGS} --defsym FLAGS=${B1FLAGS} -o ${.TARGET}
+
+boot2.o: boot2.c ${.CURDIR}/../../common/ufsread.c
+ ${CC} ${CFLAGS} -S -o boot2.s.tmp ${.IMPSRC}
+ sed -e '/align/d' -e '/nop/d' < boot2.s.tmp > boot2.s
+ rm -f boot2.s.tmp
+ ${AS} ${AFLAGS} -o boot2.o boot2.s
+
+boot2.h: boot1.out
+ ${NM} -t d ${.ALLSRC} | awk '/([0-9])+ T xread/ \
+ { x = $$1 - ORG1; \
+ printf("#define XREADORG %#x\n", REL1 + x) }' \
+ ORG1=`printf "%d" ${ORG1}` \
+ REL1=`printf "%d" ${REL1}` > boot2.h
+
+boot2: boot2.ldr boot2.bin ${BTX}/btx/btx
+ btxld -v -E ${ORG2} -f bin -b ${BTX}/btx/btx -l boot2.ldr \
+ -o boot2.ld -P 1 boot2.bin
+ @ls -l boot2.ld | awk '{ x = 7680 - $$5; \
+ print x " bytes available"; if (x < 0) exit 1 }'
+ dd if=boot2.ld of=${.TARGET} obs=7680 conv=osync 2>/dev/null
+
+boot2.ldr:
+ dd if=/dev/zero of=${.TARGET} bs=276 count=1 2>/dev/null
+
+boot2.bin: boot2.out
+ objcopy -S -O binary boot2.out ${.TARGET}
+
+boot2.out: boot2.o sio.o
+ ${LD} ${LDFLAGS} -Ttext ${ORG2} -o ${.TARGET} \
+ ${BTX}/lib/crt0.o ${.ALLSRC}
+
+boot2.o: boot2.h
+
+sio.o: sio.s
+ ${AS} ${AFLAGS} --defsym SIOPRT=${BOOT_COMCONSOLE_PORT} \
+ --defsym SIOFMT=${B2SIOFMT} \
+ --defsym SIOSPD=${BOOT_COMCONSOLE_SPEED} \
+ ${.IMPSRC} -o ${.TARGET}
+
+install:
+ ${INSTALL} -o ${BINOWN} -g ${BINGRP} -m ${BINMODE} \
+ boot ${DESTDIR}${BINDIR}/boot
+ ${INSTALL} -o ${BINOWN} -g ${BINGRP} -m ${BINMODE} \
+ boot1 ${DESTDIR}${BINDIR}/boot1
+ ${INSTALL} -o ${BINOWN} -g ${BINGRP} -m ${BINMODE} \
+ boot2 ${DESTDIR}${BINDIR}/boot2
+
+.include <bsd.prog.mk>
+
+.if defined(REALLY_AMD64)
+boot2.o: machine
+
+beforedepend ${OBJS}: machine
+
+machine:
+ ln -sf ${.CURDIR}/../../../i386/include machine
+
+CLEANFILES+= machine
+.endif
+
diff --git a/sys/boot/i386/gptboot/gptboot.c b/sys/boot/i386/gptboot/gptboot.c
new file mode 100644
index 0000000..bb58353
--- /dev/null
+++ b/sys/boot/i386/gptboot/gptboot.c
@@ -0,0 +1,645 @@
+/*-
+ * Copyright (c) 1998 Robert Nordier
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms are freely
+ * permitted provided that the above copyright notice and this
+ * paragraph and the following disclaimer are duplicated in all
+ * such forms.
+ *
+ * This software is provided "AS IS" and without any express or
+ * implied warranties, including, without limitation, the implied
+ * warranties of merchantability and fitness for a particular
+ * purpose.
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/param.h>
+#include <sys/disklabel.h>
+#include <sys/diskmbr.h>
+#include <sys/dirent.h>
+#include <sys/reboot.h>
+
+#include <machine/bootinfo.h>
+#include <machine/elf.h>
+
+#include <stdarg.h>
+
+#include <a.out.h>
+
+#include <btxv86.h>
+
+#include "boot2.h"
+#include "lib.h"
+
+#define IO_KEYBOARD 1
+#define IO_SERIAL 2
+
+#define SECOND 18 /* Circa that many ticks in a second. */
+
+#define RBX_ASKNAME 0x0 /* -a */
+#define RBX_SINGLE 0x1 /* -s */
+/* 0x2 is reserved for log2(RB_NOSYNC). */
+/* 0x3 is reserved for log2(RB_HALT). */
+/* 0x4 is reserved for log2(RB_INITNAME). */
+#define RBX_DFLTROOT 0x5 /* -r */
+#define RBX_KDB 0x6 /* -d */
+/* 0x7 is reserved for log2(RB_RDONLY). */
+/* 0x8 is reserved for log2(RB_DUMP). */
+/* 0x9 is reserved for log2(RB_MINIROOT). */
+#define RBX_CONFIG 0xa /* -c */
+#define RBX_VERBOSE 0xb /* -v */
+#define RBX_SERIAL 0xc /* -h */
+#define RBX_CDROM 0xd /* -C */
+/* 0xe is reserved for log2(RB_POWEROFF). */
+#define RBX_GDB 0xf /* -g */
+#define RBX_MUTE 0x10 /* -m */
+/* 0x11 is reserved for log2(RB_SELFTEST). */
+/* 0x12 is reserved for boot programs. */
+/* 0x13 is reserved for boot programs. */
+#define RBX_PAUSE 0x14 /* -p */
+#define RBX_NOINTR 0x1c /* -n */
+/* 0x1d is reserved for log2(RB_MULTIPLE) and is just misnamed here. */
+#define RBX_DUAL 0x1d /* -D */
+#define RBX_PROBEKBD 0x1e /* -P */
+/* 0x1f is reserved for log2(RB_BOOTINFO). */
+
+/* pass: -a, -s, -r, -d, -c, -v, -h, -C, -g, -m, -p, -D */
+#define RBX_MASK 0x2005ffff
+
+#define PATH_CONFIG "/boot.config"
+#define PATH_BOOT3 "/boot/loader"
+#define PATH_KERNEL "/kernel"
+
+#define ARGS 0x900
+#define NOPT 12
+#define NDEV 3
+#define MEM_BASE 0x12
+#define MEM_EXT 0x15
+#define V86_CY(x) ((x) & 1)
+#define V86_ZR(x) ((x) & 0x40)
+
+#define DRV_HARD 0x80
+#define DRV_MASK 0x7f
+
+#define TYPE_AD 0
+#define TYPE_DA 1
+#define TYPE_MAXHARD TYPE_DA
+#define TYPE_FD 2
+
+extern uint32_t _end;
+
+static const char optstr[NOPT] = "DhaCgmnPprsv";
+static const unsigned char flags[NOPT] = {
+ RBX_DUAL,
+ RBX_SERIAL,
+ RBX_ASKNAME,
+ RBX_CDROM,
+ RBX_GDB,
+ RBX_MUTE,
+ RBX_NOINTR,
+ RBX_PROBEKBD,
+ RBX_PAUSE,
+ RBX_DFLTROOT,
+ RBX_SINGLE,
+ RBX_VERBOSE
+};
+
+static const char *const dev_nm[NDEV] = {"ad", "da", "fd"};
+static const unsigned char dev_maj[NDEV] = {30, 4, 2};
+
+static struct dsk {
+ unsigned drive;
+ unsigned type;
+ unsigned unit;
+ unsigned slice;
+ unsigned part;
+ unsigned start;
+ int init;
+} dsk;
+static char cmd[512];
+static char kname[1024];
+static uint32_t opts;
+static struct bootinfo bootinfo;
+static uint8_t ioctrl = IO_KEYBOARD;
+
+void exit(int);
+static void load(void);
+static int parse(void);
+static int xfsread(ino_t, void *, size_t);
+static int dskread(void *, unsigned, unsigned);
+static void printf(const char *,...);
+static void putchar(int);
+static uint32_t memsize(void);
+static int drvread(void *, unsigned, unsigned);
+static int keyhit(unsigned);
+static int xputc(int);
+static int xgetc(int);
+static int getc(int);
+
+#define memcpy __builtin_memcpy
+
+static inline int
+strcmp(const char *s1, const char *s2)
+{
+ for (; *s1 == *s2 && *s1; s1++, s2++);
+ return (unsigned char)*s1 - (unsigned char)*s2;
+}
+
+#include "ufsread.c"
+
+static int
+xfsread(ino_t inode, void *buf, size_t nbyte)
+{
+ if ((size_t)fsread(inode, buf, nbyte) != nbyte) {
+ printf("Invalid %s\n", "format");
+ return -1;
+ }
+ return 0;
+}
+
+static inline uint32_t
+memsize(void)
+{
+ v86.addr = MEM_EXT;
+ v86.eax = 0x8800;
+ v86int();
+ return v86.eax;
+}
+
+static inline void
+getstr(void)
+{
+ char *s;
+ int c;
+
+ s = cmd;
+ for (;;) {
+ switch (c = xgetc(0)) {
+ case 0:
+ break;
+ case '\177':
+ case '\b':
+ if (s > cmd) {
+ s--;
+ printf("\b \b");
+ }
+ break;
+ case '\n':
+ case '\r':
+ *s = 0;
+ return;
+ default:
+ if (s - cmd < sizeof(cmd) - 1)
+ *s++ = c;
+ putchar(c);
+ }
+ }
+}
+
+static inline void
+putc(int c)
+{
+ v86.addr = 0x10;
+ v86.eax = 0xe00 | (c & 0xff);
+ v86.ebx = 0x7;
+ v86int();
+}
+
+int
+main(void)
+{
+ int autoboot;
+ ino_t ino;
+
+ dmadat = (void *)(roundup2(__base + (int32_t)&_end, 0x10000) - __base);
+ v86.ctl = V86_FLAGS;
+ dsk.drive = *(uint8_t *)PTOV(ARGS);
+ dsk.type = dsk.drive & DRV_HARD ? TYPE_AD : TYPE_FD;
+ dsk.unit = dsk.drive & DRV_MASK;
+ dsk.slice = *(uint8_t *)PTOV(ARGS + 1) + 1;
+ bootinfo.bi_version = BOOTINFO_VERSION;
+ bootinfo.bi_size = sizeof(bootinfo);
+ bootinfo.bi_basemem = 0; /* XXX will be filled by loader or kernel */
+ bootinfo.bi_extmem = memsize();
+ bootinfo.bi_memsizes_valid++;
+
+ /* Process configuration file */
+
+ autoboot = 1;
+
+ if ((ino = lookup(PATH_CONFIG)))
+ fsread(ino, cmd, sizeof(cmd));
+
+ if (*cmd) {
+ printf("%s: %s", PATH_CONFIG, cmd);
+ if (parse())
+ autoboot = 0;
+ /* Do not process this command twice */
+ *cmd = 0;
+ }
+
+ /*
+ * Try to exec stage 3 boot loader. If interrupted by a keypress,
+ * or in case of failure, try to load a kernel directly instead.
+ */
+
+ if (autoboot && !*kname) {
+ memcpy(kname, PATH_BOOT3, sizeof(PATH_BOOT3));
+ if (!keyhit(3*SECOND)) {
+ load();
+ memcpy(kname, PATH_KERNEL, sizeof(PATH_KERNEL));
+ }
+ }
+
+ /* Present the user with the boot2 prompt. */
+
+ for (;;) {
+ printf("\nFreeBSD/i386 boot\n"
+ "Default: %u:%s(%u,%c)%s\n"
+ "boot: ",
+ dsk.drive & DRV_MASK, dev_nm[dsk.type], dsk.unit,
+ 'a' + dsk.part, kname);
+ if (ioctrl & IO_SERIAL)
+ sio_flush();
+ if (!autoboot || keyhit(5*SECOND))
+ getstr();
+ else
+ putchar('\n');
+ autoboot = 0;
+ if (parse())
+ putchar('\a');
+ else
+ load();
+ }
+}
+
+/* XXX - Needed for btxld to link the boot2 binary; do not remove. */
+void
+exit(int x)
+{
+}
+
+static void
+load(void)
+{
+ union {
+ struct exec ex;
+ Elf32_Ehdr eh;
+ } hdr;
+ Elf32_Phdr ep[2];
+ Elf32_Shdr es[2];
+ caddr_t p;
+ ino_t ino;
+ uint32_t addr, x;
+ int fmt, i, j;
+
+ if (!(ino = lookup(kname))) {
+ if (!ls)
+ printf("No %s\n", kname);
+ return;
+ }
+ if (xfsread(ino, &hdr, sizeof(hdr)))
+ return;
+ if (N_GETMAGIC(hdr.ex) == ZMAGIC)
+ fmt = 0;
+ else if (IS_ELF(hdr.eh))
+ fmt = 1;
+ else {
+ printf("Invalid %s\n", "format");
+ return;
+ }
+ if (fmt == 0) {
+ addr = hdr.ex.a_entry & 0xffffff;
+ p = PTOV(addr);
+ fs_off = PAGE_SIZE;
+ if (xfsread(ino, p, hdr.ex.a_text))
+ return;
+ p += roundup2(hdr.ex.a_text, PAGE_SIZE);
+ if (xfsread(ino, p, hdr.ex.a_data))
+ return;
+ p += hdr.ex.a_data + roundup2(hdr.ex.a_bss, PAGE_SIZE);
+ bootinfo.bi_symtab = VTOP(p);
+ memcpy(p, &hdr.ex.a_syms, sizeof(hdr.ex.a_syms));
+ p += sizeof(hdr.ex.a_syms);
+ if (hdr.ex.a_syms) {
+ if (xfsread(ino, p, hdr.ex.a_syms))
+ return;
+ p += hdr.ex.a_syms;
+ if (xfsread(ino, p, sizeof(int)))
+ return;
+ x = *(uint32_t *)p;
+ p += sizeof(int);
+ x -= sizeof(int);
+ if (xfsread(ino, p, x))
+ return;
+ p += x;
+ }
+ } else {
+ fs_off = hdr.eh.e_phoff;
+ for (j = i = 0; i < hdr.eh.e_phnum && j < 2; i++) {
+ if (xfsread(ino, ep + j, sizeof(ep[0])))
+ return;
+ if (ep[j].p_type == PT_LOAD)
+ j++;
+ }
+ for (i = 0; i < 2; i++) {
+ p = PTOV(ep[i].p_paddr & 0xffffff);
+ fs_off = ep[i].p_offset;
+ if (xfsread(ino, p, ep[i].p_filesz))
+ return;
+ }
+ p += roundup2(ep[1].p_memsz, PAGE_SIZE);
+ bootinfo.bi_symtab = VTOP(p);
+ if (hdr.eh.e_shnum == hdr.eh.e_shstrndx + 3) {
+ fs_off = hdr.eh.e_shoff + sizeof(es[0]) *
+ (hdr.eh.e_shstrndx + 1);
+ if (xfsread(ino, &es, sizeof(es)))
+ return;
+ for (i = 0; i < 2; i++) {
+ memcpy(p, &es[i].sh_size, sizeof(es[i].sh_size));
+ p += sizeof(es[i].sh_size);
+ fs_off = es[i].sh_offset;
+ if (xfsread(ino, p, es[i].sh_size))
+ return;
+ p += es[i].sh_size;
+ }
+ }
+ addr = hdr.eh.e_entry & 0xffffff;
+ }
+ bootinfo.bi_esymtab = VTOP(p);
+ bootinfo.bi_kernelname = VTOP(kname);
+ bootinfo.bi_bios_dev = dsk.drive;
+ __exec((caddr_t)addr, RB_BOOTINFO | (opts & RBX_MASK),
+ MAKEBOOTDEV(dev_maj[dsk.type], 0, dsk.slice, dsk.unit, dsk.part),
+ 0, 0, 0, VTOP(&bootinfo));
+}
+
+static int
+parse()
+{
+ char *arg = cmd;
+ char *p, *q;
+ unsigned int drv;
+ int c, i;
+
+ while ((c = *arg++)) {
+ if (c == ' ' || c == '\t' || c == '\n')
+ continue;
+ for (p = arg; *p && *p != '\n' && *p != ' ' && *p != '\t'; p++);
+ if (*p)
+ *p++ = 0;
+ if (c == '-') {
+ while ((c = *arg++)) {
+ for (i = 0; c != optstr[i]; i++)
+ if (i == NOPT - 1)
+ return -1;
+ opts ^= 1 << flags[i];
+ }
+ if (opts & 1 << RBX_PROBEKBD) {
+ i = *(uint8_t *)PTOV(0x496) & 0x10;
+ printf("Keyboard: %s\n", i ? "yes" : "no");
+ if (!i)
+ opts |= 1 << RBX_DUAL | 1 << RBX_SERIAL;
+ opts &= ~(1 << RBX_PROBEKBD);
+ }
+ ioctrl = opts & 1 << RBX_DUAL ? (IO_SERIAL|IO_KEYBOARD) :
+ opts & 1 << RBX_SERIAL ? IO_SERIAL : IO_KEYBOARD;
+ if (ioctrl & IO_SERIAL)
+ sio_init();
+ } else {
+ for (q = arg--; *q && *q != '('; q++);
+ if (*q) {
+ drv = -1;
+ if (arg[1] == ':') {
+ drv = *arg - '0';
+ if (drv > 9)
+ return (-1);
+ arg += 2;
+ }
+ if (q - arg != 2)
+ return -1;
+ for (i = 0; arg[0] != dev_nm[i][0] ||
+ arg[1] != dev_nm[i][1]; i++)
+ if (i == NDEV - 1)
+ return -1;
+ dsk.type = i;
+ arg += 3;
+ dsk.unit = *arg - '0';
+ if (arg[1] != ',' || dsk.unit > 9)
+ return -1;
+ arg += 2;
+ dsk.slice = WHOLE_DISK_SLICE;
+ if (arg[1] == ',') {
+ dsk.slice = *arg - '0' + 1;
+ if (dsk.slice > NDOSPART)
+ return -1;
+ arg += 2;
+ }
+ if (arg[1] != ')')
+ return -1;
+ dsk.part = *arg - 'a';
+ if (dsk.part > 7)
+ return (-1);
+ arg += 2;
+ if (drv == -1)
+ drv = dsk.unit;
+ dsk.drive = (dsk.type <= TYPE_MAXHARD
+ ? DRV_HARD : 0) + drv;
+ dsk_meta = 0;
+ }
+ if ((i = p - arg - !*(p - 1))) {
+ if ((size_t)i >= sizeof(kname))
+ return -1;
+ memcpy(kname, arg, i + 1);
+ }
+ }
+ arg = p;
+ }
+ return 0;
+}
+
+static int
+dskread(void *buf, unsigned lba, unsigned nblk)
+{
+ struct dos_partition *dp;
+ struct disklabel *d;
+ char *sec;
+ unsigned sl, i;
+
+ if (!dsk_meta) {
+ sec = dmadat->secbuf;
+ dsk.start = 0;
+ if (drvread(sec, DOSBBSECTOR, 1))
+ return -1;
+ dp = (void *)(sec + DOSPARTOFF);
+ sl = dsk.slice;
+ if (sl < BASE_SLICE) {
+ for (i = 0; i < NDOSPART; i++)
+ if (dp[i].dp_typ == DOSPTYP_386BSD &&
+ (dp[i].dp_flag & 0x80 || sl < BASE_SLICE)) {
+ sl = BASE_SLICE + i;
+ if (dp[i].dp_flag & 0x80 ||
+ dsk.slice == COMPATIBILITY_SLICE)
+ break;
+ }
+ if (dsk.slice == WHOLE_DISK_SLICE)
+ dsk.slice = sl;
+ }
+ if (sl != WHOLE_DISK_SLICE) {
+ if (sl != COMPATIBILITY_SLICE)
+ dp += sl - BASE_SLICE;
+ if (dp->dp_typ != DOSPTYP_386BSD) {
+ printf("Invalid %s\n", "slice");
+ return -1;
+ }
+ dsk.start = dp->dp_start;
+ }
+ if (drvread(sec, dsk.start + LABELSECTOR, 1))
+ return -1;
+ d = (void *)(sec + LABELOFFSET);
+ if (d->d_magic != DISKMAGIC || d->d_magic2 != DISKMAGIC) {
+ if (dsk.part != RAW_PART) {
+ printf("Invalid %s\n", "label");
+ return -1;
+ }
+ } else {
+ if (!dsk.init) {
+ if (d->d_type == DTYPE_SCSI)
+ dsk.type = TYPE_DA;
+ dsk.init++;
+ }
+ if (dsk.part >= d->d_npartitions ||
+ !d->d_partitions[dsk.part].p_size) {
+ printf("Invalid %s\n", "partition");
+ return -1;
+ }
+ dsk.start += d->d_partitions[dsk.part].p_offset;
+ dsk.start -= d->d_partitions[RAW_PART].p_offset;
+ }
+ }
+ return drvread(buf, dsk.start + lba, nblk);
+}
+
+static void
+printf(const char *fmt,...)
+{
+ va_list ap;
+ char buf[10];
+ char *s;
+ unsigned u;
+ int c;
+
+ va_start(ap, fmt);
+ while ((c = *fmt++)) {
+ if (c == '%') {
+ c = *fmt++;
+ switch (c) {
+ case 'c':
+ putchar(va_arg(ap, int));
+ continue;
+ case 's':
+ for (s = va_arg(ap, char *); *s; s++)
+ putchar(*s);
+ continue;
+ case 'u':
+ u = va_arg(ap, unsigned);
+ s = buf;
+ do
+ *s++ = '0' + u % 10U;
+ while (u /= 10U);
+ while (--s >= buf)
+ putchar(*s);
+ continue;
+ }
+ }
+ putchar(c);
+ }
+ va_end(ap);
+ return;
+}
+
+static void
+putchar(int c)
+{
+ if (c == '\n')
+ xputc('\r');
+ xputc(c);
+}
+
+static int
+drvread(void *buf, unsigned lba, unsigned nblk)
+{
+ static unsigned c = 0x2d5c7c2f;
+
+ printf("%c\b", c = c << 8 | c >> 24);
+ v86.ctl = V86_ADDR | V86_CALLF | V86_FLAGS;
+ v86.addr = XREADORG; /* call to xread in boot1 */
+ v86.es = VTOPSEG(buf);
+ v86.eax = lba;
+ v86.ebx = VTOPOFF(buf);
+ v86.ecx = lba >> 16;
+ v86.edx = nblk << 8 | dsk.drive;
+ v86int();
+ v86.ctl = V86_FLAGS;
+ if (V86_CY(v86.efl)) {
+ printf("error %u lba %u\n", v86.eax >> 8 & 0xff, lba);
+ return -1;
+ }
+ return 0;
+}
+
+static int
+keyhit(unsigned ticks)
+{
+ uint32_t t0, t1;
+
+ if (opts & 1 << RBX_NOINTR)
+ return 0;
+ t0 = 0;
+ for (;;) {
+ if (xgetc(1))
+ return 1;
+ t1 = *(uint32_t *)PTOV(0x46c);
+ if (!t0)
+ t0 = t1;
+ if (t1 < t0 || t1 >= t0 + ticks)
+ return 0;
+ }
+}
+
+static int
+xputc(int c)
+{
+ if (ioctrl & IO_KEYBOARD)
+ putc(c);
+ if (ioctrl & IO_SERIAL)
+ sio_putc(c);
+ return c;
+}
+
+static int
+xgetc(int fn)
+{
+ if (opts & 1 << RBX_NOINTR)
+ return 0;
+ for (;;) {
+ if (ioctrl & IO_KEYBOARD && getc(1))
+ return fn ? 1 : getc(0);
+ if (ioctrl & IO_SERIAL && sio_ischar())
+ return fn ? 1 : sio_getc();
+ if (fn)
+ return 0;
+ }
+}
+
+static int
+getc(int fn)
+{
+ v86.addr = 0x16;
+ v86.eax = fn << 8;
+ v86int();
+ return fn == 0 ? v86.eax & 0xff : !V86_ZR(v86.efl);
+}
diff --git a/sys/boot/i386/kgzldr/Makefile b/sys/boot/i386/kgzldr/Makefile
new file mode 100644
index 0000000..7228a4f
--- /dev/null
+++ b/sys/boot/i386/kgzldr/Makefile
@@ -0,0 +1,25 @@
+# $FreeBSD$
+
+SRCS= start.s boot.c inflate.c lib.c crt.s sio.s
+OBJS= ${SRCS:N*.h:R:S/$/.o/g}
+CFLAGS= -ffreestanding
+CFLAGS+=-Os
+CFLAGS+=-DKZIP
+LDFLAGS=-nostdlib -static -Wl,-r
+.PATH: ${.CURDIR}/../../../kern
+
+CLEANFILES=kgzldr.o
+
+BOOT_COMCONSOLE_PORT?= 0x3f8
+AFLAGS+=--defsym SIO_PRT=${BOOT_COMCONSOLE_PORT}
+
+all: kgzldr.o
+
+kgzldr.o: ${OBJS}
+ ${CC} ${LDFLAGS} -o ${.TARGET} ${OBJS}
+
+realinstall:
+ ${INSTALL} -o ${LIBOWN} -g ${LIBGRP} -m ${LIBMODE} \
+ kgzldr.o ${DESTDIR}${LIBDIR}
+
+.include <bsd.prog.mk>
diff --git a/sys/boot/i386/kgzldr/boot.c b/sys/boot/i386/kgzldr/boot.c
new file mode 100644
index 0000000..45ed2ee
--- /dev/null
+++ b/sys/boot/i386/kgzldr/boot.c
@@ -0,0 +1,129 @@
+/*-
+ * Copyright (c) 1999 Global Technology Associates, Inc.
+ * 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 <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/types.h>
+#include <sys/reboot.h>
+#include <sys/inflate.h>
+
+#include "kgzldr.h"
+
+#define KGZ_HEAD 0xa /* leading bytes to ignore */
+#define KGZ_TAIL 0x8 /* trailing bytes to ignore */
+
+#define E_FMT 1 /* Error: Invalid format */
+#define E_MEM 2 /* Error: Out of memory */
+
+struct kgz_hdr {
+ char ident[4]; /* identification */
+ uint32_t dload; /* decoded image load address */
+ uint32_t dsize; /* decoded image size */
+ uint32_t isize; /* image size in memory */
+ uint32_t entry; /* program entry point */
+ uint32_t nsize; /* encoded image size */
+};
+extern struct kgz_hdr kgz; /* header */
+extern uint8_t kgz_ndata[]; /* encoded image */
+
+static const char *const msg[] = {
+ "done",
+ "invalid format",
+ "out of memory"
+};
+
+static const u_char *ip; /* input pointer */
+static u_char *op; /* output pointer */
+
+static struct inflate infl; /* inflate() parameters */
+
+static int decode(void);
+static int input(void *);
+static int output(void *, u_char *, u_long);
+
+/*
+ * Uncompress and boot a kernel.
+ */
+int
+boot(int howto)
+{
+ int err;
+
+ kgz_con = howto & RB_SERIAL ? KGZ_SIO : KGZ_CRT;
+ putstr("Uncompressing ... ");
+ err = decode();
+ putstr(msg[err]);
+ putstr("\n");
+ if (err) {
+ putstr("System halted");
+ for (;;)
+ ;
+ }
+ return err;
+}
+
+/*
+ * Interface with inflate() to uncompress the data.
+ */
+static int
+decode(void)
+{
+ static u_char slide[GZ_WSIZE];
+ int err;
+
+ ip = kgz_ndata + KGZ_HEAD;
+ op = (u_char *)kgz.dload;
+ infl.gz_input = input;
+ infl.gz_output = output;
+ infl.gz_slide = slide;
+ err = inflate(&infl);
+ return err ? err == 3 ? E_MEM : E_FMT : 0;
+}
+
+/*
+ * Read a byte.
+ */
+static int
+input(void *dummy)
+{
+ if ((size_t)(ip - kgz_ndata) + KGZ_TAIL > kgz.nsize)
+ return GZ_EOF;
+ return *ip++;
+}
+
+/*
+ * Write some bytes.
+ */
+static int
+output(void *dummy, u_char * ptr, u_long len)
+{
+ if (op - (u_char *)kgz.dload + len > kgz.dsize)
+ return -1;
+ while (len--)
+ *op++ = *ptr++;
+ return 0;
+}
diff --git a/sys/boot/i386/kgzldr/crt.s b/sys/boot/i386/kgzldr/crt.s
new file mode 100644
index 0000000..cfb479f
--- /dev/null
+++ b/sys/boot/i386/kgzldr/crt.s
@@ -0,0 +1,83 @@
+#
+# Copyright (c) 1999 Global Technology Associates, Inc.
+# 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.
+#
+# From: btx.s 1.10 1999/02/25 16:27:41 rnordier
+# $FreeBSD$
+#
+
+# Screen defaults and assumptions.
+
+ .set SCR_MAT,0x7 # Mode/attribute
+ .set SCR_COL,0x50 # Columns per row
+ .set SCR_ROW,0x19 # Rows per screen
+
+# BIOS Data Area locations.
+
+ .set BDA_SCR,0x449 # Video mode
+ .set BDA_POS,0x450 # Cursor position
+
+ .globl crt_putchr
+
+# void crt_putchr(int c)
+
+crt_putchr: movb 0x4(%esp,1),%al # Get character
+ pusha # Save
+ xorl %ecx,%ecx # Zero for loops
+ movb $SCR_MAT,%ah # Mode/attribute
+ movl $BDA_POS,%ebx # BDA pointer
+ movw (%ebx),%dx # Cursor position
+ movl $0xb8000,%edi # Regen buffer (color)
+ cmpb %ah,BDA_SCR-BDA_POS(%ebx) # Mono mode?
+ jne crt_putchr.1 # No
+ xorw %di,%di # Regen buffer (mono)
+crt_putchr.1: cmpb $0xa,%al # New line?
+ je crt_putchr.2 # Yes
+ xchgl %eax,%ecx # Save char
+ movb $SCR_COL,%al # Columns per row
+ mulb %dh # * row position
+ addb %dl,%al # + column
+ adcb $0x0,%ah # position
+ shll %eax # * 2
+ xchgl %eax,%ecx # Swap char, offset
+ movw %ax,(%edi,%ecx,1) # Write attr:char
+ incl %edx # Bump cursor
+ cmpb $SCR_COL,%dl # Beyond row?
+ jb crt_putchr.3 # No
+crt_putchr.2: xorb %dl,%dl # Zero column
+ incb %dh # Bump row
+crt_putchr.3: cmpb $SCR_ROW,%dh # Beyond screen?
+ jb crt_putchr.4 # No
+ leal 2*SCR_COL(%edi),%esi # New top line
+ movw $(SCR_ROW-1)*SCR_COL/2,%cx # Words to move
+ rep # Scroll
+ movsl # screen
+ movb $' ',%al # Space
+ movb $SCR_COL,%cl # Columns to clear
+ rep # Clear
+ stosw # line
+ movb $SCR_ROW-1,%dh # Bottom line
+crt_putchr.4: movw %dx,(%ebx) # Update position
+ popa # Restore
+ ret # To caller
diff --git a/sys/boot/i386/kgzldr/kgzldr.h b/sys/boot/i386/kgzldr/kgzldr.h
new file mode 100644
index 0000000..5cd5b44
--- /dev/null
+++ b/sys/boot/i386/kgzldr/kgzldr.h
@@ -0,0 +1,41 @@
+/*
+ * Copyright (c) 1999 Global Technology Associates, Inc.
+ * 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.
+ *
+ * $FreeBSD$
+ */
+
+#define KGZ_CRT 0x1 /* Video console */
+#define KGZ_SIO 0x2 /* Serial console */
+
+extern int kgz_con;
+
+int boot(int);
+
+unsigned char *kzipmalloc(int);
+void kzipfree(void *);
+void putstr(const char *);
+
+void crt_putchr(int);
+void sio_putchr(int);
diff --git a/sys/boot/i386/kgzldr/lib.c b/sys/boot/i386/kgzldr/lib.c
new file mode 100644
index 0000000..538875b
--- /dev/null
+++ b/sys/boot/i386/kgzldr/lib.c
@@ -0,0 +1,88 @@
+/*-
+ * Copyright (c) 1999 Global Technology Associates, Inc.
+ * 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 <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/types.h>
+#include <stddef.h>
+
+#include "kgzldr.h"
+
+#define MEMSIZ 0x8000 /* Memory pool size */
+
+int kgz_con; /* Console control */
+
+static size_t memtot; /* Memory allocated: bytes */
+static u_int memcnt; /* Memory allocated: blocks */
+
+/*
+ * Library functions required by inflate().
+ */
+
+/*
+ * Allocate memory block.
+ */
+unsigned char *
+kzipmalloc(int size)
+{
+ static u_char mem[MEMSIZ];
+ void *ptr;
+
+ if (memtot + size > MEMSIZ)
+ return NULL;
+ ptr = mem + memtot;
+ memtot += size;
+ memcnt++;
+ return ptr;
+}
+
+/*
+ * Free allocated memory block.
+ */
+void
+kzipfree(void *ptr)
+{
+ memcnt--;
+ if (!memcnt)
+ memtot = 0;
+}
+
+/*
+ * Write a string to the console.
+ */
+void
+putstr(const char *str)
+{
+ int c;
+
+ while ((c = *str++)) {
+ if (kgz_con & KGZ_CRT)
+ crt_putchr(c);
+ if (kgz_con & KGZ_SIO)
+ sio_putchr(c);
+ }
+}
diff --git a/sys/boot/i386/kgzldr/sio.s b/sys/boot/i386/kgzldr/sio.s
new file mode 100644
index 0000000..ff174eb
--- /dev/null
+++ b/sys/boot/i386/kgzldr/sio.s
@@ -0,0 +1,44 @@
+#
+# Copyright (c) 1999 Global Technology Associates, Inc.
+# 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.
+#
+# From: sio.s 1.3 1999/01/10 14:48:03 rnordier
+# $FreeBSD$
+#
+
+ .globl sio_putchr
+
+# void sio_putchr(int c)
+
+sio_putchr: movw $SIO_PRT+0x5,%dx # Line status reg
+ xor %ecx,%ecx # Timeout
+ movb $0x40,%ch # counter
+sio_putchr.1: inb %dx,%al # Transmitter
+ testb $0x20,%al # buffer empty?
+ loopz sio_putchr.1 # No
+ jz sio_putchr.2 # If timeout
+ movb 0x4(%esp,1),%al # Get character
+ subb $0x5,%dl # Transmitter hold reg
+ outb %al,%dx # Write character
+sio_putchr.2: ret # To caller
diff --git a/sys/boot/i386/kgzldr/start.s b/sys/boot/i386/kgzldr/start.s
new file mode 100644
index 0000000..550fa52
--- /dev/null
+++ b/sys/boot/i386/kgzldr/start.s
@@ -0,0 +1,45 @@
+#
+# Copyright (c) 1999 Global Technology Associates, Inc.
+# 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.
+#
+# $FreeBSD$
+#
+
+ .set entry,0x10 # kgz.entry
+
+ .globl _start
+
+# C startup code for kgzldr.
+
+_start: cld # String ops inc
+ movl $_edata,%edi # Start of bss
+ movl $_end,%ecx # Compute
+ subl %edi,%ecx # size
+ xorl %eax,%eax # Zero
+ rep # Clear
+ stosb # bss
+ pushl 0x4(%esp) # Pass howto flags
+ call boot # Call C code
+ popl %ecx # Clear stack
+ jmp *kgz+entry # To loaded code
diff --git a/sys/boot/i386/libi386/Makefile b/sys/boot/i386/libi386/Makefile
new file mode 100644
index 0000000..3bdb791
--- /dev/null
+++ b/sys/boot/i386/libi386/Makefile
@@ -0,0 +1,48 @@
+# $FreeBSD$
+#
+LIB= i386
+INTERNALLIB= true
+
+SRCS= biosacpi.c bioscd.c biosdisk.c biosmem.c biospnp.c \
+ biospci.c biossmap.c bootinfo.c bootinfo32.c bootinfo64.c \
+ comconsole.c devicename.c elf32_freebsd.c \
+ elf64_freebsd.c gatea20.c \
+ i386_copy.c i386_module.c nullconsole.c pxe.c pxetramp.s \
+ time.c vidconsole.c amd64_tramp.S
+
+CFLAGS+= -ffreestanding
+BOOT_COMCONSOLE_PORT?= 0x3f8
+CFLAGS+= -DCOMPORT=${BOOT_COMCONSOLE_PORT}
+
+BOOT_COMCONSOLE_SPEED?= 9600
+CFLAGS+= -DCOMSPEED=${BOOT_COMCONSOLE_SPEED}
+
+.ifdef(BOOT_BIOSDISK_DEBUG)
+# Make the disk code more talkative
+CFLAGS+= -DDISK_DEBUG
+.endif
+
+# Include simple terminal emulation (cons25-compatible)
+CFLAGS+= -DTERM_EMU
+
+CFLAGS+= -I${.CURDIR}/../../common -I${.CURDIR}/../btx/lib \
+ -I${.CURDIR}/../../../contrib/dev/acpica \
+ -I${.CURDIR}/../../.. -I.
+# the location of libstand
+CFLAGS+= -I${.CURDIR}/../../../../lib/libstand/
+
+# Make "machine" required for all objects
+# (based on the more complete case in sys/i386/boot/Makefile.inc)
+${SRCS:M*.c:R:S/$/.o/g}: machine
+
+# If it's not there, don't consider it a target
+.if exists(${.CURDIR}/../../../i386/include)
+beforedepend ${OBJS}: machine
+
+CLEANFILES+= machine
+machine:
+ ln -sf ${.CURDIR}/../../../i386/include machine
+
+.endif
+
+.include <bsd.lib.mk>
diff --git a/sys/boot/i386/libi386/amd64_tramp.S b/sys/boot/i386/libi386/amd64_tramp.S
new file mode 100644
index 0000000..ff12c66
--- /dev/null
+++ b/sys/boot/i386/libi386/amd64_tramp.S
@@ -0,0 +1,113 @@
+/*-
+ * Copyright (c) 2003 Peter Wemm <peter@FreeBSD.org>
+ * 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.
+ *
+ * $FreeBSD$
+ */
+
+/*
+ * Quick and dirty trampoline to get into 64 bit (long) mode and running
+ * with paging enabled so that we enter the kernel at its linked address.
+ */
+#define MSR_EFER 0xc0000080
+#define EFER_LME 0x00000100
+#define CR4_PAE 0x00000020
+#define CR4_PSE 0x00000010
+#define CR0_PG 0x80000000
+
+/* GRRR. Deal with BTX that links us for a non-zero location */
+#define VPBASE 0xa000
+#define VTOP(x) ((x) + VPBASE)
+
+ .data
+
+ .p2align 12,0x40
+
+ .globl PT4
+PT4:
+ .space 0x1000
+ .globl PT3
+PT3:
+ .space 0x1000
+ .globl PT2
+PT2:
+ .space 0x1000
+
+gdtdesc:
+ .word gdtend - gdt
+ .long VTOP(gdt) # low
+ .long 0 # high
+
+gdt:
+ .long 0 # null descriptor
+ .long 0
+ .long 0x00000000 # %cs
+ .long 0x00209800
+ .long 0x00000000 # %ds
+ .long 0x00008000
+gdtend:
+
+ .text
+ .code32
+
+ .globl amd64_tramp
+amd64_tramp:
+ /* Be sure that interrupts are disabled */
+ cli
+
+ /* Turn on EFER.LME */
+ movl $MSR_EFER, %ecx
+ rdmsr
+ orl $EFER_LME, %eax
+ wrmsr
+
+ /* Turn on PAE */
+ movl %cr4, %eax
+ orl $(CR4_PAE | CR4_PSE), %eax
+ movl %eax, %cr4
+
+ /* Set %cr3 for PT4 */
+ movl $VTOP(PT4), %eax
+ movl %eax, %cr3
+
+ /* Turn on paging (implicitly sets EFER.LMA) */
+ movl %cr0, %eax
+ orl $CR0_PG, %eax
+ movl %eax, %cr0
+
+ /* Now we're in compatability mode. set %cs for long mode */
+ movl $VTOP(gdtdesc), %eax
+ movl VTOP(entry_hi), %esi
+ movl VTOP(entry_lo), %edi
+ lgdt (%eax)
+ ljmp $0x8, $VTOP(longmode)
+
+ .code64
+longmode:
+ /* We're still running V=P, jump to entry point */
+ movl %esi, %eax
+ salq $32, %rax
+ orq %rdi, %rax
+ pushq %rax
+ ret
diff --git a/sys/boot/i386/libi386/biosacpi.c b/sys/boot/i386/libi386/biosacpi.c
new file mode 100644
index 0000000..b4080bd
--- /dev/null
+++ b/sys/boot/i386/libi386/biosacpi.c
@@ -0,0 +1,130 @@
+/*-
+ * Copyright (c) 2001 Michael Smith <msmith@freebsd.org>
+ * 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 <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <stand.h>
+#include <machine/stdarg.h>
+#include <bootstrap.h>
+
+#include "acfreebsd.h"
+#include "acconfig.h"
+#define ACPI_SYSTEM_XFACE
+#include "actypes.h"
+#include "actbl.h"
+
+/*
+ * Detect ACPI and export information about the APCI BIOS into the
+ * environment.
+ */
+
+static RSDP_DESCRIPTOR *biosacpi_find_rsdp(void);
+static RSDP_DESCRIPTOR *biosacpi_search_rsdp(char *base, int length);
+
+#define RSDP_CHECKSUM_LENGTH 20
+
+void
+biosacpi_detect(void)
+{
+ RSDP_DESCRIPTOR *rsdp;
+ char buf[16];
+ int revision;
+
+ /* XXX check the BIOS datestamp */
+
+ /* locate and validate the RSDP */
+ if ((rsdp = biosacpi_find_rsdp()) == NULL)
+ return;
+
+ /* export values from the RSDP */
+ revision = rsdp->Revision;
+ if (revision == 0)
+ revision = 1;
+ sprintf(buf, "%d", revision);
+ setenv("hint.acpi.0.revision", buf, 1);
+ sprintf(buf, "%6s", rsdp->OemId);
+ buf[6] = '\0';
+ setenv("hint.acpi.0.oem", buf, 1);
+ sprintf(buf, "0x%08x", rsdp->RsdtPhysicalAddress);
+ setenv("hint.acpi.0.rsdt", buf, 1);
+ if (revision >= 2) {
+ /* XXX extended checksum? */
+ sprintf(buf, "0x%016llx", rsdp->XsdtPhysicalAddress);
+ setenv("hint.acpi.0.xsdt", buf, 1);
+ sprintf(buf, "%d", rsdp->Length);
+ setenv("hint.acpi.0.xsdt_length", buf, 1);
+ }
+ /* XXX other tables? */
+
+ setenv("acpi_load", "YES", 1);
+}
+
+/*
+ * Find the RSDP in low memory.
+ */
+static RSDP_DESCRIPTOR *
+biosacpi_find_rsdp(void)
+{
+ RSDP_DESCRIPTOR *rsdp;
+
+ /* search the EBDA */
+ if ((rsdp = biosacpi_search_rsdp((char *)0, 0x400)) != NULL)
+ return(rsdp);
+
+ /* search the BIOS space */
+ if ((rsdp = biosacpi_search_rsdp((char *)0xe0000, 0x20000)) != NULL)
+ return(rsdp);
+
+ return(NULL);
+}
+
+static RSDP_DESCRIPTOR *
+biosacpi_search_rsdp(char *base, int length)
+{
+ RSDP_DESCRIPTOR *rsdp;
+ u_int8_t *cp, sum;
+ int ofs, idx;
+
+ /* search on 16-byte boundaries */
+ for (ofs = 0; ofs < length; ofs += 16) {
+ rsdp = (RSDP_DESCRIPTOR *)(base + ofs);
+
+ /* compare signature, validate checksum */
+ if (!strncmp(rsdp->Signature, RSDP_SIG, strlen(RSDP_SIG))) {
+ cp = (u_int8_t *)rsdp;
+ sum = 0;
+ for (idx = 0; idx < RSDP_CHECKSUM_LENGTH; idx++)
+ sum += *(cp + idx);
+ if (sum != 0) {
+ printf("acpi: bad RSDP checksum (%d)\n", sum);
+ continue;
+ }
+ return(rsdp);
+ }
+ }
+ return(NULL);
+}
diff --git a/sys/boot/i386/libi386/bioscd.c b/sys/boot/i386/libi386/bioscd.c
new file mode 100644
index 0000000..7259ff5
--- /dev/null
+++ b/sys/boot/i386/libi386/bioscd.c
@@ -0,0 +1,359 @@
+/*-
+ * Copyright (c) 1998 Michael Smith <msmith@freebsd.org>
+ * Copyright (c) 2001 John H. Baldwin <jhb@FreeBSD.org>
+ * 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 <sys/cdefs.h>
+__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 <stand.h>
+
+#include <sys/param.h>
+#include <machine/bootinfo.h>
+#include <machine/psl.h>
+
+#include <stdarg.h>
+
+#include <bootstrap.h>
+#include <btxv86.h>
+#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_realstrategy(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;
+ v86.ctl = V86_FLAGS;
+ v86.addr = 0x13;
+ v86.eax = 0x4b01;
+ v86.edx = biosdev;
+ v86.ds = VTOPSEG(&bcinfo[nbcinfo].bc_sp);
+ v86.esi = VTOPOFF(&bcinfo[nbcinfo].bc_sp);
+ v86int();
+ if ((v86.eax & 0xff00) != 0)
+ 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;
+ int error;
+
+ 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, resid, 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 = 0x13;
+ v86.eax = 0;
+ v86.edx = biosdev;
+ v86int();
+ }
+
+ packet[0] = 0x10;
+ packet[1] = blks;
+ packet[2] = VTOPOFF(dest);
+ packet[3] = VTOPSEG(dest);
+ packet[4] = dblk & 0xffff;
+ packet[5] = dblk >> 16;
+ packet[6] = 0;
+ packet[7] = 0;
+ v86.ctl = V86_FLAGS;
+ v86.addr = 0x13;
+ v86.eax = 0x4200;
+ v86.edx = biosdev;
+ v86.ds = VTOPSEG(packet);
+ v86.esi = VTOPOFF(packet);
+ 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;
+ 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);
+
+ /*
+ * XXX: Need to examine device spec here to figure out if SCSI or
+ * ATAPI. No idea on how to figure out device number. All we can
+ * really pass to the kernel is what bus and device on which bus we
+ * were booted from, which dev_t isn't well suited to since those
+ * number don't match to unit numbers very well. We may just need
+ * to engage in a hack where we pass -C to the boot args if we are
+ * the boot device.
+ */
+ major = ACDMAJOR;
+ unit = 0; /* XXX */
+
+ /* XXX: Assume partition 'a'. */
+ rootdev = MAKEBOOTDEV(major, 0, 0, unit, 0);
+ DEBUG("dev is 0x%x\n", rootdev);
+ return(rootdev);
+}
diff --git a/sys/boot/i386/libi386/biosdisk.c b/sys/boot/i386/libi386/biosdisk.c
new file mode 100644
index 0000000..6d381d4
--- /dev/null
+++ b/sys/boot/i386/libi386/biosdisk.c
@@ -0,0 +1,1233 @@
+/*-
+ * Copyright (c) 1998 Michael Smith <msmith@freebsd.org>
+ * 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 <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+/*
+ * BIOS disk device handling.
+ *
+ * Ideas and algorithms from:
+ *
+ * - NetBSD libi386/biosdisk.c
+ * - FreeBSD biosboot/disk.c
+ *
+ */
+
+#include <stand.h>
+
+#include <sys/disklabel.h>
+#include <sys/diskmbr.h>
+#include <machine/bootinfo.h>
+
+#include <stdarg.h>
+
+#include <bootstrap.h>
+#include <btxv86.h>
+#include "libi386.h"
+
+#define BIOS_NUMDRIVES 0x475
+#define BIOSDISK_SECSIZE 512
+#define BUFSIZE (1 * BIOSDISK_SECSIZE)
+#define MAXBDDEV MAXDEV
+
+#define DT_ATAPI 0x10 /* disk type for ATAPI floppies */
+#define WDMAJOR 0 /* major numbers for devices we frontend for */
+#define WFDMAJOR 1
+#define FDMAJOR 2
+#define DAMAJOR 4
+
+#ifdef DISK_DEBUG
+# define DEBUG(fmt, args...) printf("%s: " fmt "\n" , __func__ , ## args)
+#else
+# define DEBUG(fmt, args...)
+#endif
+
+struct open_disk {
+ int od_dkunit; /* disk unit number */
+ int od_unit; /* BIOS unit number */
+ int od_cyl; /* BIOS geometry */
+ int od_hds;
+ int od_sec;
+ int od_boff; /* block offset from beginning of BIOS disk */
+ int od_flags;
+#define BD_MODEINT13 0x0000
+#define BD_MODEEDD1 0x0001
+#define BD_MODEEDD3 0x0002
+#define BD_MODEMASK 0x0003
+#define BD_FLOPPY 0x0004
+#define BD_LABELOK 0x0008
+#define BD_PARTTABOK 0x0010
+ struct disklabel od_disklabel;
+ int od_nslices; /* slice count */
+ struct dos_partition od_slicetab[NEXTDOSPART];
+};
+
+/*
+ * List of BIOS devices, translation from disk unit number to
+ * BIOS unit number.
+ */
+static struct bdinfo
+{
+ int bd_unit; /* BIOS unit number */
+ int bd_flags;
+ int bd_type; /* BIOS 'drive type' (floppy only) */
+} bdinfo [MAXBDDEV];
+static int nbdinfo = 0;
+
+static int bd_getgeom(struct open_disk *od);
+static int bd_read(struct open_disk *od, daddr_t dblk, int blks,
+ caddr_t dest);
+static int bd_write(struct open_disk *od, daddr_t dblk, int blks,
+ caddr_t dest);
+
+static int bd_int13probe(struct bdinfo *bd);
+
+static void bd_printslice(struct open_disk *od, struct dos_partition *dp,
+ char *prefix, int verbose);
+static void bd_printbsdslice(struct open_disk *od, daddr_t offset,
+ char *prefix, int verbose);
+
+static int bd_init(void);
+static int bd_strategy(void *devdata, int flag, daddr_t dblk,
+ size_t size, char *buf, size_t *rsize);
+static int bd_realstrategy(void *devdata, int flag, daddr_t dblk,
+ size_t size, char *buf, size_t *rsize);
+static int bd_open(struct open_file *f, ...);
+static int bd_close(struct open_file *f);
+static void bd_print(int verbose);
+
+struct devsw biosdisk = {
+ "disk",
+ DEVT_DISK,
+ bd_init,
+ bd_strategy,
+ bd_open,
+ bd_close,
+ noioctl,
+ bd_print,
+ NULL
+};
+
+static int bd_opendisk(struct open_disk **odp, struct i386_devdesc *dev);
+static void bd_closedisk(struct open_disk *od);
+static int bd_bestslice(struct open_disk *od);
+static void bd_checkextended(struct open_disk *od, int slicenum);
+
+/*
+ * Translate between BIOS device numbers and our private unit numbers.
+ */
+int
+bd_bios2unit(int biosdev)
+{
+ int i;
+
+ DEBUG("looking for bios device 0x%x", biosdev);
+ for (i = 0; i < nbdinfo; i++) {
+ DEBUG("bd unit %d is BIOS device 0x%x", i, bdinfo[i].bd_unit);
+ if (bdinfo[i].bd_unit == biosdev)
+ return(i);
+ }
+ return(-1);
+}
+
+int
+bd_unit2bios(int unit)
+{
+ if ((unit >= 0) && (unit < nbdinfo))
+ return(bdinfo[unit].bd_unit);
+ return(-1);
+}
+
+/*
+ * Quiz the BIOS for disk devices, save a little info about them.
+ */
+static int
+bd_init(void)
+{
+ int base, unit, nfd = 0;
+
+ /* sequence 0, 0x80 */
+ for (base = 0; base <= 0x80; base += 0x80) {
+ for (unit = base; (nbdinfo < MAXBDDEV); unit++) {
+ /* check the BIOS equipment list for number of fixed disks */
+ if((base == 0x80) &&
+ (nfd >= *(unsigned char *)PTOV(BIOS_NUMDRIVES)))
+ break;
+
+ bdinfo[nbdinfo].bd_unit = unit;
+ bdinfo[nbdinfo].bd_flags = (unit < 0x80) ? BD_FLOPPY : 0;
+
+ if (!bd_int13probe(&bdinfo[nbdinfo]))
+ break;
+
+ /* XXX we need "disk aliases" to make this simpler */
+ printf("BIOS drive %c: is disk%d\n",
+ (unit < 0x80) ? ('A' + unit) : ('C' + unit - 0x80), nbdinfo);
+ nbdinfo++;
+ if (base == 0x80)
+ nfd++;
+ }
+ }
+ return(0);
+}
+
+/*
+ * Try to detect a device supported by the legacy int13 BIOS
+ */
+static int
+bd_int13probe(struct bdinfo *bd)
+{
+ v86.ctl = V86_FLAGS;
+ v86.addr = 0x13;
+ v86.eax = 0x800;
+ v86.edx = bd->bd_unit;
+ v86int();
+
+ if (!(v86.efl & 0x1) && /* carry clear */
+ ((v86.edx & 0xff) > ((unsigned)bd->bd_unit & 0x7f))) { /* unit # OK */
+ bd->bd_flags |= BD_MODEINT13;
+ bd->bd_type = v86.ebx & 0xff;
+
+ /* Determine if we can use EDD with this device. */
+ v86.eax = 0x4100;
+ v86.edx = bd->bd_unit;
+ v86.ebx = 0x55aa;
+ v86int();
+ if (!(v86.efl & 0x1) && /* carry clear */
+ ((v86.ebx & 0xffff) == 0xaa55) && /* signature */
+ (v86.ecx & 0x1)) { /* packets mode ok */
+ bd->bd_flags |= BD_MODEEDD1;
+ if((v86.eax & 0xff00) > 0x300)
+ bd->bd_flags |= BD_MODEEDD3;
+ }
+ return(1);
+ }
+ return(0);
+}
+
+/*
+ * Print information about disks
+ */
+static void
+bd_print(int verbose)
+{
+ int i, j;
+ char line[80];
+ struct i386_devdesc dev;
+ struct open_disk *od;
+ struct dos_partition *dptr;
+
+ for (i = 0; i < nbdinfo; i++) {
+ sprintf(line, " disk%d: BIOS drive %c:\n", i,
+ (bdinfo[i].bd_unit < 0x80) ? ('A' + bdinfo[i].bd_unit) : ('C' + bdinfo[i].bd_unit - 0x80));
+ pager_output(line);
+
+ /* try to open the whole disk */
+ dev.d_kind.biosdisk.unit = i;
+ dev.d_kind.biosdisk.slice = -1;
+ dev.d_kind.biosdisk.partition = -1;
+
+ if (!bd_opendisk(&od, &dev)) {
+
+ /* Do we have a partition table? */
+ if (od->od_flags & BD_PARTTABOK) {
+ dptr = &od->od_slicetab[0];
+
+ /* Check for a "dedicated" disk */
+ if ((dptr[3].dp_typ == DOSPTYP_386BSD) &&
+ (dptr[3].dp_start == 0) &&
+ (dptr[3].dp_size == 50000)) {
+ sprintf(line, " disk%d", i);
+ bd_printbsdslice(od, 0, line, verbose);
+ } else {
+ for (j = 0; j < od->od_nslices; j++) {
+ sprintf(line, " disk%ds%d", i, j + 1);
+ bd_printslice(od, &dptr[j], line, verbose);
+ }
+ }
+ }
+ bd_closedisk(od);
+ }
+ }
+}
+
+/*
+ * Print information about slices on a disk. For the size calculations we
+ * assume a 512 byte sector.
+ */
+static void
+bd_printslice(struct open_disk *od, struct dos_partition *dp, char *prefix,
+ int verbose)
+{
+ char line[80];
+
+ switch (dp->dp_typ) {
+ case DOSPTYP_386BSD:
+ bd_printbsdslice(od, (daddr_t)dp->dp_start, prefix, verbose);
+ return;
+ case DOSPTYP_LINSWP:
+ if (verbose)
+ sprintf(line, "%s: Linux swap %.6dMB (%d - %d)\n",
+ prefix, dp->dp_size / 2048,
+ dp->dp_start, dp->dp_start + dp->dp_size);
+ else
+ sprintf(line, "%s: Linux swap\n", prefix);
+ break;
+ case DOSPTYP_LINUX:
+ /*
+ * XXX
+ * read the superblock to confirm this is an ext2fs partition?
+ */
+ if (verbose)
+ sprintf(line, "%s: ext2fs %.6dMB (%d - %d)\n", prefix,
+ dp->dp_size / 2048, dp->dp_start,
+ dp->dp_start + dp->dp_size);
+ else
+ sprintf(line, "%s: ext2fs\n", prefix);
+ break;
+ case 0x00: /* unused partition */
+ case DOSPTYP_EXT:
+ return;
+ case 0x01:
+ if (verbose)
+ sprintf(line, "%s: FAT-12 %.6dMB (%d - %d)\n", prefix,
+ dp->dp_size / 2048, dp->dp_start,
+ dp->dp_start + dp->dp_size);
+ else
+ sprintf(line, "%s: FAT-12\n", prefix);
+ break;
+ case 0x04:
+ case 0x06:
+ case 0x0e:
+ if (verbose)
+ sprintf(line, "%s: FAT-16 %.6dMB (%d - %d)\n", prefix,
+ dp->dp_size / 2048, dp->dp_start,
+ dp->dp_start + dp->dp_size);
+ else
+ sprintf(line, "%s: FAT-16\n", prefix);
+ break;
+ case 0x0b:
+ case 0x0c:
+ if (verbose)
+ sprintf(line, "%s: FAT-32 %.6dMB (%d - %d)\n", prefix,
+ dp->dp_size / 2048, dp->dp_start,
+ dp->dp_start + dp->dp_size);
+ else
+ sprintf(line, "%s: FAT-32\n", prefix);
+ break;
+ default:
+ if (verbose)
+ sprintf(line, "%s: Unknown fs: 0x%x %.6dMB (%d - %d)\n",
+ prefix, dp->dp_typ, dp->dp_size / 2048,
+ dp->dp_start, dp->dp_start + dp->dp_size);
+ else
+ sprintf(line, "%s: Unknown fs: 0x%x\n", prefix,
+ dp->dp_typ);
+ }
+ pager_output(line);
+}
+
+/*
+ * Print out each valid partition in the disklabel of a FreeBSD slice.
+ * For size calculations, we assume a 512 byte sector size.
+ */
+static void
+bd_printbsdslice(struct open_disk *od, daddr_t offset, char *prefix,
+ int verbose)
+{
+ char line[80];
+ char buf[BIOSDISK_SECSIZE];
+ struct disklabel *lp;
+ int i;
+
+ /* read disklabel */
+ if (bd_read(od, offset + LABELSECTOR, 1, buf))
+ return;
+ lp =(struct disklabel *)(&buf[0]);
+ if (lp->d_magic != DISKMAGIC) {
+ sprintf(line, "%s: FFS bad disklabel\n", prefix);
+ pager_output(line);
+ return;
+ }
+
+ /* Print partitions */
+ for (i = 0; i < lp->d_npartitions; i++) {
+ /*
+ * For each partition, make sure we know what type of fs it is. If
+ * not, then skip it. However, since floppies often have bogus
+ * fstypes, print the 'a' partition on a floppy even if it is marked
+ * unused.
+ */
+ if ((lp->d_partitions[i].p_fstype == FS_BSDFFS) ||
+ (lp->d_partitions[i].p_fstype == FS_SWAP) ||
+ (lp->d_partitions[i].p_fstype == FS_VINUM) ||
+ ((lp->d_partitions[i].p_fstype == FS_UNUSED) &&
+ (od->od_flags & BD_FLOPPY) && (i == 0))) {
+
+ /* Only print out statistics in verbose mode */
+ if (verbose)
+ sprintf(line, " %s%c: %s %.6dMB (%d - %d)\n", prefix, 'a' + i,
+ (lp->d_partitions[i].p_fstype == FS_SWAP) ? "swap" :
+ (lp->d_partitions[i].p_fstype == FS_VINUM) ? "vinum" :
+ "FFS",
+ lp->d_partitions[i].p_size / 2048,
+ lp->d_partitions[i].p_offset,
+ lp->d_partitions[i].p_offset + lp->d_partitions[i].p_size);
+ else
+ sprintf(line, " %s%c: %s\n", prefix, 'a' + i,
+ (lp->d_partitions[i].p_fstype == FS_SWAP) ? "swap" :
+ (lp->d_partitions[i].p_fstype == FS_VINUM) ? "vinum" :
+ "FFS");
+ pager_output(line);
+ }
+ }
+}
+
+
+/*
+ * Attempt to open the disk described by (dev) for use by (f).
+ *
+ * Note that the philosophy here is "give them exactly what
+ * they ask for". This is necessary because being too "smart"
+ * about what the user might want leads to complications.
+ * (eg. given no slice or partition value, with a disk that is
+ * sliced - are they after the first BSD slice, or the DOS
+ * slice before it?)
+ */
+static int
+bd_open(struct open_file *f, ...)
+{
+ va_list ap;
+ struct i386_devdesc *dev;
+ struct open_disk *od;
+ int error;
+
+ va_start(ap, f);
+ dev = va_arg(ap, struct i386_devdesc *);
+ va_end(ap);
+ if ((error = bd_opendisk(&od, dev)))
+ return(error);
+
+ /*
+ * Save our context
+ */
+ ((struct i386_devdesc *)(f->f_devdata))->d_kind.biosdisk.data = od;
+ DEBUG("open_disk %p, partition at 0x%x", od, od->od_boff);
+ return(0);
+}
+
+static int
+bd_opendisk(struct open_disk **odp, struct i386_devdesc *dev)
+{
+ struct dos_partition *dptr;
+ struct disklabel *lp;
+ struct open_disk *od;
+ int sector, slice, i;
+ int error;
+ char buf[BUFSIZE];
+
+ if (dev->d_kind.biosdisk.unit >= nbdinfo) {
+ DEBUG("attempt to open nonexistent disk");
+ return(ENXIO);
+ }
+
+ od = (struct open_disk *)malloc(sizeof(struct open_disk));
+ if (!od) {
+ DEBUG("no memory");
+ return (ENOMEM);
+ }
+
+ /* Look up BIOS unit number, intialise open_disk structure */
+ od->od_dkunit = dev->d_kind.biosdisk.unit;
+ od->od_unit = bdinfo[od->od_dkunit].bd_unit;
+ od->od_flags = bdinfo[od->od_dkunit].bd_flags;
+ od->od_boff = 0;
+ od->od_nslices = 0;
+ error = 0;
+ DEBUG("open '%s', unit 0x%x slice %d partition %c",
+ i386_fmtdev(dev), dev->d_kind.biosdisk.unit,
+ dev->d_kind.biosdisk.slice, dev->d_kind.biosdisk.partition + 'a');
+
+ /* Get geometry for this open (removable device may have changed) */
+ if (bd_getgeom(od)) {
+ DEBUG("can't get geometry");
+ error = ENXIO;
+ goto out;
+ }
+
+ /*
+ * Following calculations attempt to determine the correct value
+ * for d->od_boff by looking for the slice and partition specified,
+ * or searching for reasonable defaults.
+ */
+
+ /*
+ * Find the slice in the DOS slice table.
+ */
+ if (bd_read(od, 0, 1, buf)) {
+ DEBUG("error reading MBR");
+ error = EIO;
+ goto out;
+ }
+
+ /*
+ * Check the slice table magic.
+ */
+ if (((u_char)buf[0x1fe] != 0x55) || ((u_char)buf[0x1ff] != 0xaa)) {
+ /* If a slice number was explicitly supplied, this is an error */
+ if (dev->d_kind.biosdisk.slice > 0) {
+ DEBUG("no slice table/MBR (no magic)");
+ error = ENOENT;
+ goto out;
+ }
+ sector = 0;
+ goto unsliced; /* may be a floppy */
+ }
+
+ /*
+ * copy the partition table, then pick up any extended partitions.
+ */
+ bcopy(buf + DOSPARTOFF, &od->od_slicetab,
+ sizeof(struct dos_partition) * NDOSPART);
+ od->od_nslices = 4; /* extended slices start here */
+ for (i = 0; i < NDOSPART; i++)
+ bd_checkextended(od, i);
+ od->od_flags |= BD_PARTTABOK;
+ dptr = &od->od_slicetab[0];
+
+ /* Is this a request for the whole disk? */
+ if (dev->d_kind.biosdisk.slice == -1) {
+ sector = 0;
+ goto unsliced;
+ }
+
+ /*
+ * if a slice number was supplied but not found, this is an error.
+ */
+ if (dev->d_kind.biosdisk.slice > 0) {
+ slice = dev->d_kind.biosdisk.slice - 1;
+ if (slice >= od->od_nslices) {
+ DEBUG("slice %d not found", slice);
+ error = ENOENT;
+ goto out;
+ }
+ }
+
+ /*
+ * Check for the historically bogus MBR found on true dedicated disks
+ */
+ if ((dptr[3].dp_typ == DOSPTYP_386BSD) &&
+ (dptr[3].dp_start == 0) &&
+ (dptr[3].dp_size == 50000)) {
+ sector = 0;
+ goto unsliced;
+ }
+
+ /* Try to auto-detect the best slice; this should always give a slice number */
+ if (dev->d_kind.biosdisk.slice == 0) {
+ slice = bd_bestslice(od);
+ if (slice == -1) {
+ error = ENOENT;
+ goto out;
+ }
+ dev->d_kind.biosdisk.slice = slice;
+ }
+
+ dptr = &od->od_slicetab[0];
+ /*
+ * Accept the supplied slice number unequivocally (we may be looking
+ * at a DOS partition).
+ */
+ dptr += (dev->d_kind.biosdisk.slice - 1); /* we number 1-4, offsets are 0-3 */
+ sector = dptr->dp_start;
+ DEBUG("slice entry %d at %d, %d sectors", dev->d_kind.biosdisk.slice - 1, sector, dptr->dp_size);
+
+ /*
+ * If we are looking at a BSD slice, and the partition is < 0, assume the 'a' partition
+ */
+ if ((dptr->dp_typ == DOSPTYP_386BSD) && (dev->d_kind.biosdisk.partition < 0))
+ dev->d_kind.biosdisk.partition = 0;
+
+ unsliced:
+ /*
+ * Now we have the slice offset, look for the partition in the disklabel if we have
+ * a partition to start with.
+ *
+ * XXX we might want to check the label checksum.
+ */
+ if (dev->d_kind.biosdisk.partition < 0) {
+ od->od_boff = sector; /* no partition, must be after the slice */
+ DEBUG("opening raw slice");
+ } else {
+
+ if (bd_read(od, sector + LABELSECTOR, 1, buf)) {
+ DEBUG("error reading disklabel");
+ error = EIO;
+ goto out;
+ }
+ DEBUG("copy %d bytes of label from %p to %p", sizeof(struct disklabel), buf + LABELOFFSET, &od->od_disklabel);
+ bcopy(buf + LABELOFFSET, &od->od_disklabel, sizeof(struct disklabel));
+ lp = &od->od_disklabel;
+ od->od_flags |= BD_LABELOK;
+
+ if (lp->d_magic != DISKMAGIC) {
+ DEBUG("no disklabel");
+ error = ENOENT;
+ goto out;
+ }
+ if (dev->d_kind.biosdisk.partition >= lp->d_npartitions) {
+ DEBUG("partition '%c' exceeds partitions in table (a-'%c')",
+ 'a' + dev->d_kind.biosdisk.partition, 'a' + lp->d_npartitions);
+ error = EPART;
+ goto out;
+
+ }
+
+#ifdef DISK_DEBUG
+ /* Complain if the partition is unused unless this is a floppy. */
+ if ((lp->d_partitions[dev->d_kind.biosdisk.partition].p_fstype == FS_UNUSED) &&
+ !(od->od_flags & BD_FLOPPY))
+ DEBUG("warning, partition marked as unused");
+#endif
+
+ od->od_boff =
+ lp->d_partitions[dev->d_kind.biosdisk.partition].p_offset -
+ lp->d_partitions[RAW_PART].p_offset +
+ sector;
+ }
+
+ out:
+ if (error) {
+ free(od);
+ } else {
+ *odp = od; /* return the open disk */
+ }
+ return(error);
+}
+
+static void
+bd_checkextended(struct open_disk *od, int slicenum)
+{
+ char buf[BIOSDISK_SECSIZE];
+ struct dos_partition *dp;
+ u_int base;
+ int i, start, end;
+
+ dp = &od->od_slicetab[slicenum];
+ start = od->od_nslices;
+
+ if (dp->dp_size == 0)
+ goto done;
+ if (dp->dp_typ != DOSPTYP_EXT)
+ goto done;
+ if (bd_read(od, (daddr_t)dp->dp_start, 1, buf))
+ goto done;
+ if (((u_char)buf[0x1fe] != 0x55) || ((u_char)buf[0x1ff] != 0xaa)) {
+ DEBUG("no magic in extended table");
+ goto done;
+ }
+ base = dp->dp_start;
+ dp = (struct dos_partition *)(&buf[DOSPARTOFF]);
+ for (i = 0; i < NDOSPART; i++, dp++) {
+ if (dp->dp_size == 0)
+ continue;
+ if (od->od_nslices == NEXTDOSPART)
+ goto done;
+ dp->dp_start += base;
+ bcopy(dp, &od->od_slicetab[od->od_nslices], sizeof(*dp));
+ od->od_nslices++;
+ }
+ end = od->od_nslices;
+
+ /*
+ * now, recursively check the slices we just added
+ */
+ for (i = start; i < end; i++)
+ bd_checkextended(od, i);
+done:
+ return;
+}
+
+/*
+ * Search for a slice with the following preferences:
+ *
+ * 1: Active FreeBSD slice
+ * 2: Non-active FreeBSD slice
+ * 3: Active Linux slice
+ * 4: non-active Linux slice
+ * 5: Active FAT/FAT32 slice
+ * 6: non-active FAT/FAT32 slice
+ */
+#define PREF_RAWDISK 0
+#define PREF_FBSD_ACT 1
+#define PREF_FBSD 2
+#define PREF_LINUX_ACT 3
+#define PREF_LINUX 4
+#define PREF_DOS_ACT 5
+#define PREF_DOS 6
+#define PREF_NONE 7
+
+/*
+ * slicelimit is in the range 0 .. NDOSPART
+ */
+static int
+bd_bestslice(struct open_disk *od)
+{
+ struct dos_partition *dp;
+ int pref, preflevel;
+ int i, prefslice;
+
+ prefslice = 0;
+ preflevel = PREF_NONE;
+
+ dp = &od->od_slicetab[0];
+ for (i = 0; i < od->od_nslices; i++, dp++) {
+
+ switch (dp->dp_typ) {
+ case DOSPTYP_386BSD: /* FreeBSD */
+ pref = dp->dp_flag & 0x80 ? PREF_FBSD_ACT : PREF_FBSD;
+ break;
+
+ case DOSPTYP_LINUX:
+ pref = dp->dp_flag & 0x80 ? PREF_LINUX_ACT : PREF_LINUX;
+ break;
+
+ case 0x01: /* DOS/Windows */
+ case 0x04:
+ case 0x06:
+ case 0x0b:
+ case 0x0c:
+ case 0x0e:
+ pref = dp->dp_flag & 0x80 ? PREF_DOS_ACT : PREF_DOS;
+ break;
+
+ default:
+ pref = PREF_NONE;
+ }
+ if (pref < preflevel) {
+ preflevel = pref;
+ prefslice = i + 1;
+ }
+ }
+ return (prefslice);
+}
+
+static int
+bd_close(struct open_file *f)
+{
+ struct open_disk *od = (struct open_disk *)(((struct i386_devdesc *)(f->f_devdata))->d_kind.biosdisk.data);
+
+ bd_closedisk(od);
+ return(0);
+}
+
+static void
+bd_closedisk(struct open_disk *od)
+{
+ DEBUG("open_disk %p", od);
+#if 0
+ /* XXX is this required? (especially if disk already open...) */
+ if (od->od_flags & BD_FLOPPY)
+ delay(3000000);
+#endif
+ free(od);
+}
+
+static int
+bd_strategy(void *devdata, int rw, daddr_t dblk, size_t size, char *buf, size_t *rsize)
+{
+ struct bcache_devdata bcd;
+ struct open_disk *od = (struct open_disk *)(((struct i386_devdesc *)devdata)->d_kind.biosdisk.data);
+
+ bcd.dv_strategy = bd_realstrategy;
+ bcd.dv_devdata = devdata;
+ return(bcache_strategy(&bcd, od->od_unit, rw, dblk+od->od_boff, size, buf, rsize));
+}
+
+static int
+bd_realstrategy(void *devdata, int rw, daddr_t dblk, size_t size, char *buf, size_t *rsize)
+{
+ struct open_disk *od = (struct open_disk *)(((struct i386_devdesc *)devdata)->d_kind.biosdisk.data);
+ int blks;
+#ifdef BD_SUPPORT_FRAGS
+ char fragbuf[BIOSDISK_SECSIZE];
+ size_t fragsize;
+
+ fragsize = size % BIOSDISK_SECSIZE;
+#else
+ if (size % BIOSDISK_SECSIZE)
+ panic("bd_strategy: %d bytes I/O not multiple of block size", size);
+#endif
+
+ DEBUG("open_disk %p", od);
+
+
+ switch(rw){
+ case F_READ:
+ blks = size / BIOSDISK_SECSIZE;
+ DEBUG("read %d from %d to %p", blks, dblk, buf);
+
+ if (rsize)
+ *rsize = 0;
+ if (blks && bd_read(od, dblk, blks, buf)) {
+ DEBUG("read error");
+ return (EIO);
+ }
+#ifdef BD_SUPPORT_FRAGS
+ DEBUG("bd_strategy: frag read %d from %d+%d to %p",
+ fragsize, dblk, blks, buf + (blks * BIOSDISK_SECSIZE));
+ if (fragsize && bd_read(od, dblk + blks, 1, fragsize)) {
+ DEBUG("frag read error");
+ return(EIO);
+ }
+ bcopy(fragbuf, buf + (blks * BIOSDISK_SECSIZE), fragsize);
+#endif
+ if (rsize)
+ *rsize = size;
+ return (0);
+ break;
+
+ case F_WRITE :
+ blks = size / BIOSDISK_SECSIZE;
+ DEBUG("write %d from %d to %p", blks, dblk, buf);
+
+ if (rsize)
+ *rsize = 0;
+ if (blks && bd_write(od, dblk, blks, buf)) {
+ DEBUG("write error");
+ return (EIO);
+ }
+#ifdef BD_SUPPORT_FRAGS
+ if(fragsize) {
+ DEBUG("Attempted to write a frag");
+ return (EIO);
+ }
+#endif
+
+ if (rsize)
+ *rsize = size;
+ return (0);
+ default:
+ /* DO NOTHING */
+ }
+
+ return EROFS;
+}
+
+/* Max number of sectors to bounce-buffer if the request crosses a 64k boundary */
+#define FLOPPY_BOUNCEBUF 18
+
+static int
+bd_read(struct open_disk *od, daddr_t dblk, int blks, caddr_t dest)
+{
+ u_int x, bpc, cyl, hd, sec, result, resid, retry, maxfer;
+ caddr_t p, xp, bbuf, breg;
+
+ /* Just in case some idiot actually tries to read -1 blocks... */
+ if (blks < 0)
+ return (-1);
+
+ bpc = (od->od_sec * od->od_hds); /* blocks per cylinder */
+ resid = blks;
+ p = dest;
+
+ /* Decide whether we have to bounce */
+ if ((od->od_unit < 0x80) &&
+ ((VTOP(dest) >> 16) != (VTOP(dest + blks * BIOSDISK_SECSIZE) >> 16))) {
+
+ /*
+ * There is a 64k physical boundary somewhere in the destination buffer, so we have
+ * to arrange a suitable bounce buffer. Allocate a buffer twice as large as we
+ * need to. Use the bottom half unless there is a break there, in which case we
+ * use the top half.
+ */
+ x = min(FLOPPY_BOUNCEBUF, (unsigned)blks);
+ bbuf = malloc(x * 2 * BIOSDISK_SECSIZE);
+ if (((u_int32_t)VTOP(bbuf) & 0xffff0000) == ((u_int32_t)VTOP(dest + x * BIOSDISK_SECSIZE) & 0xffff0000)) {
+ breg = bbuf;
+ } else {
+ breg = bbuf + x * BIOSDISK_SECSIZE;
+ }
+ maxfer = x; /* limit transfers to bounce region size */
+ } else {
+ breg = bbuf = NULL;
+ maxfer = 0;
+ }
+
+ while (resid > 0) {
+ x = dblk;
+ cyl = x / bpc; /* block # / blocks per cylinder */
+ x %= bpc; /* block offset into cylinder */
+ hd = x / od->od_sec; /* offset / blocks per track */
+ sec = x % od->od_sec; /* offset into track */
+
+ /* play it safe and don't cross track boundaries (XXX this is probably unnecessary) */
+ x = min(od->od_sec - sec, resid);
+ if (maxfer > 0)
+ x = min(x, maxfer); /* fit bounce buffer */
+
+ /* where do we transfer to? */
+ xp = bbuf == NULL ? p : breg;
+
+ /* correct sector number for 1-based BIOS numbering */
+ sec++;
+
+ /* 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 = 0x13;
+ v86.eax = 0;
+ v86.edx = od->od_unit;
+ v86int();
+ }
+
+ if(cyl > 1023) {
+ /* use EDD if the disk supports it, otherwise, return error */
+ if(od->od_flags & BD_MODEEDD1) {
+ static unsigned short packet[8];
+
+ packet[0] = 0x10;
+ packet[1] = x;
+ packet[2] = VTOPOFF(xp);
+ packet[3] = VTOPSEG(xp);
+ packet[4] = dblk & 0xffff;
+ packet[5] = dblk >> 16;
+ packet[6] = 0;
+ packet[7] = 0;
+ v86.ctl = V86_FLAGS;
+ v86.addr = 0x13;
+ v86.eax = 0x4200;
+ v86.edx = od->od_unit;
+ v86.ds = VTOPSEG(packet);
+ v86.esi = VTOPOFF(packet);
+ v86int();
+ result = (v86.efl & 0x1);
+ if(result == 0)
+ break;
+ } else {
+ result = 1;
+ break;
+ }
+ } else {
+ /* Use normal CHS addressing */
+ v86.ctl = V86_FLAGS;
+ v86.addr = 0x13;
+ v86.eax = 0x200 | x;
+ v86.ecx = ((cyl & 0xff) << 8) | ((cyl & 0x300) >> 2) | sec;
+ v86.edx = (hd << 8) | od->od_unit;
+ v86.es = VTOPSEG(xp);
+ v86.ebx = VTOPOFF(xp);
+ v86int();
+ result = (v86.efl & 0x1);
+ if (result == 0)
+ break;
+ }
+ }
+
+ DEBUG("%d sectors from %d/%d/%d to %p (0x%x) %s", x, cyl, hd, sec - 1, p, VTOP(p), result ? "failed" : "ok");
+ /* BUG here, cannot use v86 in printf because putchar uses it too */
+ DEBUG("ax = 0x%04x cx = 0x%04x dx = 0x%04x status 0x%x",
+ 0x200 | x, ((cyl & 0xff) << 8) | ((cyl & 0x300) >> 2) | sec, (hd << 8) | od->od_unit, (v86.eax >> 8) & 0xff);
+ if (result) {
+ if (bbuf != NULL)
+ free(bbuf);
+ return(-1);
+ }
+ if (bbuf != NULL)
+ bcopy(breg, p, x * BIOSDISK_SECSIZE);
+ p += (x * BIOSDISK_SECSIZE);
+ dblk += x;
+ resid -= x;
+ }
+
+/* hexdump(dest, (blks * BIOSDISK_SECSIZE)); */
+ if (bbuf != NULL)
+ free(bbuf);
+ return(0);
+}
+
+
+static int
+bd_write(struct open_disk *od, daddr_t dblk, int blks, caddr_t dest)
+{
+ u_int x, bpc, cyl, hd, sec, result, resid, retry, maxfer;
+ caddr_t p, xp, bbuf, breg;
+
+ /* Just in case some idiot actually tries to read -1 blocks... */
+ if (blks < 0)
+ return (-1);
+
+ bpc = (od->od_sec * od->od_hds); /* blocks per cylinder */
+ resid = blks;
+ p = dest;
+
+ /* Decide whether we have to bounce */
+ if ((od->od_unit < 0x80) &&
+ ((VTOP(dest) >> 16) != (VTOP(dest + blks * BIOSDISK_SECSIZE) >> 16))) {
+
+ /*
+ * There is a 64k physical boundary somewhere in the destination buffer, so we have
+ * to arrange a suitable bounce buffer. Allocate a buffer twice as large as we
+ * need to. Use the bottom half unless there is a break there, in which case we
+ * use the top half.
+ */
+
+ x = min(FLOPPY_BOUNCEBUF, (unsigned)blks);
+ bbuf = malloc(x * 2 * BIOSDISK_SECSIZE);
+ if (((u_int32_t)VTOP(bbuf) & 0xffff0000) == ((u_int32_t)VTOP(dest + x * BIOSDISK_SECSIZE) & 0xffff0000)) {
+ breg = bbuf;
+ } else {
+ breg = bbuf + x * BIOSDISK_SECSIZE;
+ }
+ maxfer = x; /* limit transfers to bounce region size */
+ } else {
+ breg = bbuf = NULL;
+ maxfer = 0;
+ }
+
+ while (resid > 0) {
+ x = dblk;
+ cyl = x / bpc; /* block # / blocks per cylinder */
+ x %= bpc; /* block offset into cylinder */
+ hd = x / od->od_sec; /* offset / blocks per track */
+ sec = x % od->od_sec; /* offset into track */
+
+ /* play it safe and don't cross track boundaries (XXX this is probably unnecessary) */
+ x = min(od->od_sec - sec, resid);
+ if (maxfer > 0)
+ x = min(x, maxfer); /* fit bounce buffer */
+
+ /* where do we transfer to? */
+ xp = bbuf == NULL ? p : breg;
+
+ /* correct sector number for 1-based BIOS numbering */
+ sec++;
+
+
+ /* Put your Data In, Put your Data out,
+ Put your Data In, and shake it all about
+ */
+ if (bbuf != NULL)
+ bcopy(p, breg, x * BIOSDISK_SECSIZE);
+ p += (x * BIOSDISK_SECSIZE);
+ dblk += x;
+ resid -= x;
+
+ /* 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 = 0x13;
+ v86.eax = 0;
+ v86.edx = od->od_unit;
+ v86int();
+ }
+
+ if(cyl > 1023) {
+ /* use EDD if the disk supports it, otherwise, return error */
+ if(od->od_flags & BD_MODEEDD1) {
+ static unsigned short packet[8];
+
+ packet[0] = 0x10;
+ packet[1] = x;
+ packet[2] = VTOPOFF(xp);
+ packet[3] = VTOPSEG(xp);
+ packet[4] = dblk & 0xffff;
+ packet[5] = dblk >> 16;
+ packet[6] = 0;
+ packet[7] = 0;
+ v86.ctl = V86_FLAGS;
+ v86.addr = 0x13;
+ /* Should we Write with verify ?? 0x4302 ? */
+ v86.eax = 0x4300;
+ v86.edx = od->od_unit;
+ v86.ds = VTOPSEG(packet);
+ v86.esi = VTOPOFF(packet);
+ v86int();
+ result = (v86.efl & 0x1);
+ if(result == 0)
+ break;
+ } else {
+ result = 1;
+ break;
+ }
+ } else {
+ /* Use normal CHS addressing */
+ v86.ctl = V86_FLAGS;
+ v86.addr = 0x13;
+ v86.eax = 0x300 | x;
+ v86.ecx = ((cyl & 0xff) << 8) | ((cyl & 0x300) >> 2) | sec;
+ v86.edx = (hd << 8) | od->od_unit;
+ v86.es = VTOPSEG(xp);
+ v86.ebx = VTOPOFF(xp);
+ v86int();
+ result = (v86.efl & 0x1);
+ if (result == 0)
+ break;
+ }
+ }
+
+ DEBUG("%d sectors from %d/%d/%d to %p (0x%x) %s", x, cyl, hd, sec - 1, p, VTOP(p), result ? "failed" : "ok");
+ /* BUG here, cannot use v86 in printf because putchar uses it too */
+ DEBUG("ax = 0x%04x cx = 0x%04x dx = 0x%04x status 0x%x",
+ 0x200 | x, ((cyl & 0xff) << 8) | ((cyl & 0x300) >> 2) | sec, (hd << 8) | od->od_unit, (v86.eax >> 8) & 0xff);
+ if (result) {
+ if (bbuf != NULL)
+ free(bbuf);
+ return(-1);
+ }
+ }
+
+/* hexdump(dest, (blks * BIOSDISK_SECSIZE)); */
+ if (bbuf != NULL)
+ free(bbuf);
+ return(0);
+}
+static int
+bd_getgeom(struct open_disk *od)
+{
+
+ v86.ctl = V86_FLAGS;
+ v86.addr = 0x13;
+ v86.eax = 0x800;
+ v86.edx = od->od_unit;
+ v86int();
+
+ if ((v86.efl & 0x1) || /* carry set */
+ ((v86.edx & 0xff) <= (unsigned)(od->od_unit & 0x7f))) /* unit # bad */
+ return(1);
+
+ /* convert max cyl # -> # of cylinders */
+ od->od_cyl = ((v86.ecx & 0xc0) << 2) + ((v86.ecx & 0xff00) >> 8) + 1;
+ /* convert max head # -> # of heads */
+ od->od_hds = ((v86.edx & 0xff00) >> 8) + 1;
+ od->od_sec = v86.ecx & 0x3f;
+
+ DEBUG("unit 0x%x geometry %d/%d/%d", od->od_unit, od->od_cyl, od->od_hds, od->od_sec);
+ return(0);
+}
+
+/*
+ * Return the BIOS geometry of a given "fixed drive" in a format
+ * suitable for the legacy bootinfo structure. Since the kernel is
+ * expecting raw int 0x13/0x8 values for N_BIOS_GEOM drives, we
+ * prefer to get the information directly, rather than rely on being
+ * able to put it together from information already maintained for
+ * different purposes and for a probably different number of drives.
+ *
+ * For valid drives, the geometry is expected in the format (31..0)
+ * "000000cc cccccccc hhhhhhhh 00ssssss"; and invalid drives are
+ * indicated by returning the geometry of a "1.2M" PC-format floppy
+ * disk. And, incidentally, what is returned is not the geometry as
+ * such but the highest valid cylinder, head, and sector numbers.
+ */
+u_int32_t
+bd_getbigeom(int bunit)
+{
+
+ v86.ctl = V86_FLAGS;
+ v86.addr = 0x13;
+ v86.eax = 0x800;
+ v86.edx = 0x80 + bunit;
+ v86int();
+ if (v86.efl & 0x1)
+ return 0x4f010f;
+ return ((v86.ecx & 0xc0) << 18) | ((v86.ecx & 0xff00) << 8) |
+ (v86.edx & 0xff00) | (v86.ecx & 0x3f);
+}
+
+/*
+ * Return a suitable dev_t value for (dev).
+ *
+ * In the case where it looks like (dev) is a SCSI disk, we allow the number of
+ * IDE disks to be specified in $num_ide_disks. There should be a Better Way.
+ */
+int
+bd_getdev(struct i386_devdesc *dev)
+{
+ struct open_disk *od;
+ int biosdev;
+ int major;
+ int rootdev;
+ char *nip, *cp;
+ int unitofs = 0, i, unit;
+
+ biosdev = bd_unit2bios(dev->d_kind.biosdisk.unit);
+ DEBUG("unit %d BIOS device %d", dev->d_kind.biosdisk.unit, biosdev);
+ if (biosdev == -1) /* not a BIOS device */
+ return(-1);
+ if (bd_opendisk(&od, dev) != 0) /* oops, not a viable device */
+ return(-1);
+
+ if (biosdev < 0x80) {
+ /* floppy (or emulated floppy) or ATAPI device */
+ if (bdinfo[dev->d_kind.biosdisk.unit].bd_type == DT_ATAPI) {
+ /* is an ATAPI disk */
+ major = WFDMAJOR;
+ } else {
+ /* is a floppy disk */
+ major = FDMAJOR;
+ }
+ } else {
+ /* harddisk */
+ if ((od->od_flags & BD_LABELOK) && (od->od_disklabel.d_type == DTYPE_SCSI)) {
+ /* label OK, disk labelled as SCSI */
+ major = DAMAJOR;
+ /* check for unit number correction hint, now deprecated */
+ if ((nip = getenv("num_ide_disks")) != NULL) {
+ i = strtol(nip, &cp, 0);
+ /* check for parse error */
+ if ((cp != nip) && (*cp == 0))
+ unitofs = i;
+ }
+ } else {
+ /* assume an IDE disk */
+ major = WDMAJOR;
+ }
+ }
+ /* default root disk unit number */
+ unit = (biosdev & 0x7f) - unitofs;
+
+ /* XXX a better kludge to set the root disk unit number */
+ if ((nip = getenv("root_disk_unit")) != NULL) {
+ i = strtol(nip, &cp, 0);
+ /* check for parse error */
+ if ((cp != nip) && (*cp == 0))
+ unit = i;
+ }
+
+ rootdev = MAKEBOOTDEV(major,
+ (dev->d_kind.biosdisk.slice + 1) >> 4, /* XXX slices may be wrong here */
+ (dev->d_kind.biosdisk.slice + 1) & 0xf,
+ unit,
+ dev->d_kind.biosdisk.partition);
+ DEBUG("dev is 0x%x\n", rootdev);
+ return(rootdev);
+}
diff --git a/sys/boot/i386/libi386/biosmem.c b/sys/boot/i386/libi386/biosmem.c
new file mode 100644
index 0000000..019cf90
--- /dev/null
+++ b/sys/boot/i386/libi386/biosmem.c
@@ -0,0 +1,107 @@
+/*-
+ * Copyright (c) 1998 Michael Smith <msmith@freebsd.org>
+ * 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 <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+/*
+ * Obtain memory configuration information from the BIOS
+ */
+#include <stand.h>
+#include "libi386.h"
+#include "btxv86.h"
+
+vm_offset_t memtop;
+u_int32_t bios_basemem, bios_extmem;
+
+#define SMAPSIG 0x534D4150
+
+struct smap {
+ u_int64_t base;
+ u_int64_t length;
+ u_int32_t type;
+} __packed;
+
+static struct smap smap;
+
+void
+bios_getmem(void)
+{
+
+ /* Parse system memory map */
+ v86.ebx = 0;
+ do {
+ v86.ctl = V86_FLAGS;
+ v86.addr = 0x15; /* int 0x15 function 0xe820*/
+ v86.eax = 0xe820;
+ v86.ecx = sizeof(struct smap);
+ v86.edx = SMAPSIG;
+ v86.es = VTOPSEG(&smap);
+ v86.edi = VTOPOFF(&smap);
+ v86int();
+ if ((v86.efl & 1) || (v86.eax != SMAPSIG))
+ break;
+ /* look for a low-memory segment that's large enough */
+ if ((smap.type == 1) && (smap.base == 0) && (smap.length >= (512 * 1024)))
+ bios_basemem = smap.length;
+ /* look for the first segment in 'extended' memory */
+ if ((smap.type == 1) && (smap.base == 0x100000)) {
+ bios_extmem = smap.length;
+ }
+ } while (v86.ebx != 0);
+
+ /* Fall back to the old compatibility function for base memory */
+ if (bios_basemem == 0) {
+ v86.ctl = 0;
+ v86.addr = 0x12; /* int 0x12 */
+ v86int();
+
+ bios_basemem = (v86.eax & 0xffff) * 1024;
+ }
+
+ /* Fall back through several compatibility functions for extended memory */
+ if (bios_extmem == 0) {
+ v86.ctl = V86_FLAGS;
+ v86.addr = 0x15; /* int 0x15 function 0xe801*/
+ v86.eax = 0xe801;
+ v86int();
+ if (!(v86.efl & 1)) {
+ bios_extmem = ((v86.ecx & 0xffff) + ((v86.edx & 0xffff) * 64)) * 1024;
+ }
+ }
+ if (bios_extmem == 0) {
+ v86.ctl = 0;
+ v86.addr = 0x15; /* int 0x15 function 0x88*/
+ v86.eax = 0x8800;
+ v86int();
+ bios_extmem = (v86.eax & 0xffff) * 1024;
+ }
+
+ /* Set memtop to actual top of memory */
+ memtop = 0x100000 + bios_extmem;
+
+}
+
diff --git a/sys/boot/i386/libi386/biospci.c b/sys/boot/i386/libi386/biospci.c
new file mode 100644
index 0000000..5717bdc
--- /dev/null
+++ b/sys/boot/i386/libi386/biospci.c
@@ -0,0 +1,294 @@
+/*-
+ * Copyright (c) 1998 Michael Smith <msmith@freebsd.org>
+ * 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 <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+/*
+ * PnP enumerator using the PCI BIOS.
+ */
+
+#include <stand.h>
+#include <machine/stdarg.h>
+#include <bootstrap.h>
+#include <isapnp.h>
+#include <btxv86.h>
+
+/*
+ * Stupid PCI BIOS interface doesn't let you simply enumerate everything
+ * that's there, instead you have to ask it if it has something.
+ *
+ * So we have to scan by class code, subclass code and sometimes programming
+ * interface.
+ */
+
+struct pci_progif
+{
+ int pi_code;
+ const char *pi_name;
+};
+
+static struct pci_progif progif_null[] = {
+ {0x0, NULL},
+ {-1, NULL}
+};
+
+static struct pci_progif progif_display[] = {
+ {0x0, "VGA"},
+ {0x1, "8514"},
+ {-1, NULL}
+};
+
+static struct pci_progif progif_ide[] = {
+ {0x00, NULL},
+ {0x01, NULL},
+ {0x02, NULL},
+ {0x03, NULL},
+ {0x04, NULL},
+ {0x05, NULL},
+ {0x06, NULL},
+ {0x07, NULL},
+ {0x08, NULL},
+ {0x09, NULL},
+ {0x0a, NULL},
+ {0x0b, NULL},
+ {0x0c, NULL},
+ {0x0d, NULL},
+ {0x0e, NULL},
+ {0x0f, NULL},
+ {0x80, NULL},
+ {0x81, NULL},
+ {0x82, NULL},
+ {0x83, NULL},
+ {0x84, NULL},
+ {0x85, NULL},
+ {0x86, NULL},
+ {0x87, NULL},
+ {0x88, NULL},
+ {0x89, NULL},
+ {0x8a, NULL},
+ {0x8b, NULL},
+ {0x8c, NULL},
+ {0x8d, NULL},
+ {0x8e, NULL},
+ {0x8f, NULL},
+ {-1, NULL}
+};
+
+static struct pci_progif progif_serial[] = {
+ {0x0, "8250"},
+ {0x1, "16450"},
+ {0x2, "16550"},
+ {-1, NULL}
+};
+
+static struct pci_progif progif_parallel[] = {
+ {0x0, "Standard"},
+ {0x1, "Bidirectional"},
+ {0x2, "ECP"},
+ {-1, NULL}
+};
+
+
+struct pci_subclass
+{
+ int ps_subclass;
+ const char *ps_name;
+ struct pci_progif *ps_progif; /* if set, use for programming interface value(s) */
+};
+
+static struct pci_subclass subclass_old[] = {
+ {0x0, "Old non-VGA", progif_null},
+ {0x1, "Old VGA", progif_null},
+ {-1, NULL, NULL}
+};
+
+static struct pci_subclass subclass_mass[] = {
+ {0x0, "SCSI", progif_null},
+ {0x1, "IDE", progif_ide},
+ {0x2, "Floppy disk", progif_null},
+ {0x3, "IPI", progif_null},
+ {0x4, "RAID", progif_null},
+ {0x80, "mass storage", progif_null},
+ {-1, NULL, NULL}
+};
+
+static struct pci_subclass subclass_net[] = {
+ {0x0, "Ethernet", progif_null},
+ {0x1, "Token ring", progif_null},
+ {0x2, "FDDI", progif_null},
+ {0x3, "ATM", progif_null},
+ {0x80, "network", progif_null},
+ {-1, NULL, NULL}
+};
+
+static struct pci_subclass subclass_display[] = {
+ {0x0, NULL, progif_display},
+ {0x1, "XGA", progif_null},
+ {0x80, "other", progif_null},
+ {-1, NULL, NULL}
+};
+
+static struct pci_subclass subclass_comms[] = {
+ {0x0, "serial", progif_serial},
+ {0x1, "parallel", progif_parallel},
+ {0x80, "communications", progif_null},
+ {-1, NULL, NULL}
+};
+
+static struct pci_subclass subclass_serial[] = {
+ {0x0, "Firewire", progif_null},
+ {0x1, "ACCESS.bus", progif_null},
+ {0x2, "SSA", progif_null},
+ {0x3, "USB", progif_null},
+ {0x4, "Fibrechannel", progif_null},
+ {-1, NULL, NULL}
+};
+
+static struct pci_class
+{
+ int pc_class;
+ const char *pc_name;
+ struct pci_subclass *pc_subclass;
+} pci_classes[] = {
+ {0x0, "device", subclass_old},
+ {0x1, "controller", subclass_mass},
+ {0x2, "controller", subclass_net},
+ {0x3, "display", subclass_display},
+ {0x7, "controller", subclass_comms},
+ {0xc, "controller", subclass_serial},
+ {-1, NULL, NULL}
+};
+
+
+static void biospci_enumerate(void);
+static void biospci_addinfo(int devid, struct pci_class *pc, struct pci_subclass *psc, struct pci_progif *ppi);
+
+static int biospci_version;
+static int biospci_hwcap;
+
+struct pnphandler biospcihandler =
+{
+ "PCI BIOS",
+ biospci_enumerate
+};
+
+static void
+biospci_enumerate(void)
+{
+ int device_index, locator, devid;
+ struct pci_class *pc;
+ struct pci_subclass *psc;
+ struct pci_progif *ppi;
+
+ /* Find the PCI BIOS */
+ v86.ctl = V86_FLAGS;
+ v86.addr = 0x1a;
+ v86.eax = 0xb101;
+ v86.edi = 0x0;
+ v86int();
+
+ /* Check for OK response */
+ if ((v86.efl & 1) || ((v86.eax & 0xff00) != 0) || (v86.edx != 0x20494350))
+ return;
+
+ biospci_version = v86.ebx & 0xffff;
+ biospci_hwcap = v86.eax & 0xff;
+#if 0
+ printf("PCI BIOS %d.%d%s%s\n",
+ bcd2bin((biospci_version >> 8) & 0xf), bcd2bin(biospci_version & 0xf),
+ (biospci_hwcap & 1) ? " config1" : "", (biospci_hwcap & 2) ? " config2" : "");
+#endif
+ /* Iterate over known classes */
+ for (pc = pci_classes; pc->pc_class >= 0; pc++) {
+ /* Iterate over subclasses */
+ for (psc = pc->pc_subclass; psc->ps_subclass >= 0; psc++) {
+ /* Iterate over programming interfaces */
+ for (ppi = psc->ps_progif; ppi->pi_code >= 0; ppi++) {
+
+ /* Scan for matches */
+ for (device_index = 0; ; device_index++) {
+
+ /* Look for a match */
+ v86.ctl = V86_FLAGS;
+ v86.addr = 0x1a;
+ v86.eax = 0xb103;
+ v86.ecx = (pc->pc_class << 16) + (psc->ps_subclass << 8) + ppi->pi_code;
+ v86.esi = device_index;
+ v86int();
+ /* error/end of matches */
+ if ((v86.efl & 1) || (v86.eax & 0xff00))
+ break;
+
+ /* Got something */
+ locator = v86.ebx;
+
+ /* Read the device identifier from the nominated device */
+ v86.ctl = V86_FLAGS;
+ v86.addr = 0x1a;
+ v86.eax = 0xb10a;
+ v86.ebx = locator;
+ v86.edi = 0x0;
+ v86int();
+ /* error */
+ if ((v86.efl & 1) || (v86.eax & 0xff00))
+ break;
+
+ /* We have the device ID, create a PnP object and save everything */
+ devid = v86.ecx;
+ biospci_addinfo(devid, pc, psc, ppi);
+ }
+ }
+ }
+ }
+}
+
+static void
+biospci_addinfo(int devid, struct pci_class *pc, struct pci_subclass *psc, struct pci_progif *ppi)
+{
+ struct pnpinfo *pi;
+ char desc[80];
+
+
+ /* build the description */
+ desc[0] = 0;
+ if (ppi->pi_name != NULL) {
+ strcat(desc, ppi->pi_name);
+ strcat(desc, " ");
+ }
+ if (psc->ps_name != NULL) {
+ strcat(desc, psc->ps_name);
+ strcat(desc, " ");
+ }
+ if (pc->pc_name != NULL)
+ strcat(desc, pc->pc_name);
+
+ pi = pnp_allocinfo();
+ pi->pi_desc = strdup(desc);
+ sprintf(desc,"0x%08x", devid);
+ pnp_addident(pi, desc);
+ pnp_addinfo(pi);
+}
diff --git a/sys/boot/i386/libi386/biospnp.c b/sys/boot/i386/libi386/biospnp.c
new file mode 100644
index 0000000..b6e765a
--- /dev/null
+++ b/sys/boot/i386/libi386/biospnp.c
@@ -0,0 +1,291 @@
+/*-
+ * Copyright (c) 1998 Michael Smith <msmith@freebsd.org>
+ * 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 <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+/*
+ * PnP BIOS enumerator.
+ */
+
+#include <stand.h>
+#include <machine/stdarg.h>
+#include <bootstrap.h>
+#include <isapnp.h>
+#include <btxv86.h>
+
+
+static int biospnp_init(void);
+static void biospnp_enumerate(void);
+
+struct pnphandler biospnphandler =
+{
+ "PnP BIOS",
+ biospnp_enumerate
+};
+
+struct pnp_ICstructure
+{
+ u_int8_t pnp_signature[4] __packed;
+ u_int8_t pnp_version __packed;
+ u_int8_t pnp_length __packed;
+ u_int16_t pnp_BIOScontrol __packed;
+ u_int8_t pnp_checksum __packed;
+ u_int32_t pnp_eventflag __packed;
+ u_int16_t pnp_rmip __packed;
+ u_int16_t pnp_rmcs __packed;
+ u_int16_t pnp_pmip __packed;
+ u_int32_t pnp_pmcs __packed;
+ u_int8_t pnp_OEMdev[4] __packed;
+ u_int16_t pnp_rmds __packed;
+ u_int32_t pnp_pmds __packed;
+};
+
+struct pnp_devNode
+{
+ u_int16_t dn_size __packed;
+ u_int8_t dn_handle __packed;
+ u_int8_t dn_id[4] __packed;
+ u_int8_t dn_type[3] __packed;
+ u_int16_t dn_attrib __packed;
+ u_int8_t dn_data[1] __packed;
+};
+
+struct pnp_isaConfiguration
+{
+ u_int8_t ic_revision __packed;
+ u_int8_t ic_nCSN __packed;
+ u_int16_t ic_rdport __packed;
+ u_int16_t ic_reserved __packed;
+};
+
+static struct pnp_ICstructure *pnp_Icheck = NULL;
+static u_int16_t pnp_NumNodes;
+static u_int16_t pnp_NodeSize;
+
+static void biospnp_scanresdata(struct pnpinfo *pi, struct pnp_devNode *dn);
+static int biospnp_call(int func, const char *fmt, ...);
+
+#define vsegofs(vptr) (((u_int32_t)VTOPSEG(vptr) << 16) + VTOPOFF(vptr))
+
+typedef void v86bios_t(u_int32_t, u_int32_t, u_int32_t, u_int32_t);
+v86bios_t *v86bios = (v86bios_t *)v86int;
+
+#define biospnp_f00(NumNodes, NodeSize) biospnp_call(0x00, "ll", NumNodes, NodeSize)
+#define biospnp_f01(Node, devNodeBuffer, Control) biospnp_call(0x01, "llw", Node, devNodeBuffer, Control)
+#define biospnp_f40(Configuration) biospnp_call(0x40, "l", Configuration)
+
+/* PnP BIOS return codes */
+#define PNP_SUCCESS 0x00
+#define PNP_FUNCTION_NOT_SUPPORTED 0x80
+
+/*
+ * Initialisation: locate the PnP BIOS, test that we can call it.
+ * Returns nonzero if the PnP BIOS is not usable on this system.
+ */
+static int
+biospnp_init(void)
+{
+ struct pnp_isaConfiguration icfg;
+ char *sigptr;
+ int result;
+
+ /* Search for the $PnP signature */
+ pnp_Icheck = NULL;
+ for (sigptr = PTOV(0xf0000); sigptr < PTOV(0xfffff); sigptr += 16)
+ if (!bcmp(sigptr, "$PnP", 4)) {
+ pnp_Icheck = (struct pnp_ICstructure *)sigptr;
+ break;
+ }
+
+ /* No signature, no BIOS */
+ if (pnp_Icheck == NULL)
+ return(1);
+
+ /*
+ * Fetch the system table parameters as a test of the BIOS
+ */
+ result = biospnp_f00(vsegofs(&pnp_NumNodes), vsegofs(&pnp_NodeSize));
+ if (result != PNP_SUCCESS) {
+ return(1);
+ }
+
+ /*
+ * Look for the PnP ISA configuration table
+ */
+ result = biospnp_f40(vsegofs(&icfg));
+ switch (result) {
+ case PNP_SUCCESS:
+ /* If the BIOS found some PnP devices, take its hint for the read port */
+ if ((icfg.ic_revision == 1) && (icfg.ic_nCSN > 0))
+ isapnp_readport = icfg.ic_rdport;
+ break;
+ case PNP_FUNCTION_NOT_SUPPORTED:
+ /* The BIOS says there is no ISA bus (should we trust that this works?) */
+ printf("PnP BIOS claims no ISA bus\n");
+ isapnp_readport = -1;
+ break;
+ }
+ return(0);
+}
+
+static void
+biospnp_enumerate(void)
+{
+ u_int8_t Node;
+ struct pnp_devNode *devNodeBuffer;
+ int result;
+ struct pnpinfo *pi;
+ int count;
+
+ /* Init/check state */
+ if (biospnp_init())
+ return;
+
+ devNodeBuffer = (struct pnp_devNode *)malloc(pnp_NodeSize);
+ Node = 0;
+ count = 1000;
+ while((Node != 0xff) && (count-- > 0)) {
+ result = biospnp_f01(vsegofs(&Node), vsegofs(devNodeBuffer), 0x1);
+ if (result != PNP_SUCCESS) {
+ printf("PnP BIOS node %d: error 0x%x\n", Node, result);
+ } else {
+ pi = pnp_allocinfo();
+ pnp_addident(pi, pnp_eisaformat(devNodeBuffer->dn_id));
+ biospnp_scanresdata(pi, devNodeBuffer);
+ pnp_addinfo(pi);
+ }
+ }
+}
+
+/*
+ * Scan the resource data in the node's data area for compatible device IDs
+ * and descriptions.
+ */
+static void
+biospnp_scanresdata(struct pnpinfo *pi, struct pnp_devNode *dn)
+{
+ u_int tag, i, rlen, dlen;
+ u_int8_t *p;
+ char *str;
+
+ p = dn->dn_data; /* point to resource data */
+ dlen = dn->dn_size - (p - (u_int8_t *)dn); /* length of resource data */
+
+ for (i = 0; i < dlen; i+= rlen) {
+ tag = p[i];
+ i++;
+ if (PNP_RES_TYPE(tag) == 0) {
+ rlen = PNP_SRES_LEN(tag);
+ /* small resource */
+ switch (PNP_SRES_NUM(tag)) {
+
+ case COMP_DEVICE_ID:
+ /* got a compatible device ID */
+ pnp_addident(pi, pnp_eisaformat(p + i));
+ break;
+
+ case END_TAG:
+ return;
+ }
+ } else {
+ /* large resource */
+ rlen = *(u_int16_t *)(p + i);
+ i += sizeof(u_int16_t);
+
+ switch(PNP_LRES_NUM(tag)) {
+
+ case ID_STRING_ANSI:
+ str = malloc(rlen + 1);
+ bcopy(p + i, str, rlen);
+ str[rlen] = 0;
+ if (pi->pi_desc == NULL) {
+ pi->pi_desc = str;
+ } else {
+ free(str);
+ }
+ break;
+ }
+ }
+ }
+}
+
+
+/*
+ * Make a 16-bit realmode PnP BIOS call.
+ *
+ * The first argument passed is the function number, the last is the
+ * BIOS data segment selector. Intermediate arguments may be 16 or
+ * 32 bytes in length, and are described by the format string.
+ *
+ * Arguments to the BIOS functions must be packed on the stack, hence
+ * this evil.
+ */
+static int
+biospnp_call(int func, const char *fmt, ...)
+{
+ va_list ap;
+ const char *p;
+ u_int8_t *argp;
+ u_int32_t args[4];
+ u_int32_t i;
+
+ /* function number first */
+ argp = (u_int8_t *)args;
+ *(u_int16_t *)argp = func;
+ argp += sizeof(u_int16_t);
+
+ /* take args according to format */
+ va_start(ap, fmt);
+ for (p = fmt; *p != 0; p++) {
+ switch(*p) {
+
+ case 'w':
+ i = va_arg(ap, u_int);
+ *(u_int16_t *)argp = i;
+ argp += sizeof(u_int16_t);
+ break;
+
+ case 'l':
+ i = va_arg(ap, u_int32_t);
+ *(u_int32_t *)argp = i;
+ argp += sizeof(u_int32_t);
+ break;
+ }
+ }
+
+ /* BIOS segment last */
+ *(u_int16_t *)argp = pnp_Icheck->pnp_rmds;
+ argp += sizeof(u_int16_t);
+
+ /* prepare for call */
+ v86.ctl = V86_ADDR | V86_CALLF;
+ v86.addr = ((u_int32_t)pnp_Icheck->pnp_rmcs << 16) + pnp_Icheck->pnp_rmip;
+
+ /* call with packed stack and return */
+ v86bios(args[0], args[1], args[2], args[3]);
+ return(v86.eax & 0xffff);
+}
diff --git a/sys/boot/i386/libi386/biossmap.c b/sys/boot/i386/libi386/biossmap.c
new file mode 100644
index 0000000..2859a56
--- /dev/null
+++ b/sys/boot/i386/libi386/biossmap.c
@@ -0,0 +1,108 @@
+/*-
+ * Copyright (c) 1998 Michael Smith <msmith@freebsd.org>
+ * 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 <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+/*
+ * Obtain memory configuration information from the BIOS
+ */
+#include <stand.h>
+#include <sys/param.h>
+#include <sys/linker.h>
+#include <machine/metadata.h>
+#include "bootstrap.h"
+#include "libi386.h"
+#include "btxv86.h"
+
+#define SMAPSIG 0x534D4150
+
+struct smap {
+ u_int64_t base;
+ u_int64_t length;
+ u_int32_t type;
+} __packed;
+
+static struct smap smap;
+
+static struct smap *smapbase;
+static int smaplen;
+
+void
+bios_getsmap(void)
+{
+ int n;
+
+ n = 0;
+ smaplen = 0;
+ /* Count up segments in system memory map */
+ v86.ebx = 0;
+ do {
+ v86.ctl = V86_FLAGS;
+ v86.addr = 0x15; /* int 0x15 function 0xe820*/
+ v86.eax = 0xe820;
+ v86.ecx = sizeof(struct smap);
+ v86.edx = SMAPSIG;
+ v86.es = VTOPSEG(&smap);
+ v86.edi = VTOPOFF(&smap);
+ v86int();
+ if ((v86.efl & 1) || (v86.eax != SMAPSIG))
+ break;
+ n++;
+ } while (v86.ebx != 0);
+ if (n == 0)
+ return;
+ n += 10; /* spare room */
+ smapbase = malloc(n * sizeof(*smapbase));
+
+ /* Save system memory map */
+ v86.ebx = 0;
+ do {
+ v86.ctl = V86_FLAGS;
+ v86.addr = 0x15; /* int 0x15 function 0xe820*/
+ v86.eax = 0xe820;
+ v86.ecx = sizeof(struct smap);
+ v86.edx = SMAPSIG;
+ v86.es = VTOPSEG(&smapbase[smaplen]);
+ v86.edi = VTOPOFF(&smapbase[smaplen]);
+ v86int();
+ smaplen++;
+ if ((v86.efl & 1) || (v86.eax != SMAPSIG))
+ break;
+ } while (v86.ebx != 0 && smaplen < n);
+}
+void
+bios_addsmapdata(struct preloaded_file *kfp)
+{
+ int len;
+
+ if (smapbase == 0 || smaplen == 0)
+ return;
+ len = smaplen * sizeof(*smapbase);
+ file_addmetadata(kfp, MODINFOMD_SMAP, len, smapbase);
+ /* Temporary compatability with older development kernels */
+ file_addmetadata(kfp, 0x0009, len, smapbase);
+}
diff --git a/sys/boot/i386/libi386/bootinfo.c b/sys/boot/i386/libi386/bootinfo.c
new file mode 100644
index 0000000..5095281
--- /dev/null
+++ b/sys/boot/i386/libi386/bootinfo.c
@@ -0,0 +1,157 @@
+/*-
+ * Copyright (c) 1998 Michael Smith <msmith@freebsd.org>
+ * 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 <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <stand.h>
+#include <sys/param.h>
+#include <sys/reboot.h>
+#include <sys/linker.h>
+#include "bootstrap.h"
+#include "libi386.h"
+#include "btxv86.h"
+
+/*
+ * Return a 'boothowto' value corresponding to the kernel arguments in
+ * (kargs) and any relevant environment variables.
+ */
+static struct
+{
+ const char *ev;
+ int mask;
+} howto_names[] = {
+ {"boot_askname", RB_ASKNAME},
+ {"boot_cdrom", RB_CDROM},
+ {"boot_userconfig", RB_CONFIG},
+ {"boot_ddb", RB_KDB},
+ {"boot_gdb", RB_GDB},
+ {"boot_single", RB_SINGLE},
+ {"boot_verbose", RB_VERBOSE},
+ {"boot_multicons", RB_MULTIPLE},
+ {"boot_serial", RB_SERIAL},
+ {NULL, 0}
+};
+
+int
+bi_getboothowto(char *kargs)
+{
+ char *cp;
+ int howto;
+ int active;
+ int i;
+
+ /* Parse kargs */
+ howto = 0;
+ if (kargs != NULL) {
+ cp = kargs;
+ active = 0;
+ while (*cp != 0) {
+ if (!active && (*cp == '-')) {
+ active = 1;
+ } else if (active)
+ switch (*cp) {
+ case 'a':
+ howto |= RB_ASKNAME;
+ break;
+ case 'c':
+ howto |= RB_CONFIG;
+ break;
+ case 'C':
+ howto |= RB_CDROM;
+ break;
+ case 'd':
+ howto |= RB_KDB;
+ break;
+ case 'D':
+ howto |= RB_MULTIPLE;
+ break;
+ case 'm':
+ howto |= RB_MUTE;
+ break;
+ case 'g':
+ howto |= RB_GDB;
+ break;
+ case 'h':
+ howto |= RB_SERIAL;
+ break;
+ case 'p':
+ howto |= RB_PAUSE;
+ break;
+ case 'r':
+ howto |= RB_DFLTROOT;
+ break;
+ case 's':
+ howto |= RB_SINGLE;
+ break;
+ case 'v':
+ howto |= RB_VERBOSE;
+ break;
+ default:
+ active = 0;
+ break;
+ }
+ cp++;
+ }
+ }
+ /* get equivalents from the environment */
+ for (i = 0; howto_names[i].ev != NULL; i++)
+ if (getenv(howto_names[i].ev) != NULL)
+ howto |= howto_names[i].mask;
+ if (!strcmp(getenv("console"), "comconsole"))
+ howto |= RB_SERIAL;
+ if (!strcmp(getenv("console"), "nullconsole"))
+ howto |= RB_MUTE;
+ return(howto);
+}
+
+/*
+ * Copy the environment into the load area starting at (addr).
+ * Each variable is formatted as <name>=<value>, with a single nul
+ * separating each variable, and a double nul terminating the environment.
+ */
+vm_offset_t
+bi_copyenv(vm_offset_t addr)
+{
+ struct env_var *ep;
+
+ /* traverse the environment */
+ for (ep = environ; ep != NULL; ep = ep->ev_next) {
+ i386_copyin(ep->ev_name, addr, strlen(ep->ev_name));
+ addr += strlen(ep->ev_name);
+ i386_copyin("=", addr, 1);
+ addr++;
+ if (ep->ev_value != NULL) {
+ i386_copyin(ep->ev_value, addr, strlen(ep->ev_value));
+ addr += strlen(ep->ev_value);
+ }
+ i386_copyin("", addr, 1);
+ addr++;
+ }
+ i386_copyin("", addr, 1);
+ addr++;
+ return(addr);
+}
diff --git a/sys/boot/i386/libi386/bootinfo32.c b/sys/boot/i386/libi386/bootinfo32.c
new file mode 100644
index 0000000..ceb254c
--- /dev/null
+++ b/sys/boot/i386/libi386/bootinfo32.c
@@ -0,0 +1,273 @@
+/*-
+ * Copyright (c) 1998 Michael Smith <msmith@freebsd.org>
+ * 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 <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <stand.h>
+#include <sys/param.h>
+#include <sys/reboot.h>
+#include <sys/linker.h>
+#include <machine/bootinfo.h>
+#include "bootstrap.h"
+#include "libi386.h"
+#include "btxv86.h"
+
+static struct bootinfo bi;
+
+/*
+ * Copy module-related data into the load area, where it can be
+ * used as a directory for loaded modules.
+ *
+ * Module data is presented in a self-describing format. Each datum
+ * is preceded by a 32-bit identifier and a 32-bit size field.
+ *
+ * Currently, the following data are saved:
+ *
+ * MOD_NAME (variable) module name (string)
+ * MOD_TYPE (variable) module type (string)
+ * MOD_ARGS (variable) module parameters (string)
+ * MOD_ADDR sizeof(vm_offset_t) module load address
+ * MOD_SIZE sizeof(size_t) module size
+ * MOD_METADATA (variable) type-specific metadata
+ */
+#define COPY32(v, a, c) { \
+ u_int32_t x = (v); \
+ if (c) \
+ i386_copyin(&x, a, sizeof(x)); \
+ a += sizeof(x); \
+}
+
+#define MOD_STR(t, a, s, c) { \
+ COPY32(t, a, c); \
+ COPY32(strlen(s) + 1, a, c); \
+ if (c) \
+ i386_copyin(s, a, strlen(s) + 1); \
+ a += roundup(strlen(s) + 1, sizeof(u_long));\
+}
+
+#define MOD_NAME(a, s, c) MOD_STR(MODINFO_NAME, a, s, c)
+#define MOD_TYPE(a, s, c) MOD_STR(MODINFO_TYPE, a, s, c)
+#define MOD_ARGS(a, s, c) MOD_STR(MODINFO_ARGS, a, s, c)
+
+#define MOD_VAR(t, a, s, c) { \
+ COPY32(t, a, c); \
+ COPY32(sizeof(s), a, c); \
+ if (c) \
+ i386_copyin(&s, a, sizeof(s)); \
+ a += roundup(sizeof(s), sizeof(u_long)); \
+}
+
+#define MOD_ADDR(a, s, c) MOD_VAR(MODINFO_ADDR, a, s, c)
+#define MOD_SIZE(a, s, c) MOD_VAR(MODINFO_SIZE, a, s, c)
+
+#define MOD_METADATA(a, mm, c) { \
+ COPY32(MODINFO_METADATA | mm->md_type, a, c); \
+ COPY32(mm->md_size, a, c); \
+ if (c) \
+ i386_copyin(mm->md_data, a, mm->md_size); \
+ a += roundup(mm->md_size, sizeof(u_long));\
+}
+
+#define MOD_END(a, c) { \
+ COPY32(MODINFO_END, a, c); \
+ COPY32(0, a, c); \
+}
+
+static vm_offset_t
+bi_copymodules32(vm_offset_t addr)
+{
+ struct preloaded_file *fp;
+ struct file_metadata *md;
+ int c;
+
+ c = addr != 0;
+ /* start with the first module on the list, should be the kernel */
+ for (fp = file_findfile(NULL, NULL); fp != NULL; fp = fp->f_next) {
+
+ MOD_NAME(addr, fp->f_name, c); /* this field must come first */
+ MOD_TYPE(addr, fp->f_type, c);
+ if (fp->f_args)
+ MOD_ARGS(addr, fp->f_args, c);
+ MOD_ADDR(addr, fp->f_addr, c);
+ MOD_SIZE(addr, fp->f_size, c);
+ for (md = fp->f_metadata; md != NULL; md = md->md_next)
+ if (!(md->md_type & MODINFOMD_NOCOPY))
+ MOD_METADATA(addr, md, c);
+ }
+ MOD_END(addr, c);
+ return(addr);
+}
+
+/*
+ * Load the information expected by an i386 kernel.
+ *
+ * - The 'boothowto' argument is constructed
+ * - The 'bootdev' argument is constructed
+ * - The 'bootinfo' struct is constructed, and copied into the kernel space.
+ * - The kernel environment is copied into kernel space.
+ * - Module metadata are formatted and placed in kernel space.
+ */
+int
+bi_load32(char *args, int *howtop, int *bootdevp, vm_offset_t *bip, vm_offset_t *modulep, vm_offset_t *kernendp)
+{
+ struct preloaded_file *xp, *kfp;
+ struct i386_devdesc *rootdev;
+ struct file_metadata *md;
+ vm_offset_t addr;
+ vm_offset_t kernend;
+ vm_offset_t envp;
+ vm_offset_t size;
+ vm_offset_t ssym, esym;
+ char *rootdevname;
+ int bootdevnr, i, howto;
+ char *kernelname;
+ const char *kernelpath;
+
+ howto = bi_getboothowto(args);
+
+ /*
+ * Allow the environment variable 'rootdev' to override the supplied device
+ * This should perhaps go to MI code and/or have $rootdev tested/set by
+ * MI code before launching the kernel.
+ */
+ rootdevname = getenv("rootdev");
+ i386_getdev((void **)(&rootdev), rootdevname, NULL);
+ if (rootdev == NULL) { /* bad $rootdev/$currdev */
+ printf("can't determine root device\n");
+ return(EINVAL);
+ }
+
+ /* Try reading the /etc/fstab file to select the root device */
+ getrootmount(i386_fmtdev((void *)rootdev));
+
+ /* Do legacy rootdev guessing */
+
+ /* XXX - use a default bootdev of 0. Is this ok??? */
+ bootdevnr = 0;
+
+ switch(rootdev->d_type) {
+ case DEVT_CD:
+ /* Pass in BIOS device number. */
+ bi.bi_bios_dev = bc_unit2bios(rootdev->d_kind.bioscd.unit);
+ bootdevnr = bc_getdev(rootdev);
+ break;
+
+ case DEVT_DISK:
+ /* pass in the BIOS device number of the current disk */
+ bi.bi_bios_dev = bd_unit2bios(rootdev->d_kind.biosdisk.unit);
+ bootdevnr = bd_getdev(rootdev);
+ break;
+
+ case DEVT_NET:
+ break;
+
+ default:
+ printf("WARNING - don't know how to boot from device type %d\n", rootdev->d_type);
+ }
+ if (bootdevnr == -1) {
+ printf("root device %s invalid\n", i386_fmtdev(rootdev));
+ return (EINVAL);
+ }
+ free(rootdev);
+
+ /* find the last module in the chain */
+ addr = 0;
+ for (xp = file_findfile(NULL, NULL); xp != NULL; xp = xp->f_next) {
+ if (addr < (xp->f_addr + xp->f_size))
+ addr = xp->f_addr + xp->f_size;
+ }
+ /* pad to a page boundary */
+ addr = roundup(addr, PAGE_SIZE);
+
+ /* copy our environment */
+ envp = addr;
+ addr = bi_copyenv(addr);
+
+ /* pad to a page boundary */
+ addr = roundup(addr, PAGE_SIZE);
+
+ kfp = file_findfile(NULL, "elf kernel");
+ if (kfp == NULL)
+ kfp = file_findfile(NULL, "elf32 kernel");
+ if (kfp == NULL)
+ panic("can't find kernel file");
+ kernend = 0; /* fill it in later */
+ file_addmetadata(kfp, MODINFOMD_HOWTO, sizeof howto, &howto);
+ file_addmetadata(kfp, MODINFOMD_ENVP, sizeof envp, &envp);
+ file_addmetadata(kfp, MODINFOMD_KERNEND, sizeof kernend, &kernend);
+ bios_addsmapdata(kfp);
+
+ /* Figure out the size and location of the metadata */
+ *modulep = addr;
+ size = bi_copymodules32(0);
+ kernend = roundup(addr + size, PAGE_SIZE);
+ *kernendp = kernend;
+
+ /* patch MODINFOMD_KERNEND */
+ md = file_findmetadata(kfp, MODINFOMD_KERNEND);
+ bcopy(&kernend, md->md_data, sizeof kernend);
+
+ /* copy module list and metadata */
+ (void)bi_copymodules32(addr);
+
+ ssym = esym = 0;
+ md = file_findmetadata(kfp, MODINFOMD_SSYM);
+ if (md != NULL)
+ ssym = *((vm_offset_t *)&(md->md_data));
+ md = file_findmetadata(kfp, MODINFOMD_ESYM);
+ if (md != NULL)
+ esym = *((vm_offset_t *)&(md->md_data));
+ if (ssym == 0 || esym == 0)
+ ssym = esym = 0; /* sanity */
+
+ /* legacy bootinfo structure */
+ kernelname = getenv("kernelname");
+ i386_getdev(NULL, kernelname, &kernelpath);
+ bi.bi_version = BOOTINFO_VERSION;
+ bi.bi_kernelname = 0; /* XXX char * -> kernel name */
+ bi.bi_nfs_diskless = 0; /* struct nfs_diskless * */
+ bi.bi_n_bios_used = 0; /* XXX would have to hook biosdisk driver for these */
+ for (i = 0; i < N_BIOS_GEOM; i++)
+ bi.bi_bios_geom[i] = bd_getbigeom(i);
+ bi.bi_size = sizeof(bi);
+ bi.bi_memsizes_valid = 1;
+ bi.bi_basemem = bios_basemem / 1024;
+ bi.bi_extmem = bios_extmem / 1024;
+ bi.bi_envp = envp;
+ bi.bi_modulep = *modulep;
+ bi.bi_kernend = kernend;
+ bi.bi_kernelname = VTOP(kernelpath);
+ bi.bi_symtab = ssym; /* XXX this is only the primary kernel symtab */
+ bi.bi_esymtab = esym;
+
+ /* legacy boot arguments */
+ *howtop = howto | RB_BOOTINFO;
+ *bootdevp = bootdevnr;
+ *bip = VTOP(&bi);
+
+ return(0);
+}
diff --git a/sys/boot/i386/libi386/bootinfo64.c b/sys/boot/i386/libi386/bootinfo64.c
new file mode 100644
index 0000000..58b4e3c
--- /dev/null
+++ b/sys/boot/i386/libi386/bootinfo64.c
@@ -0,0 +1,208 @@
+/*-
+ * Copyright (c) 1998 Michael Smith <msmith@freebsd.org>
+ * 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 <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <stand.h>
+#include <sys/param.h>
+#include <sys/reboot.h>
+#include <sys/linker.h>
+#include <machine/bootinfo.h>
+#include "bootstrap.h"
+#include "libi386.h"
+#include "btxv86.h"
+
+/*
+ * Copy module-related data into the load area, where it can be
+ * used as a directory for loaded modules.
+ *
+ * Module data is presented in a self-describing format. Each datum
+ * is preceded by a 32-bit identifier and a 32-bit size field.
+ *
+ * Currently, the following data are saved:
+ *
+ * MOD_NAME (variable) module name (string)
+ * MOD_TYPE (variable) module type (string)
+ * MOD_ARGS (variable) module parameters (string)
+ * MOD_ADDR sizeof(vm_offset_t) module load address
+ * MOD_SIZE sizeof(size_t) module size
+ * MOD_METADATA (variable) type-specific metadata
+ */
+#define COPY32(v, a, c) { \
+ u_int32_t x = (v); \
+ if (c) \
+ i386_copyin(&x, a, sizeof(x)); \
+ a += sizeof(x); \
+}
+
+#define MOD_STR(t, a, s, c) { \
+ COPY32(t, a, c); \
+ COPY32(strlen(s) + 1, a, c); \
+ if (c) \
+ i386_copyin(s, a, strlen(s) + 1); \
+ a += roundup(strlen(s) + 1, sizeof(u_int64_t));\
+}
+
+#define MOD_NAME(a, s, c) MOD_STR(MODINFO_NAME, a, s, c)
+#define MOD_TYPE(a, s, c) MOD_STR(MODINFO_TYPE, a, s, c)
+#define MOD_ARGS(a, s, c) MOD_STR(MODINFO_ARGS, a, s, c)
+
+#define MOD_VAR(t, a, s, c) { \
+ COPY32(t, a, c); \
+ COPY32(sizeof(s), a, c); \
+ if (c) \
+ i386_copyin(&s, a, sizeof(s)); \
+ a += roundup(sizeof(s), sizeof(u_int64_t)); \
+}
+
+#define MOD_ADDR(a, s, c) MOD_VAR(MODINFO_ADDR, a, s, c)
+#define MOD_SIZE(a, s, c) MOD_VAR(MODINFO_SIZE, a, s, c)
+
+#define MOD_METADATA(a, mm, c) { \
+ COPY32(MODINFO_METADATA | mm->md_type, a, c); \
+ COPY32(mm->md_size, a, c); \
+ if (c) \
+ i386_copyin(mm->md_data, a, mm->md_size); \
+ a += roundup(mm->md_size, sizeof(u_int64_t));\
+}
+
+#define MOD_END(a, c) { \
+ COPY32(MODINFO_END, a, c); \
+ COPY32(0, a, c); \
+}
+
+static vm_offset_t
+bi_copymodules64(vm_offset_t addr)
+{
+ struct preloaded_file *fp;
+ struct file_metadata *md;
+ int c;
+ u_int64_t v;
+
+ c = addr != 0;
+ /* start with the first module on the list, should be the kernel */
+ for (fp = file_findfile(NULL, NULL); fp != NULL; fp = fp->f_next) {
+
+ MOD_NAME(addr, fp->f_name, c); /* this field must come first */
+ MOD_TYPE(addr, fp->f_type, c);
+ if (fp->f_args)
+ MOD_ARGS(addr, fp->f_args, c);
+ v = fp->f_addr;
+ MOD_ADDR(addr, v, c);
+ v = fp->f_size;
+ MOD_SIZE(addr, v, c);
+ for (md = fp->f_metadata; md != NULL; md = md->md_next)
+ if (!(md->md_type & MODINFOMD_NOCOPY))
+ MOD_METADATA(addr, md, c);
+ }
+ MOD_END(addr, c);
+ return(addr);
+}
+
+/*
+ * Load the information expected by an i386 kernel.
+ *
+ * - The 'boothowto' argument is constructed
+ * - The 'bootdev' argument is constructed
+ * - The 'bootinfo' struct is constructed, and copied into the kernel space.
+ * - The kernel environment is copied into kernel space.
+ * - Module metadata are formatted and placed in kernel space.
+ */
+int
+bi_load64(char *args, vm_offset_t *modulep, vm_offset_t *kernendp)
+{
+ struct preloaded_file *xp, *kfp;
+ struct i386_devdesc *rootdev;
+ struct file_metadata *md;
+ vm_offset_t addr;
+ u_int64_t kernend;
+ u_int64_t envp;
+ vm_offset_t size;
+ char *rootdevname;
+ int i, howto;
+ char *kernelname;
+ const char *kernelpath;
+
+ howto = bi_getboothowto(args);
+
+ /*
+ * Allow the environment variable 'rootdev' to override the supplied device
+ * This should perhaps go to MI code and/or have $rootdev tested/set by
+ * MI code before launching the kernel.
+ */
+ rootdevname = getenv("rootdev");
+ i386_getdev((void **)(&rootdev), rootdevname, NULL);
+ if (rootdev == NULL) { /* bad $rootdev/$currdev */
+ printf("can't determine root device\n");
+ return(EINVAL);
+ }
+
+ /* Try reading the /etc/fstab file to select the root device */
+ getrootmount(i386_fmtdev((void *)rootdev));
+
+ /* find the last module in the chain */
+ addr = 0;
+ for (xp = file_findfile(NULL, NULL); xp != NULL; xp = xp->f_next) {
+ if (addr < (xp->f_addr + xp->f_size))
+ addr = xp->f_addr + xp->f_size;
+ }
+ /* pad to a page boundary */
+ addr = roundup(addr, PAGE_SIZE);
+
+ /* copy our environment */
+ envp = addr;
+ addr = bi_copyenv(addr);
+
+ /* pad to a page boundary */
+ addr = roundup(addr, PAGE_SIZE);
+
+ kfp = file_findfile(NULL, "elf kernel");
+ if (kfp == NULL)
+ kfp = file_findfile(NULL, "elf64 kernel");
+ if (kfp == NULL)
+ panic("can't find kernel file");
+ kernend = 0; /* fill it in later */
+ file_addmetadata(kfp, MODINFOMD_HOWTO, sizeof howto, &howto);
+ file_addmetadata(kfp, MODINFOMD_ENVP, sizeof envp, &envp);
+ file_addmetadata(kfp, MODINFOMD_KERNEND, sizeof kernend, &kernend);
+ bios_addsmapdata(kfp);
+
+ /* Figure out the size and location of the metadata */
+ *modulep = addr;
+ size = bi_copymodules64(0);
+ kernend = roundup(addr + size, PAGE_SIZE);
+ *kernendp = kernend;
+
+ /* patch MODINFOMD_KERNEND */
+ md = file_findmetadata(kfp, MODINFOMD_KERNEND);
+ bcopy(&kernend, md->md_data, sizeof kernend);
+
+ /* copy module list and metadata */
+ (void)bi_copymodules64(addr);
+
+ return(0);
+}
diff --git a/sys/boot/i386/libi386/comconsole.c b/sys/boot/i386/libi386/comconsole.c
new file mode 100644
index 0000000..1b08d9b
--- /dev/null
+++ b/sys/boot/i386/libi386/comconsole.c
@@ -0,0 +1,114 @@
+/*-
+ * Copyright (c) 1998 Michael Smith (msmith@freebsd.org)
+ *
+ * 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 <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <stand.h>
+#include <bootstrap.h>
+#include <machine/cpufunc.h>
+#include <dev/ic/ns16550.h>
+#include "libi386.h"
+
+#define COMC_FMT 0x3 /* 8N1 */
+#define COMC_TXWAIT 0x40000 /* transmit timeout */
+#define COMC_BPS(x) (115200 / (x)) /* speed to DLAB divisor */
+
+#ifndef COMPORT
+#define COMPORT 0x3f8
+#endif
+#ifndef COMSPEED
+#define COMSPEED 9600
+#endif
+
+static void comc_probe(struct console *cp);
+static int comc_init(int arg);
+static void comc_putchar(int c);
+static int comc_getchar(void);
+static int comc_ischar(void);
+
+static int comc_started;
+
+struct console comconsole = {
+ "comconsole",
+ "serial port",
+ 0,
+ comc_probe,
+ comc_init,
+ comc_putchar,
+ comc_getchar,
+ comc_ischar
+};
+
+static void
+comc_probe(struct console *cp)
+{
+ /* XXX check the BIOS equipment list? */
+ cp->c_flags |= (C_PRESENTIN | C_PRESENTOUT);
+}
+
+static int
+comc_init(int arg)
+{
+ if (comc_started && arg == 0)
+ return 0;
+ comc_started = 1;
+
+ outb(COMPORT + com_cfcr, CFCR_DLAB | COMC_FMT);
+ outb(COMPORT + com_dlbl, COMC_BPS(COMSPEED) & 0xff);
+ outb(COMPORT + com_dlbh, COMC_BPS(COMSPEED) >> 8);
+ outb(COMPORT + com_cfcr, COMC_FMT);
+ outb(COMPORT + com_mcr, MCR_RTS | MCR_DTR);
+
+ do
+ inb(COMPORT + com_data);
+ while (inb(COMPORT + com_lsr) & LSR_RXRDY);
+
+ return(0);
+}
+
+static void
+comc_putchar(int c)
+{
+ int wait;
+
+ for (wait = COMC_TXWAIT; wait > 0; wait--)
+ if (inb(COMPORT + com_lsr) & LSR_TXRDY) {
+ outb(COMPORT + com_data, (u_char)c);
+ break;
+ }
+}
+
+static int
+comc_getchar(void)
+{
+ return(comc_ischar() ? inb(COMPORT + com_data) : -1);
+}
+
+static int
+comc_ischar(void)
+{
+ return(inb(COMPORT + com_lsr) & LSR_RXRDY);
+}
diff --git a/sys/boot/i386/libi386/devicename.c b/sys/boot/i386/libi386/devicename.c
new file mode 100644
index 0000000..7ae45b1
--- /dev/null
+++ b/sys/boot/i386/libi386/devicename.c
@@ -0,0 +1,244 @@
+/*-
+ * Copyright (c) 1998 Michael Smith <msmith@freebsd.org>
+ * 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 <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <stand.h>
+#include <string.h>
+#include <sys/disklabel.h>
+#include "bootstrap.h"
+#include "libi386.h"
+
+static int i386_parsedev(struct i386_devdesc **dev, const char *devspec, const char **path);
+
+/*
+ * Point (dev) at an allocated device specifier for the device matching the
+ * path in (devspec). If it contains an explicit device specification,
+ * use that. If not, use the default device.
+ */
+int
+i386_getdev(void **vdev, const char *devspec, const char **path)
+{
+ struct i386_devdesc **dev = (struct i386_devdesc **)vdev;
+ int rv;
+
+ /*
+ * If it looks like this is just a path and no
+ * device, go with the current device.
+ */
+ if ((devspec == NULL) ||
+ (devspec[0] == '/') ||
+ (strchr(devspec, ':') == NULL)) {
+
+ if (((rv = i386_parsedev(dev, getenv("currdev"), NULL)) == 0) &&
+ (path != NULL))
+ *path = devspec;
+ return(rv);
+ }
+
+ /*
+ * Try to parse the device name off the beginning of the devspec
+ */
+ return(i386_parsedev(dev, devspec, path));
+}
+
+/*
+ * Point (dev) at an allocated device specifier matching the string version
+ * at the beginning of (devspec). Return a pointer to the remaining
+ * text in (path).
+ *
+ * In all cases, the beginning of (devspec) is compared to the names
+ * of known devices in the device switch, and then any following text
+ * is parsed according to the rules applied to the device type.
+ *
+ * For disk-type devices, the syntax is:
+ *
+ * disk<unit>[s<slice>][<partition>]:
+ *
+ */
+static int
+i386_parsedev(struct i386_devdesc **dev, const char *devspec, const char **path)
+{
+ struct i386_devdesc *idev;
+ struct devsw *dv;
+ int i, unit, slice, partition, err;
+ char *cp;
+ const char *np;
+
+ /* minimum length check */
+ if (strlen(devspec) < 2)
+ return(EINVAL);
+
+ /* look for a device that matches */
+ for (i = 0, dv = NULL; devsw[i] != NULL; i++) {
+ if (!strncmp(devspec, devsw[i]->dv_name, strlen(devsw[i]->dv_name))) {
+ dv = devsw[i];
+ break;
+ }
+ }
+ if (dv == NULL)
+ return(ENOENT);
+ idev = malloc(sizeof(struct i386_devdesc));
+ err = 0;
+ np = (devspec + strlen(dv->dv_name));
+
+ switch(dv->dv_type) {
+ case DEVT_NONE: /* XXX what to do here? Do we care? */
+ break;
+
+ case DEVT_DISK:
+ unit = -1;
+ slice = -1;
+ partition = -1;
+ if (*np && (*np != ':')) {
+ unit = strtol(np, &cp, 10); /* next comes the unit number */
+ if (cp == np) {
+ err = EUNIT;
+ goto fail;
+ }
+ if (*cp == 's') { /* got a slice number */
+ np = cp + 1;
+ slice = strtol(np, &cp, 10);
+ if (cp == np) {
+ err = ESLICE;
+ goto fail;
+ }
+ }
+ if (*cp && (*cp != ':')) {
+ partition = *cp - 'a'; /* get a partition number */
+ if ((partition < 0) || (partition >= MAXPARTITIONS)) {
+ err = EPART;
+ goto fail;
+ }
+ cp++;
+ }
+ }
+ if (*cp && (*cp != ':')) {
+ err = EINVAL;
+ goto fail;
+ }
+
+ idev->d_kind.biosdisk.unit = unit;
+ idev->d_kind.biosdisk.slice = slice;
+ idev->d_kind.biosdisk.partition = partition;
+ if (path != NULL)
+ *path = (*cp == 0) ? cp : cp + 1;
+ break;
+
+ case DEVT_CD:
+ case DEVT_NET:
+ unit = 0;
+
+ if (*np && (*np != ':')) {
+ unit = strtol(np, &cp, 0); /* get unit number if present */
+ if (cp == np) {
+ err = EUNIT;
+ goto fail;
+ }
+ }
+ if (*cp && (*cp != ':')) {
+ err = EINVAL;
+ goto fail;
+ }
+
+ if (dv->dv_type == DEVT_NET)
+ idev->d_kind.netif.unit = unit;
+ else
+ idev->d_kind.bioscd.unit = unit;
+ if (path != NULL)
+ *path = (*cp == 0) ? cp : cp + 1;
+ break;
+
+ default:
+ err = EINVAL;
+ goto fail;
+ }
+ idev->d_dev = dv;
+ idev->d_type = dv->dv_type;
+ if (dev == NULL) {
+ free(idev);
+ } else {
+ *dev = idev;
+ }
+ return(0);
+
+ fail:
+ free(idev);
+ return(err);
+}
+
+
+char *
+i386_fmtdev(void *vdev)
+{
+ struct i386_devdesc *dev = (struct i386_devdesc *)vdev;
+ static char buf[128]; /* XXX device length constant? */
+ char *cp;
+
+ switch(dev->d_type) {
+ case DEVT_NONE:
+ strcpy(buf, "(no device)");
+ break;
+
+ case DEVT_CD:
+ sprintf(buf, "%s%d:", dev->d_dev->dv_name, dev->d_kind.bioscd.unit);
+ break;
+
+ case DEVT_DISK:
+ cp = buf;
+ cp += sprintf(cp, "%s%d", dev->d_dev->dv_name, dev->d_kind.biosdisk.unit);
+ if (dev->d_kind.biosdisk.slice > 0)
+ cp += sprintf(cp, "s%d", dev->d_kind.biosdisk.slice);
+ if (dev->d_kind.biosdisk.partition >= 0)
+ cp += sprintf(cp, "%c", dev->d_kind.biosdisk.partition + 'a');
+ strcat(cp, ":");
+ break;
+
+ case DEVT_NET:
+ sprintf(buf, "%s%d:", dev->d_dev->dv_name, dev->d_kind.netif.unit);
+ break;
+ }
+ return(buf);
+}
+
+
+/*
+ * Set currdev to suit the value being supplied in (value)
+ */
+int
+i386_setcurrdev(struct env_var *ev, int flags, void *value)
+{
+ struct i386_devdesc *ncurr;
+ int rv;
+
+ if ((rv = i386_parsedev(&ncurr, value, NULL)) != 0)
+ return(rv);
+ free(ncurr);
+ env_setenv(ev->ev_name, flags | EV_NOHOOK, value, NULL, NULL);
+ return(0);
+}
+
diff --git a/sys/boot/i386/libi386/elf32_freebsd.c b/sys/boot/i386/libi386/elf32_freebsd.c
new file mode 100644
index 0000000..a508201
--- /dev/null
+++ b/sys/boot/i386/libi386/elf32_freebsd.c
@@ -0,0 +1,76 @@
+/*-
+ * Copyright (c) 1998 Michael Smith <msmith@freebsd.org>
+ * 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 <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/param.h>
+#include <sys/exec.h>
+#include <sys/linker.h>
+#include <string.h>
+#include <machine/bootinfo.h>
+#include <machine/elf.h>
+#include <stand.h>
+
+#include "bootstrap.h"
+#include "libi386.h"
+#include "btxv86.h"
+
+static int elf32_exec(struct preloaded_file *amp);
+
+struct file_format i386_elf = { elf32_loadfile, elf32_exec };
+
+/*
+ * There is an a.out kernel and one or more a.out modules loaded.
+ * We wish to start executing the kernel image, so make such
+ * preparations as are required, and do so.
+ */
+static int
+elf32_exec(struct preloaded_file *fp)
+{
+ struct file_metadata *md;
+ Elf_Ehdr *ehdr;
+ vm_offset_t entry, bootinfop, modulep, kernend;
+ int boothowto, err, bootdev;
+
+ if ((md = file_findmetadata(fp, MODINFOMD_ELFHDR)) == NULL)
+ return(EFTYPE); /* XXX actually EFUCKUP */
+ ehdr = (Elf_Ehdr *)&(md->md_data);
+
+ err = bi_load32(fp->f_args, &boothowto, &bootdev, &bootinfop, &modulep, &kernend);
+ if (err != 0)
+ return(err);
+ entry = ehdr->e_entry & 0xffffff;
+
+#ifdef DEBUG
+ printf("Start @ 0x%lx ...\n", entry);
+#endif
+
+ dev_cleanup();
+ __exec((void *)entry, boothowto, bootdev, 0, 0, 0, bootinfop, modulep, kernend);
+
+ panic("exec returned");
+}
diff --git a/sys/boot/i386/libi386/elf64_freebsd.c b/sys/boot/i386/libi386/elf64_freebsd.c
new file mode 100644
index 0000000..229c259
--- /dev/null
+++ b/sys/boot/i386/libi386/elf64_freebsd.c
@@ -0,0 +1,118 @@
+/*-
+ * Copyright (c) 1998 Michael Smith <msmith@freebsd.org>
+ * 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 <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#define __ELF_WORD_SIZE 64
+#include <sys/param.h>
+#include <sys/exec.h>
+#include <sys/linker.h>
+#include <string.h>
+#include <machine/bootinfo.h>
+#include <machine/elf.h>
+#include <stand.h>
+
+#include "bootstrap.h"
+#include "libi386.h"
+#include "btxv86.h"
+
+static int elf64_exec(struct preloaded_file *amp);
+
+struct file_format amd64_elf = { elf64_loadfile, elf64_exec };
+
+#define PG_V 0x001
+#define PG_RW 0x002
+#define PG_U 0x004
+#define PG_PS 0x080
+
+typedef u_int64_t p4_entry_t;
+typedef u_int64_t p3_entry_t;
+typedef u_int64_t p2_entry_t;
+extern p4_entry_t PT4[];
+extern p3_entry_t PT3[];
+extern p2_entry_t PT2[];
+
+u_int32_t entry_hi;
+u_int32_t entry_lo;
+
+extern amd64_tramp();
+
+/*
+ * There is an a.out kernel and one or more a.out modules loaded.
+ * We wish to start executing the kernel image, so make such
+ * preparations as are required, and do so.
+ */
+static int
+elf64_exec(struct preloaded_file *fp)
+{
+ struct file_metadata *md;
+ Elf_Ehdr *ehdr;
+ vm_offset_t modulep, kernend;
+ int err;
+ int i;
+
+ if ((md = file_findmetadata(fp, MODINFOMD_ELFHDR)) == NULL)
+ return(EFTYPE); /* XXX actually EFUCKUP */
+ ehdr = (Elf_Ehdr *)&(md->md_data);
+
+ err = bi_load64(fp->f_args, &modulep, &kernend);
+ if (err != 0)
+ return(err);
+
+ bzero(PT4, PAGE_SIZE);
+ bzero(PT3, PAGE_SIZE);
+ bzero(PT2, PAGE_SIZE);
+
+ /*
+ * This is kinda brutal, but every single 1GB VM memory segment points to
+ * the same first 1GB of physical memory. But it is more than adequate.
+ */
+ for (i = 0; i < 512; i++) {
+ /* Each slot of the level 4 pages points to the same level 3 page */
+ PT4[i] = (p4_entry_t)VTOP((uintptr_t)&PT3[0]);
+ PT4[i] |= PG_V | PG_RW | PG_U;
+
+ /* Each slot of the level 3 pages points to the same level 2 page */
+ PT3[i] = (p3_entry_t)VTOP((uintptr_t)&PT2[0]);
+ PT3[i] |= PG_V | PG_RW | PG_U;
+
+ /* The level 2 page slots are mapped with 2MB pages for 1GB. */
+ PT2[i] = i * (2 * 1024 * 1024);
+ PT2[i] |= PG_V | PG_RW | PG_PS | PG_U;
+ }
+
+ entry_lo = ehdr->e_entry & 0xffffffff;
+ entry_hi = (ehdr->e_entry >> 32) & 0xffffffff;
+#ifdef DEBUG
+ printf("Start @ %#llx ...\n", ehdr->e_entry);
+#endif
+
+ dev_cleanup();
+ __exec((void *)VTOP(amd64_tramp), modulep, kernend);
+
+ panic("exec returned");
+}
diff --git a/sys/boot/i386/libi386/gatea20.c b/sys/boot/i386/libi386/gatea20.c
new file mode 100644
index 0000000..1c5f0f0
--- /dev/null
+++ b/sys/boot/i386/libi386/gatea20.c
@@ -0,0 +1,54 @@
+/*
+ * $NetBSD: gatea20.c,v 1.2 1997/10/29 00:32:49 fvdl Exp $
+ */
+
+/* extracted from freebsd:sys/i386/boot/biosboot/io.c */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <stand.h>
+#include <machine/cpufunc.h>
+
+#include <bootstrap.h>
+
+#include "libi386.h"
+
+#define K_RDWR 0x60 /* keyboard data & cmds (read/write) */
+#define K_STATUS 0x64 /* keyboard status */
+#define K_CMD 0x64 /* keybd ctlr command (write-only) */
+
+#define K_OBUF_FUL 0x01 /* output buffer full */
+#define K_IBUF_FUL 0x02 /* input buffer full */
+
+#define KC_CMD_WIN 0xd0 /* read output port */
+#define KC_CMD_WOUT 0xd1 /* write output port */
+#define KB_A20 0x9f /* enable A20,
+ reset (!),
+ enable output buffer full interrupt
+ enable data line
+ disable clock line */
+
+/*
+ * Gate A20 for high memory
+ */
+static unsigned char x_20 = KB_A20;
+void gateA20()
+{
+ __asm("pushfl ; cli");
+#ifdef IBM_L40
+ outb(0x92, 0x2);
+#else /* !IBM_L40 */
+ while (inb(K_STATUS) & K_IBUF_FUL);
+ while (inb(K_STATUS) & K_OBUF_FUL)
+ (void)inb(K_RDWR);
+
+ outb(K_CMD, KC_CMD_WOUT);
+ delay(100);
+ while (inb(K_STATUS) & K_IBUF_FUL);
+ outb(K_RDWR, x_20);
+ delay(100);
+ while (inb(K_STATUS) & K_IBUF_FUL);
+#endif /* IBM_L40 */
+ __asm("popfl");
+}
diff --git a/sys/boot/i386/libi386/i386_copy.c b/sys/boot/i386/libi386/i386_copy.c
new file mode 100644
index 0000000..2dce4f1
--- /dev/null
+++ b/sys/boot/i386/libi386/i386_copy.c
@@ -0,0 +1,91 @@
+/*-
+ * Copyright (c) 1998 Michael Smith <msmith@freebsd.org>
+ * 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 <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+/*
+ * MD primitives supporting placement of module data
+ *
+ * XXX should check load address/size against memory top.
+ */
+#include <stand.h>
+
+#include "libi386.h"
+#include "btxv86.h"
+
+#define READIN_BUF (16 * 1024)
+
+ssize_t
+i386_copyin(const void *src, vm_offset_t dest, const size_t len)
+{
+ if (dest + len >= memtop) {
+ errno = EFBIG;
+ return(-1);
+ }
+
+ bcopy(src, PTOV(dest), len);
+ return(len);
+}
+
+ssize_t
+i386_copyout(const vm_offset_t src, void *dest, const size_t len)
+{
+ if (src + len >= memtop) {
+ errno = EFBIG;
+ return(-1);
+ }
+
+ bcopy(PTOV(src), dest, len);
+ return(len);
+}
+
+
+ssize_t
+i386_readin(const int fd, vm_offset_t dest, const size_t len)
+{
+ void *buf;
+ size_t resid, chunk, get;
+ ssize_t got;
+
+ if (dest + len >= memtop)
+ return(0);
+
+ chunk = min(READIN_BUF, len);
+ buf = malloc(chunk);
+ if (buf == NULL)
+ return(0);
+
+ for (resid = len; resid > 0; resid -= got, dest += got) {
+ get = min(chunk, resid);
+ got = read(fd, buf, get);
+ if (got <= 0)
+ break;
+ bcopy(buf, PTOV(dest), (size_t)got);
+ }
+ free(buf);
+ return(len - resid);
+}
diff --git a/sys/boot/i386/libi386/i386_module.c b/sys/boot/i386/libi386/i386_module.c
new file mode 100644
index 0000000..91993df
--- /dev/null
+++ b/sys/boot/i386/libi386/i386_module.c
@@ -0,0 +1,68 @@
+/*-
+ * Copyright (c) 1998 Michael Smith <msmith@freebsd.org>
+ * 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 <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+/*
+ * i386-specific module functionality.
+ *
+ */
+
+#include <stand.h>
+#include <string.h>
+
+#include "bootstrap.h"
+#include "libi386.h"
+
+/*
+ * Use voodoo to load modules required by current hardware.
+ */
+int
+i386_autoload(void)
+{
+ int error;
+ int disabled;
+ char *rv;
+
+ /* XXX use PnP to locate stuff here */
+
+ /* autoload ACPI support */
+ /* XXX should be in 4th keyed off acpi_load */
+ disabled = 0;
+ rv = getenv("hint.acpi.0.disabled");
+ if (rv != NULL && strncmp(rv, "0", 1) != 0) {
+ disabled = 1;
+ }
+
+ if (getenv("acpi_load") && (!disabled)) {
+ error = mod_load("acpi", NULL, 0, NULL);
+ if (error != 0)
+ printf("ACPI autoload failed - %s\n", strerror(error));
+ }
+
+ return(0);
+}
diff --git a/sys/boot/i386/libi386/libi386.h b/sys/boot/i386/libi386/libi386.h
new file mode 100644
index 0000000..f2696e0
--- /dev/null
+++ b/sys/boot/i386/libi386/libi386.h
@@ -0,0 +1,108 @@
+/*-
+ * Copyright (c) 1998 Michael Smith <msmith@freebsd.org>
+ * 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.
+ *
+ * $FreeBSD$
+ */
+
+
+/*
+ * i386 fully-qualified device descriptor.
+ * Note, this must match the 'struct devdesc' declaration
+ * in bootstrap.h.
+ */
+struct i386_devdesc
+{
+ struct devsw *d_dev;
+ int d_type;
+ union
+ {
+ struct
+ {
+ int unit;
+ int slice;
+ int partition;
+ void *data;
+ } biosdisk;
+ struct
+ {
+ int unit;
+ void *data;
+ } bioscd;
+ struct
+ {
+ int unit; /* XXX net layer lives over these? */
+ } netif;
+ } d_kind;
+};
+
+int i386_getdev(void **vdev, const char *devspec, const char **path);
+char *i386_fmtdev(void *vdev);
+int i386_setcurrdev(struct env_var *ev, int flags, void *value);
+
+extern struct devdesc currdev; /* our current device */
+
+#define MAXDEV 31 /* maximum number of distinct devices */
+
+/* exported devices XXX rename? */
+extern struct devsw bioscd;
+extern struct devsw biosdisk;
+extern struct devsw pxedisk;
+extern struct fs_ops pxe_fsops;
+
+int bc_add(int biosdev); /* Register CD booted from. */
+int bc_getdev(struct i386_devdesc *dev); /* return dev_t for (dev) */
+int bc_bios2unit(int biosdev); /* xlate BIOS device -> bioscd unit */
+int bc_unit2bios(int unit); /* xlate bioscd unit -> BIOS device */
+u_int32_t bd_getbigeom(int bunit); /* return geometry in bootinfo format */
+int bd_bios2unit(int biosdev); /* xlate BIOS device -> biosdisk unit */
+int bd_unit2bios(int unit); /* xlate biosdisk unit -> BIOS device */
+int bd_getdev(struct i386_devdesc *dev); /* return dev_t for (dev) */
+
+ssize_t i386_copyin(const void *src, vm_offset_t dest, const size_t len);
+ssize_t i386_copyout(const vm_offset_t src, void *dest, const size_t len);
+ssize_t i386_readin(const int fd, vm_offset_t dest, const size_t len);
+
+struct preloaded_file;
+void bios_addsmapdata(struct preloaded_file *);
+void bios_getsmap(void);
+
+void bios_getmem(void);
+extern u_int32_t bios_basemem; /* base memory in bytes */
+extern u_int32_t bios_extmem; /* extended memory in bytes */
+extern vm_offset_t memtop;
+
+void biosacpi_detect();
+
+void gateA20(void);
+
+int i386_autoload(void);
+
+int bi_getboothowto(char *kargs);
+vm_offset_t bi_copyenv(vm_offset_t addr);
+int bi_load32(char *args, int *howtop, int *bootdevp, vm_offset_t *bip,
+ vm_offset_t *modulep, vm_offset_t *kernend);
+int bi_load64(char *args, vm_offset_t *modulep, vm_offset_t *kernend);
+
+void pxe_enable(void *pxeinfo);
diff --git a/sys/boot/i386/libi386/nullconsole.c b/sys/boot/i386/libi386/nullconsole.c
new file mode 100644
index 0000000..ebb1e7e
--- /dev/null
+++ b/sys/boot/i386/libi386/nullconsole.c
@@ -0,0 +1,88 @@
+/*-
+ * nullconsole.c
+ *
+ * Author: Doug Ambrisko <ambrisko@whistle.com>
+ * Copyright (c) 2000 Whistle Communications, Inc.
+ * All rights reserved.
+ *
+ * Subject to the following obligations and disclaimer of warranty, use and
+ * redistribution of this software, in source or object code forms, with or
+ * without modifications are expressly permitted by Whistle Communications;
+ * provided, however, that:
+ * 1. Any and all reproductions of the source or object code must include the
+ * copyright notice above and the following disclaimer of warranties; and
+ * 2. No rights are granted, in any manner or form, to use Whistle
+ * Communications, Inc. trademarks, including the mark "WHISTLE
+ * COMMUNICATIONS" on advertising, endorsements, or otherwise except as
+ * such appears in the above copyright notice or in the software.
+ *
+ * THIS SOFTWARE IS BEING PROVIDED BY WHISTLE COMMUNICATIONS "AS IS", AND
+ * TO THE MAXIMUM EXTENT PERMITTED BY LAW, WHISTLE COMMUNICATIONS MAKES NO
+ * REPRESENTATIONS OR WARRANTIES, EXPRESS OR IMPLIED, REGARDING THIS SOFTWARE,
+ * INCLUDING WITHOUT LIMITATION, ANY AND ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, OR NON-INFRINGEMENT.
+ * WHISTLE COMMUNICATIONS DOES NOT WARRANT, GUARANTEE, OR MAKE ANY
+ * REPRESENTATIONS REGARDING THE USE OF, OR THE RESULTS OF THE USE OF THIS
+ * SOFTWARE IN TERMS OF ITS CORRECTNESS, ACCURACY, RELIABILITY OR OTHERWISE.
+ * IN NO EVENT SHALL WHISTLE COMMUNICATIONS BE LIABLE FOR ANY DAMAGES
+ * RESULTING FROM OR ARISING OUT OF ANY USE OF THIS SOFTWARE, INCLUDING
+ * WITHOUT LIMITATION, ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY,
+ * PUNITIVE, OR CONSEQUENTIAL DAMAGES, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES, LOSS OF USE, DATA OR PROFITS, HOWEVER CAUSED AND UNDER 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 WHISTLE COMMUNICATIONS IS ADVISED OF THE POSSIBILITY
+ * OF SUCH DAMAGE.
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <stand.h>
+#include <bootstrap.h>
+
+static void nullc_probe(struct console *cp);
+static int nullc_init(int arg);
+static void nullc_putchar(int c);
+static int nullc_getchar(void);
+static int nullc_ischar(void);
+
+struct console nullconsole = {
+ "nullconsole",
+ "null port",
+ 0,
+ nullc_probe,
+ nullc_init,
+ nullc_putchar,
+ nullc_getchar,
+ nullc_ischar
+};
+
+static void
+nullc_probe(struct console *cp)
+{
+ cp->c_flags |= (C_PRESENTIN | C_PRESENTOUT);
+}
+
+static int
+nullc_init(int arg)
+{
+ return(0);
+}
+
+static void
+nullc_putchar(int c)
+{
+}
+
+static int
+nullc_getchar(void)
+{
+ return(-1);
+}
+
+static int
+nullc_ischar(void)
+{
+ return(0);
+}
diff --git a/sys/boot/i386/libi386/pread.c b/sys/boot/i386/libi386/pread.c
new file mode 100644
index 0000000..870e254
--- /dev/null
+++ b/sys/boot/i386/libi386/pread.c
@@ -0,0 +1,80 @@
+/*
+ * $NetBSD: pread.c,v 1.2 1997/03/22 01:48:38 thorpej Exp $
+ */
+
+/*-
+ * Copyright (c) 1996
+ * Matthias Drochner. 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. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed for the NetBSD Project
+ * by Matthias Drochner.
+ * 4. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 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 <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+/* read into destination in flat addr space */
+
+#include <stand.h>
+
+#include "libi386.h"
+
+#ifdef SAVE_MEMORY
+#define BUFSIZE (1*1024)
+#else
+#define BUFSIZE (4*1024)
+#endif
+
+static char buf[BUFSIZE];
+
+int
+pread(fd, dest, size)
+ int fd;
+ vm_offset_t dest;
+ int size;
+{
+ int rsize;
+
+ rsize = size;
+ while (rsize > 0) {
+ int count, got;
+
+ count = (rsize < BUFSIZE ? rsize : BUFSIZE);
+
+ got = read(fd, buf, count);
+ if (got < 0)
+ return (-1);
+
+ /* put to physical space */
+ vpbcopy(buf, dest, got);
+
+ dest += got;
+ rsize -= got;
+ if (got < count)
+ break; /* EOF */
+ }
+ return (size - rsize);
+}
diff --git a/sys/boot/i386/libi386/pxe.c b/sys/boot/i386/libi386/pxe.c
new file mode 100644
index 0000000..b78508a
--- /dev/null
+++ b/sys/boot/i386/libi386/pxe.c
@@ -0,0 +1,609 @@
+/*-
+ * Copyright (c) 2000 Alfred Perlstein <alfred@freebsd.org>
+ * Copyright (c) 2000 Paul Saab <ps@freebsd.org>
+ * Copyright (c) 2000 John Baldwin <jhb@freebsd.org>
+ * 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 <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <stand.h>
+#include <string.h>
+#include <stdarg.h>
+
+#include <netinet/in_systm.h>
+#include <netinet/in.h>
+#include <netinet/udp.h>
+
+#include <net.h>
+#include <netif.h>
+#include <nfsv2.h>
+#include <iodesc.h>
+
+#include <bootp.h>
+#include <bootstrap.h>
+#include "btxv86.h"
+#include "pxe.h"
+
+/*
+ * Allocate the PXE buffers statically instead of sticking grimy fingers into
+ * BTX's private data area. The scratch buffer is used to send information to
+ * the PXE BIOS, and the data buffer is used to receive data from the PXE BIOS.
+ */
+#define PXE_BUFFER_SIZE 0x2000
+#define PXE_TFTP_BUFFER_SIZE 512
+static char scratch_buffer[PXE_BUFFER_SIZE];
+static char data_buffer[PXE_BUFFER_SIZE];
+
+static pxenv_t *pxenv_p = NULL; /* PXENV+ */
+static pxe_t *pxe_p = NULL; /* !PXE */
+static BOOTPLAYER bootplayer; /* PXE Cached information. */
+
+static int pxe_debug = 0;
+static int pxe_sock = -1;
+static int pxe_opens = 0;
+
+void pxe_enable(void *pxeinfo);
+static void (*pxe_call)(int func);
+static void pxenv_call(int func);
+static void bangpxe_call(int func);
+
+static int pxe_init(void);
+static int pxe_strategy(void *devdata, int flag, daddr_t dblk,
+ size_t size, char *buf, size_t *rsize);
+static int pxe_open(struct open_file *f, ...);
+static int pxe_close(struct open_file *f);
+static void pxe_print(int verbose);
+static void pxe_cleanup(void);
+static void pxe_setnfshandle(char *rootpath);
+
+static void pxe_perror(int error);
+static int pxe_netif_match(struct netif *nif, void *machdep_hint);
+static int pxe_netif_probe(struct netif *nif, void *machdep_hint);
+static void pxe_netif_init(struct iodesc *desc, void *machdep_hint);
+static int pxe_netif_get(struct iodesc *desc, void *pkt, size_t len,
+ time_t timeout);
+static int pxe_netif_put(struct iodesc *desc, void *pkt, size_t len);
+static void pxe_netif_end(struct netif *nif);
+
+extern struct netif_stats pxe_st[];
+extern u_int16_t __bangpxeseg;
+extern u_int16_t __bangpxeoff;
+extern void __bangpxeentry(void);
+extern u_int16_t __pxenvseg;
+extern u_int16_t __pxenvoff;
+extern void __pxenventry(void);
+
+struct netif_dif pxe_ifs[] = {
+/* dif_unit dif_nsel dif_stats dif_private */
+ {0, 1, &pxe_st[0], 0}
+};
+
+struct netif_stats pxe_st[NENTS(pxe_ifs)];
+
+struct netif_driver pxenetif = {
+ "pxenet",
+ pxe_netif_match,
+ pxe_netif_probe,
+ pxe_netif_init,
+ pxe_netif_get,
+ pxe_netif_put,
+ pxe_netif_end,
+ pxe_ifs,
+ NENTS(pxe_ifs)
+};
+
+struct netif_driver *netif_drivers[] = {
+ &pxenetif,
+ NULL
+};
+
+struct devsw pxedisk = {
+ "pxe",
+ DEVT_NET,
+ pxe_init,
+ pxe_strategy,
+ pxe_open,
+ pxe_close,
+ noioctl,
+ pxe_print,
+ pxe_cleanup
+};
+
+/*
+ * This function is called by the loader to enable PXE support if we
+ * are booted by PXE. The passed in pointer is a pointer to the
+ * PXENV+ structure.
+ */
+void
+pxe_enable(void *pxeinfo)
+{
+ pxenv_p = (pxenv_t *)pxeinfo;
+ pxe_p = (pxe_t *)PTOV(pxenv_p->PXEPtr.segment * 16 +
+ pxenv_p->PXEPtr.offset);
+ pxe_call = NULL;
+}
+
+/*
+ * return true if pxe structures are found/initialized,
+ * also figures out our IP information via the pxe cached info struct
+ */
+static int
+pxe_init(void)
+{
+ t_PXENV_GET_CACHED_INFO *gci_p;
+ int counter;
+ uint8_t checksum;
+ uint8_t *checkptr;
+
+ if(pxenv_p == NULL)
+ return (0);
+
+ /* look for "PXENV+" */
+ if (bcmp((void *)pxenv_p->Signature, S_SIZE("PXENV+"))) {
+ pxenv_p = NULL;
+ return (0);
+ }
+
+ /* make sure the size is something we can handle */
+ if (pxenv_p->Length > sizeof(*pxenv_p)) {
+ printf("PXENV+ structure too large, ignoring\n");
+ pxenv_p = NULL;
+ return (0);
+ }
+
+ /*
+ * do byte checksum:
+ * add up each byte in the structure, the total should be 0
+ */
+ checksum = 0;
+ checkptr = (uint8_t *) pxenv_p;
+ for (counter = 0; counter < pxenv_p->Length; counter++)
+ checksum += *checkptr++;
+ if (checksum != 0) {
+ printf("PXENV+ structure failed checksum, ignoring\n");
+ pxenv_p = NULL;
+ return (0);
+ }
+
+
+ /*
+ * PXENV+ passed, so use that if !PXE is not available or
+ * the checksum fails.
+ */
+ pxe_call = pxenv_call;
+ if (pxenv_p->Version >= 0x0200) {
+ for (;;) {
+ if (bcmp((void *)pxe_p->Signature, S_SIZE("!PXE"))) {
+ pxe_p = NULL;
+ break;
+ }
+ checksum = 0;
+ checkptr = (uint8_t *)pxe_p;
+ for (counter = 0; counter < pxe_p->StructLength;
+ counter++)
+ checksum += *checkptr++;
+ if (checksum != 0) {
+ pxe_p = NULL;
+ break;
+ }
+ pxe_call = bangpxe_call;
+ break;
+ }
+ }
+
+ printf("\nPXE version %d.%d, real mode entry point ",
+ (uint8_t) (pxenv_p->Version >> 8),
+ (uint8_t) (pxenv_p->Version & 0xFF));
+ if (pxe_call == bangpxe_call)
+ printf("@%04x:%04x\n",
+ pxe_p->EntryPointSP.segment,
+ pxe_p->EntryPointSP.offset);
+ else
+ printf("@%04x:%04x\n",
+ pxenv_p->RMEntry.segment, pxenv_p->RMEntry.offset);
+
+ gci_p = (t_PXENV_GET_CACHED_INFO *) scratch_buffer;
+ bzero(gci_p, sizeof(*gci_p));
+ gci_p->PacketType = PXENV_PACKET_TYPE_BINL_REPLY;
+ pxe_call(PXENV_GET_CACHED_INFO);
+ if (gci_p->Status != 0) {
+ pxe_perror(gci_p->Status);
+ pxe_p = NULL;
+ return (0);
+ }
+ bcopy(PTOV((gci_p->Buffer.segment << 4) + gci_p->Buffer.offset),
+ &bootplayer, gci_p->BufferSize);
+ return (1);
+}
+
+
+static int
+pxe_strategy(void *devdata, int flag, daddr_t dblk, size_t size,
+ char *buf, size_t *rsize)
+{
+ return (EIO);
+}
+
+static int
+pxe_open(struct open_file *f, ...)
+{
+ va_list args;
+ char *devname; /* Device part of file name (or NULL). */
+ char temp[FNAME_SIZE];
+ int error = 0;
+ int i;
+
+ va_start(args, f);
+ devname = va_arg(args, char*);
+ va_end(args);
+
+ /* On first open, do netif open, mount, etc. */
+ if (pxe_opens == 0) {
+ /* Find network interface. */
+ if (pxe_sock < 0) {
+ pxe_sock = netif_open(devname);
+ if (pxe_sock < 0) {
+ printf("pxe_open: netif_open() failed\n");
+ return (ENXIO);
+ }
+ if (pxe_debug)
+ printf("pxe_open: netif_open() succeeded\n");
+ }
+ if (rootip.s_addr == 0) {
+ /*
+ * Do a bootp/dhcp request to find out where our
+ * NFS/TFTP server is. Even if we dont get back
+ * the proper information, fall back to the server
+ * which brought us to life and a default rootpath.
+ */
+ bootp(pxe_sock, BOOTP_PXE);
+ if (rootip.s_addr == 0)
+ rootip.s_addr = bootplayer.sip;
+ if (!rootpath[1])
+ strcpy(rootpath, PXENFSROOTPATH);
+
+ for (i = 0; rootpath[i] != '\0' && i < FNAME_SIZE; i++)
+ if (rootpath[i] == ':')
+ break;
+ if (i && i != FNAME_SIZE && rootpath[i] == ':') {
+ rootpath[i++] = '\0';
+ if (inet_addr(&rootpath[0]) != INADDR_NONE)
+ rootip.s_addr = inet_addr(&rootpath[0]);
+ bcopy(&rootpath[i], &temp[0], strlen(&rootpath[i])+1);
+ bcopy(&temp[0], &rootpath[0], strlen(&rootpath[i])+1);
+ }
+ printf("pxe_open: server addr: %s\n", inet_ntoa(rootip));
+ printf("pxe_open: server path: %s\n", rootpath);
+ printf("pxe_open: gateway ip: %s\n", inet_ntoa(gateip));
+
+ setenv("boot.netif.ip", inet_ntoa(myip), 1);
+ setenv("boot.netif.netmask", intoa(netmask), 1);
+ setenv("boot.netif.gateway", inet_ntoa(gateip), 1);
+ if (bootplayer.Hardware == ETHER_TYPE) {
+ sprintf(temp, "%6D", bootplayer.CAddr, ":");
+ setenv("boot.netif.hwaddr", temp, 1);
+ }
+ setenv("boot.nfsroot.server", inet_ntoa(rootip), 1);
+ setenv("boot.nfsroot.path", rootpath, 1);
+ }
+ }
+ pxe_opens++;
+ f->f_devdata = &pxe_sock;
+ return (error);
+}
+
+static int
+pxe_close(struct open_file *f)
+{
+
+#ifdef PXE_DEBUG
+ if (pxe_debug)
+ printf("pxe_close: opens=%d\n", pxe_opens);
+#endif
+
+ /* On last close, do netif close, etc. */
+ f->f_devdata = NULL;
+ /* Extra close call? */
+ if (pxe_opens <= 0)
+ return (0);
+ pxe_opens--;
+ /* Not last close? */
+ if (pxe_opens > 0)
+ return(0);
+
+ /* get an NFS filehandle for our root filesystem */
+ pxe_setnfshandle(rootpath);
+
+ if (pxe_sock >= 0) {
+
+#ifdef PXE_DEBUG
+ if (pxe_debug)
+ printf("pxe_close: calling netif_close()\n");
+#endif
+ netif_close(pxe_sock);
+ pxe_sock = -1;
+ }
+ return (0);
+}
+
+static void
+pxe_print(int verbose)
+{
+ if (pxe_call != NULL) {
+ if (*bootplayer.Sname == '\0') {
+ printf(" "IP_STR":%s\n",
+ IP_ARGS(htonl(bootplayer.sip)),
+ bootplayer.bootfile);
+ } else {
+ printf(" %s:%s\n", bootplayer.Sname,
+ bootplayer.bootfile);
+ }
+ }
+
+ return;
+}
+
+static void
+pxe_cleanup(void)
+{
+#ifdef PXE_DEBUG
+ t_PXENV_UNLOAD_STACK *unload_stack_p =
+ (t_PXENV_UNLOAD_STACK *)scratch_buffer;
+ t_PXENV_UNDI_SHUTDOWN *undi_shutdown_p =
+ (t_PXENV_UNDI_SHUTDOWN *)scratch_buffer;
+#endif
+
+ if (pxe_call == NULL)
+ return;
+
+ pxe_call(PXENV_UNDI_SHUTDOWN);
+
+#ifdef PXE_DEBUG
+ if (pxe_debug && undi_shutdown_p->Status != 0)
+ printf("pxe_cleanup: UNDI_SHUTDOWN failed %x\n",
+ undi_shutdown_p->Status);
+#endif
+
+ pxe_call(PXENV_UNLOAD_STACK);
+
+#ifdef PXE_DEBUG
+ if (pxe_debug && unload_stack_p->Status != 0)
+ printf("pxe_cleanup: UNLOAD_STACK failed %x\n",
+ unload_stack_p->Status);
+#endif
+}
+
+void
+pxe_perror(int err)
+{
+ return;
+}
+
+/*
+ * Reach inside the libstand NFS code and dig out an NFS handle
+ * for the root filesystem.
+ */
+struct nfs_iodesc {
+ struct iodesc *iodesc;
+ off_t off;
+ u_char fh[NFS_FHSIZE];
+ /* structure truncated here */
+};
+extern struct nfs_iodesc nfs_root_node;
+
+static void
+pxe_setnfshandle(char *rootpath)
+{
+ int i;
+ u_char *fh;
+ char buf[2 * NFS_FHSIZE + 3], *cp;
+
+ fh = &nfs_root_node.fh[0];
+ buf[0] = 'X';
+ cp = &buf[1];
+ for (i = 0; i < NFS_FHSIZE; i++, cp += 2)
+ sprintf(cp, "%02x", fh[i]);
+ sprintf(cp, "X");
+ setenv("boot.nfsroot.nfshandle", buf, 1);
+}
+
+void
+pxenv_call(int func)
+{
+#ifdef PXE_DEBUG
+ if (pxe_debug)
+ printf("pxenv_call %x\n", func);
+#endif
+
+ bzero(&v86, sizeof(v86));
+ bzero(data_buffer, sizeof(data_buffer));
+
+ __pxenvseg = pxenv_p->RMEntry.segment;
+ __pxenvoff = pxenv_p->RMEntry.offset;
+
+ v86.ctl = V86_ADDR | V86_CALLF | V86_FLAGS;
+ v86.es = VTOPSEG(scratch_buffer);
+ v86.edi = VTOPOFF(scratch_buffer);
+ v86.addr = (VTOPSEG(__pxenventry) << 16) | VTOPOFF(__pxenventry);
+ v86.ebx = func;
+ v86int();
+ v86.ctl = V86_FLAGS;
+}
+
+void
+bangpxe_call(int func)
+{
+#ifdef PXE_DEBUG
+ if (pxe_debug)
+ printf("bangpxe_call %x\n", func);
+#endif
+
+ bzero(&v86, sizeof(v86));
+ bzero(data_buffer, sizeof(data_buffer));
+
+ __bangpxeseg = pxe_p->EntryPointSP.segment;
+ __bangpxeoff = pxe_p->EntryPointSP.offset;
+
+ v86.ctl = V86_ADDR | V86_CALLF | V86_FLAGS;
+ v86.edx = VTOPSEG(scratch_buffer);
+ v86.eax = VTOPOFF(scratch_buffer);
+ v86.addr = (VTOPSEG(__bangpxeentry) << 16) | VTOPOFF(__bangpxeentry);
+ v86.ebx = func;
+ v86int();
+ v86.ctl = V86_FLAGS;
+}
+
+
+time_t
+getsecs()
+{
+ time_t n = 0;
+ time(&n);
+ return n;
+}
+
+static int
+pxe_netif_match(struct netif *nif, void *machdep_hint)
+{
+ return 1;
+}
+
+
+static int
+pxe_netif_probe(struct netif *nif, void *machdep_hint)
+{
+ t_PXENV_UDP_OPEN *udpopen_p = (t_PXENV_UDP_OPEN *)scratch_buffer;
+
+ if (pxe_call == NULL)
+ return -1;
+
+ bzero(udpopen_p, sizeof(*udpopen_p));
+ udpopen_p->src_ip = bootplayer.yip;
+ pxe_call(PXENV_UDP_OPEN);
+
+ if (udpopen_p->status != 0) {
+ printf("pxe_netif_probe: failed %x\n", udpopen_p->status);
+ return -1;
+ }
+ return 0;
+}
+
+static void
+pxe_netif_end(struct netif *nif)
+{
+ t_PXENV_UDP_CLOSE *udpclose_p = (t_PXENV_UDP_CLOSE *)scratch_buffer;
+ bzero(udpclose_p, sizeof(*udpclose_p));
+
+ pxe_call(PXENV_UDP_CLOSE);
+ if (udpclose_p->status != 0)
+ printf("pxe_end failed %x\n", udpclose_p->status);
+}
+
+static void
+pxe_netif_init(struct iodesc *desc, void *machdep_hint)
+{
+ int i;
+ for (i = 0; i < 6; ++i)
+ desc->myea[i] = bootplayer.CAddr[i];
+ desc->xid = bootplayer.ident;
+}
+
+static int
+pxe_netif_get(struct iodesc *desc, void *pkt, size_t len, time_t timeout)
+{
+ return len;
+}
+
+static int
+pxe_netif_put(struct iodesc *desc, void *pkt, size_t len)
+{
+ return len;
+}
+
+ssize_t
+sendudp(struct iodesc *h, void *pkt, size_t len)
+{
+ t_PXENV_UDP_WRITE *udpwrite_p = (t_PXENV_UDP_WRITE *)scratch_buffer;
+ bzero(udpwrite_p, sizeof(*udpwrite_p));
+
+ udpwrite_p->ip = h->destip.s_addr;
+ udpwrite_p->dst_port = h->destport;
+ udpwrite_p->src_port = h->myport;
+ udpwrite_p->buffer_size = len;
+ udpwrite_p->buffer.segment = VTOPSEG(pkt);
+ udpwrite_p->buffer.offset = VTOPOFF(pkt);
+
+ if (netmask == 0 || SAMENET(myip, h->destip, netmask))
+ udpwrite_p->gw = 0;
+ else
+ udpwrite_p->gw = gateip.s_addr;
+
+ pxe_call(PXENV_UDP_WRITE);
+
+#if 0
+ /* XXX - I dont know why we need this. */
+ delay(1000);
+#endif
+ if (udpwrite_p->status != 0) {
+ /* XXX: This happens a lot. It shouldn't. */
+ if (udpwrite_p->status != 1)
+ printf("sendudp failed %x\n", udpwrite_p->status);
+ return -1;
+ }
+ return len;
+}
+
+ssize_t
+readudp(struct iodesc *h, void *pkt, size_t len, time_t timeout)
+{
+ t_PXENV_UDP_READ *udpread_p = (t_PXENV_UDP_READ *)scratch_buffer;
+ struct udphdr *uh = NULL;
+
+ uh = (struct udphdr *) pkt - 1;
+ bzero(udpread_p, sizeof(*udpread_p));
+
+ udpread_p->dest_ip = h->myip.s_addr;
+ udpread_p->d_port = h->myport;
+ udpread_p->buffer_size = len;
+ udpread_p->buffer.segment = VTOPSEG(data_buffer);
+ udpread_p->buffer.offset = VTOPOFF(data_buffer);
+
+ pxe_call(PXENV_UDP_READ);
+
+#if 0
+ /* XXX - I dont know why we need this. */
+ delay(1000);
+#endif
+ if (udpread_p->status != 0) {
+ /* XXX: This happens a lot. It shouldn't. */
+ if (udpread_p->status != 1)
+ printf("readudp failed %x\n", udpread_p->status);
+ return -1;
+ }
+ bcopy(data_buffer, pkt, udpread_p->buffer_size);
+ uh->uh_sport = udpread_p->s_port;
+ return udpread_p->buffer_size;
+}
diff --git a/sys/boot/i386/libi386/pxe.h b/sys/boot/i386/libi386/pxe.h
new file mode 100644
index 0000000..f1f22aa
--- /dev/null
+++ b/sys/boot/i386/libi386/pxe.h
@@ -0,0 +1,522 @@
+/*
+ * Copyright (c) 2000 Alfred Perlstein <alfred@freebsd.org>
+ * All rights reserved.
+ * Copyright (c) 2000 Paul Saab <ps@freebsd.org>
+ * All rights reserved.
+ * Copyright (c) 2000 John Baldwin <jhb@freebsd.org>
+ * 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.
+ *
+ * $FreeBSD$
+ */
+
+/*
+ * The typedefs and structures declared in this file
+ * clearly violate style(9), the reason for this is to conform to the
+ * typedefs/structure-names used in the Intel literature to avoid confusion.
+ *
+ * It's for your own good. :)
+ */
+
+/* It seems that intel didn't think about ABI,
+ * either that or 16bit ABI != 32bit ABI (which seems reasonable)
+ * I have to thank Intel for the hair loss I incurred trying to figure
+ * out why PXE was mis-reading structures I was passing it (at least
+ * from my point of view)
+ *
+ * Solution: use gcc's '__packed' to correctly align
+ * structures passed into PXE
+ * Question: does this really work for PXE's expected ABI?
+ */
+#define PACKED __packed
+
+#define S_SIZE(s) s, sizeof(s) - 1
+
+#define IP_STR "%d.%d.%d.%d"
+#define IP_ARGS(ip) \
+ (int)(ip >> 24) & 0xff, (int)(ip >> 16) & 0xff, \
+ (int)(ip >> 8) & 0xff, (int)ip & 0xff
+
+#define MAC_STR "%02x:%02x:%02x:%02x:%02x:%02x"
+#define MAC_ARGS(mac) \
+ mac[0], mac[1], mac[2], mac[3], mac[4], mac[5]
+
+#define PXENFSROOTPATH "/pxeroot"
+
+typedef struct {
+ uint16_t offset;
+ uint16_t segment;
+} SEGOFF16_t;
+
+typedef struct {
+ uint16_t Seg_Addr;
+ uint32_t Phy_Addr;
+ uint16_t Seg_Size;
+} SEGDESC_t;
+
+typedef uint16_t SEGSEL_t;
+typedef uint16_t PXENV_STATUS_t;
+typedef uint32_t IP4_t;
+typedef uint32_t ADDR32_t;
+typedef uint16_t UDP_PORT_t;
+
+#define MAC_ADDR_LEN 16
+typedef uint8_t MAC_ADDR[MAC_ADDR_LEN];
+
+/* PXENV+ */
+typedef struct {
+ uint8_t Signature[6]; /* 'PXENV+' */
+ uint16_t Version; /* MSB = major, LSB = minor */
+ uint8_t Length; /* structure length */
+ uint8_t Checksum; /* checksum pad */
+ SEGOFF16_t RMEntry; /* SEG:OFF to PXE entry point */
+ /* don't use PMOffset and PMSelector (from the 2.1 PXE manual) */
+ uint32_t PMOffset; /* Protected mode entry */
+ SEGSEL_t PMSelector; /* Protected mode selector */
+ SEGSEL_t StackSeg; /* Stack segment address */
+ uint16_t StackSize; /* Stack segment size (bytes) */
+ SEGSEL_t BC_CodeSeg; /* BC Code segment address */
+ uint16_t BC_CodeSize; /* BC Code segment size (bytes) */
+ SEGSEL_t BC_DataSeg; /* BC Data segment address */
+ uint16_t BC_DataSize; /* BC Data segment size (bytes) */
+ SEGSEL_t UNDIDataSeg; /* UNDI Data segment address */
+ uint16_t UNDIDataSize; /* UNDI Data segment size (bytes) */
+ SEGSEL_t UNDICodeSeg; /* UNDI Code segment address */
+ uint16_t UNDICodeSize; /* UNDI Code segment size (bytes) */
+ SEGOFF16_t PXEPtr; /* SEG:OFF to !PXE struct,
+ only present when Version > 2.1 */
+} PACKED pxenv_t;
+
+/* !PXE */
+typedef struct {
+ uint8_t Signature[4];
+ uint8_t StructLength;
+ uint8_t StructCksum;
+ uint8_t StructRev;
+ uint8_t reserved_1;
+ SEGOFF16_t UNDIROMID;
+ SEGOFF16_t BaseROMID;
+ SEGOFF16_t EntryPointSP;
+ SEGOFF16_t EntryPointESP;
+ SEGOFF16_t StatusCallout;
+ uint8_t reserved_2;
+ uint8_t SegDescCn;
+ SEGSEL_t FirstSelector;
+ SEGDESC_t Stack;
+ SEGDESC_t UNDIData;
+ SEGDESC_t UNDICode;
+ SEGDESC_t UNDICodeWrite;
+ SEGDESC_t BC_Data;
+ SEGDESC_t BC_Code;
+ SEGDESC_t BC_CodeWrite;
+} PACKED pxe_t;
+
+#define PXENV_START_UNDI 0x0000
+typedef struct {
+ PXENV_STATUS_t Status;
+ uint16_t ax;
+ uint16_t bx;
+ uint16_t dx;
+ uint16_t di;
+ uint16_t es;
+} PACKED t_PXENV_START_UNDI;
+
+#define PXENV_UNDI_STARTUP 0x0001
+typedef struct {
+ PXENV_STATUS_t Status;
+} PACKED t_PXENV_UNDI_STARTUP;
+
+#define PXENV_UNDI_CLEANUP 0x0002
+typedef struct {
+ PXENV_STATUS_t Status;
+} PACKED t_PXENV_UNDI_CLEANUP;
+
+#define PXENV_UNDI_INITIALIZE 0x0003
+typedef struct {
+ PXENV_STATUS_t Status;
+ ADDR32_t ProtocolIni; /* Phys addr of a copy of the driver module */
+ uint8_t reserved[8];
+} PACKED t_PXENV_UNDI_INITALIZE;
+
+
+#define MAXNUM_MCADDR 8
+typedef struct {
+ PXENV_STATUS_t Status;
+ uint16_t MCastAddrCount;
+ MAC_ADDR McastAddr[MAXNUM_MCADDR];
+} PACKED t_PXENV_UNDI_MCAST_ADDRESS;
+
+#define PXENV_UNDI_RESET_ADAPTER 0x0004
+typedef struct {
+ PXENV_STATUS_t Status;
+ t_PXENV_UNDI_MCAST_ADDRESS R_Mcast_Buf;
+} PACKED t_PXENV_UNDI_RESET;
+
+#define PXENV_UNDI_SHUTDOWN 0x0005
+typedef struct {
+ PXENV_STATUS_t Status;
+} PACKED t_PXENV_UNDI_SHUTDOWN;
+
+#define PXENV_UNDI_OPEN 0x0006
+typedef struct {
+ PXENV_STATUS_t Status;
+ uint16_t OpenFlag;
+ uint16_t PktFilter;
+# define FLTR_DIRECTED 0x0001
+# define FLTR_BRDCST 0x0002
+# define FLTR_PRMSCS 0x0003
+# define FLTR_SRC_RTG 0x0004
+
+ t_PXENV_UNDI_MCAST_ADDRESS R_Mcast_Buf;
+} PACKED t_PXENV_UNDI_OPEN;
+
+#define PXENV_UNDI_CLOSE 0x0007
+typedef struct {
+ PXENV_STATUS_t Status;
+} PACKED t_PXENV_UNDI_CLOSE;
+
+#define PXENV_UNDI_TRANSMIT 0x0008
+typedef struct {
+ PXENV_STATUS_t Status;
+ uint8_t Protocol;
+# define P_UNKNOWN 0
+# define P_IP 1
+# define P_ARP 2
+# define P_RARP 3
+
+ uint8_t XmitFlag;
+# define XMT_DESTADDR 0x0000
+# define XMT_BROADCAST 0x0001
+
+ SEGOFF16_t DestAddr;
+ SEGOFF16_t TBD;
+ uint32_t Reserved[2];
+} PACKED t_PXENV_UNDI_TRANSMIT;
+
+#define MAX_DATA_BLKS 8
+typedef struct {
+ uint16_t ImmedLength;
+ SEGOFF16_t Xmit;
+ uint16_t DataBlkCount;
+ struct DataBlk {
+ uint8_t TDPtrType;
+ uint8_t TDRsvdByte;
+ uint16_t TDDataLen;
+ SEGOFF16_t TDDataPtr;
+ } DataBlock[MAX_DATA_BLKS];
+} PACKED t_PXENV_UNDI_TBD;
+
+#define PXENV_UNDI_SET_MCAST_ADDRESS 0x0009
+typedef struct {
+ PXENV_STATUS_t Status;
+ t_PXENV_UNDI_MCAST_ADDRESS R_Mcast_Buf;
+} PACKED t_PXENV_UNDI_SET_MCAST_ADDR;
+
+#define PXENV_UNDI_SET_STATION_ADDRESS 0x000A
+typedef struct {
+ PXENV_STATUS_t Status;
+ MAC_ADDR StationAddress; /* Temp MAC addres to use */
+} PACKED t_PXENV_UNDI_SET_STATION_ADDR;
+
+#define PXENV_UNDI_SET_PACKET_FILTER 0x000B
+typedef struct {
+ PXENV_STATUS_t Status;
+ uint8_t filter; /* see UNDI_OPEN (0x0006) */
+} PACKED t_PXENV_UNDI_SET_PACKET_FILTER;
+
+#define PXENV_UNDI_GET_INFORMATION 0x000C
+typedef struct {
+ PXENV_STATUS_t Status;
+ uint16_t BaseIo; /* Adapter base I/O address */
+ uint16_t IntNumber; /* Adapter IRQ number */
+ uint16_t MaxTranUnit; /* Adapter maximum transmit unit */
+ uint16_t HwType; /* Type of protocol at the hardware addr */
+# define ETHER_TYPE 1
+# define EXP_ETHER_TYPE 2
+# define IEEE_TYPE 6
+# define ARCNET_TYPE 7
+
+ uint16_t HwAddrLen; /* Length of hardware address */
+ MAC_ADDR CurrentNodeAddress; /* Current hardware address */
+ MAC_ADDR PermNodeAddress; /* Permanent hardware address */
+ SEGSEL_t ROMAddress; /* Real mode ROM segment address */
+ uint16_t RxBufCt; /* Receive queue length */
+ uint16_t TxBufCt; /* Transmit queue length */
+} PACKED t_PXENV_UNDI_GET_INFORMATION;
+
+#define PXENV_UNDI_GET_STATISTICS 0x000D
+typedef struct {
+ PXENV_STATUS_t Status;
+ uint32_t XmitGoodFrames; /* Number of successful transmissions */
+ uint32_t RcvGoodFrames; /* Number of good frames received */
+ uint32_t RcvCRCErrors; /* Number of frames with CRC errors */
+ uint32_t RcvResourceErrors; /* Number of frames dropped */
+} PACKED t_PXENV_UNDI_GET_STATISTICS;
+
+#define PXENV_UNDI_CLEAR_STATISTICS 0x000E
+typedef struct {
+ PXENV_STATUS_t Status;
+} PACKED t_PXENV_UNDI_CLEAR_STATISTICS;
+
+#define PXENV_UNDI_INITIATE_DIAGS 0x000F
+typedef struct {
+ PXENV_STATUS_t Status;
+} PACKED t_PXENV_UNDI_INITIATE_DIAGS;
+
+#define PXENV_UNDI_FORCE_INTERRUPT 0x0010
+typedef struct {
+ PXENV_STATUS_t Status;
+} PACKED t_PXENV_UNDI_FORCE_INTERRUPT;
+
+#define PXENV_UNDI_GET_MCAST_ADDRESS 0x0011
+typedef struct {
+ PXENV_STATUS_t Status;
+ IP4_t InetAddr; /* IP mulicast address */
+ MAC_ADDR MediaAddr; /* MAC multicast address */
+} PACKED t_PXENV_UNDI_GET_MCAST_ADDR;
+
+#define PXENV_UNDI_GET_NIC_TYPE 0x0012
+typedef struct {
+ PXENV_STATUS_t Status;
+ uint8_t NicType; /* Type of NIC */
+# define PCI_NIC 2
+# define PnP_NIC 3
+# define CardBus_NIC 4
+
+ union {
+ struct {
+ uint16_t Vendor_ID;
+ uint16_t Dev_ID;
+ uint8_t Base_Class;
+ uint8_t Sub_Class;
+ uint8_t Prog_Intf;
+ uint8_t Rev;
+ uint16_t BusDevFunc;
+ uint16_t SubVendor_ID;
+ uint16_t SubDevice_ID;
+ } pci, cardbus;
+ struct {
+ uint32_t EISA_Dev_ID;
+ uint8_t Base_Class;
+ uint8_t Sub_Class;
+ uint8_t Prog_Intf;
+ uint16_t CardSelNum;
+ } pnp;
+ } info;
+} PACKED t_PXENV_UNDI_GET_NIC_TYPE;
+
+#define PXENV_UNDI_GET_IFACE_INFO 0x0013
+typedef struct {
+ PXENV_STATUS_t Status;
+ uint8_t IfaceType[16]; /* Name of MAC type in ASCII. */
+ uint32_t LinkSpeed; /* Defined in NDIS 2.0 spec */
+ uint32_t ServiceFlags; /* Defined in NDIS 2.0 spec */
+ uint32_t Reserved[4]; /* must be 0 */
+} PACKED t_PXENV_UNDI_GET_NDIS_INFO;
+
+#define PXENV_UNDI_ISR 0x0014
+typedef struct {
+ PXENV_STATUS_t Status;
+ uint16_t FuncFlag; /* PXENV_UNDI_ISR_OUT_xxx */
+ uint16_t BufferLength; /* Length of Frame */
+ uint16_t FrameLength; /* Total length of reciever frame */
+ uint16_t FrameHeaderLength; /* Length of the media header in Frame */
+ SEGOFF16_t Frame; /* receive buffer */
+ uint8_t ProtType; /* Protocol type */
+ uint8_t PktType; /* Packet Type */
+# define PXENV_UNDI_ISR_IN_START 1
+# define PXENV_UNDI_ISR_IN_PROCESS 2
+# define PXENV_UNDI_ISR_IN_GET_NEXT 3
+
+ /* one of these will be returned for PXENV_UNDI_ISR_IN_START */
+# define PXENV_UNDI_ISR_OUT_OURS 0
+# define PXENV_UNDI_ISR_OUT_NOT_OUTS 1
+
+ /*
+ * one of these will bre returnd for PXEND_UNDI_ISR_IN_PROCESS
+ * and PXENV_UNDI_ISR_IN_GET_NEXT
+ */
+# define PXENV_UNDI_ISR_OUT_DONE 0
+# define PXENV_UNDI_ISR_OUT_TRANSMIT 2
+# define PXENV_UNDI_ISR_OUT_RECIEVE 3
+# define PXENV_UNDI_ISR_OUT_BUSY 4
+} PACKED t_PXENV_UNDI_ISR;
+
+#define PXENV_STOP_UNDI 0x0015
+typedef struct {
+ PXENV_STATUS_t Status;
+} PACKED t_PXENV_STOP_UNDI;
+
+#define PXENV_TFTP_OPEN 0x0020
+typedef struct {
+ PXENV_STATUS_t Status;
+ IP4_t ServerIPAddress;
+ IP4_t GatewayIPAddress;
+ uint8_t FileName[128];
+ UDP_PORT_t TFTPPort;
+ uint16_t PacketSize;
+} PACKED t_PXENV_TFTP_OPEN;
+
+#define PXENV_TFTP_CLOSE 0x0021
+typedef struct {
+ PXENV_STATUS_t Status;
+} PACKED t_PXENV_TFTP_CLOSE;
+
+#define PXENV_TFTP_READ 0x0022
+typedef struct {
+ PXENV_STATUS_t Status;
+ uint16_t PacketNumber;
+ uint16_t BufferSize;
+ SEGOFF16_t Buffer;
+} PACKED t_PXENV_TFTP_READ;
+
+#define PXENV_TFTP_READ_FILE 0x0023
+typedef struct {
+ PXENV_STATUS_t Status;
+ uint8_t FileName[128];
+ uint32_t BufferSize;
+ ADDR32_t Buffer;
+ IP4_t ServerIPAddress;
+ IP4_t GatewayIPAdress;
+ IP4_t McastIPAdress;
+ UDP_PORT_t TFTPClntPort;
+ UDP_PORT_t TFTPSrvPort;
+ uint16_t TFTPOpenTimeOut;
+ uint16_t TFTPReopenDelay;
+} PACKED t_PXENV_TFTP_READ_FILE;
+
+#define PXENV_TFTP_GET_FSIZE 0x0025
+typedef struct {
+ PXENV_STATUS_t Status;
+ IP4_t ServerIPAddress;
+ IP4_t GatewayIPAdress;
+ uint8_t FileName[128];
+ uint32_t FileSize;
+} PACKED t_PXENV_TFTP_GET_FSIZE;
+
+#define PXENV_UDP_OPEN 0x0030
+typedef struct {
+ PXENV_STATUS_t status;
+ IP4_t src_ip; /* IP address of this station */
+} PACKED t_PXENV_UDP_OPEN;
+
+#define PXENV_UDP_CLOSE 0x0031
+typedef struct {
+ PXENV_STATUS_t status;
+} PACKED t_PXENV_UDP_CLOSE;
+
+#define PXENV_UDP_READ 0x0032
+typedef struct {
+ PXENV_STATUS_t status;
+ IP4_t src_ip; /* IP of sender */
+ IP4_t dest_ip; /* Only accept packets sent to this IP */
+ UDP_PORT_t s_port; /* UDP source port of sender */
+ UDP_PORT_t d_port; /* Only accept packets sent to this port */
+ uint16_t buffer_size; /* Size of the packet buffer */
+ SEGOFF16_t buffer; /* SEG:OFF to the packet buffer */
+} PACKED t_PXENV_UDP_READ;
+
+#define PXENV_UDP_WRITE 0x0033
+typedef struct {
+ PXENV_STATUS_t status;
+ IP4_t ip; /* dest ip addr */
+ IP4_t gw; /* ip gateway */
+ UDP_PORT_t src_port; /* source udp port */
+ UDP_PORT_t dst_port; /* destination udp port */
+ uint16_t buffer_size; /* Size of the packet buffer */
+ SEGOFF16_t buffer; /* SEG:OFF to the packet buffer */
+} PACKED t_PXENV_UDP_WRITE;
+
+#define PXENV_UNLOAD_STACK 0x0070
+typedef struct {
+ PXENV_STATUS_t Status;
+ uint8_t reserved[10];
+} PACKED t_PXENV_UNLOAD_STACK;
+
+
+#define PXENV_GET_CACHED_INFO 0x0071
+typedef struct {
+ PXENV_STATUS_t Status;
+ uint16_t PacketType; /* type (defined right here) */
+# define PXENV_PACKET_TYPE_DHCP_DISCOVER 1
+# define PXENV_PACKET_TYPE_DHCP_ACK 2
+# define PXENV_PACKET_TYPE_BINL_REPLY 3
+ uint16_t BufferSize; /* max to copy, leave at 0 for pointer */
+ SEGOFF16_t Buffer; /* copy to, leave at 0 for pointer */
+ uint16_t BufferLimit; /* max size of buffer in BC dataseg ? */
+} PACKED t_PXENV_GET_CACHED_INFO;
+
+
+/* structure filled in by PXENV_GET_CACHED_INFO
+ * (how we determine which IP we downloaded the initial bootstrap from)
+ * words can't describe...
+ */
+typedef struct {
+ uint8_t opcode;
+# define BOOTP_REQ 1
+# define BOOTP_REP 2
+ uint8_t Hardware; /* hardware type */
+ uint8_t Hardlen; /* hardware addr len */
+ uint8_t Gatehops; /* zero it */
+ uint32_t ident; /* random number chosen by client */
+ uint16_t seconds; /* seconds since did initial bootstrap */
+ uint16_t Flags; /* seconds since did initial bootstrap */
+# define BOOTP_BCAST 0x8000 /* ? */
+ IP4_t cip; /* Client IP */
+ IP4_t yip; /* Your IP */
+ IP4_t sip; /* IP to use for next boot stage */
+ IP4_t gip; /* Relay IP ? */
+ MAC_ADDR CAddr; /* Client hardware address */
+ uint8_t Sname[64]; /* Server's hostname (Optional) */
+ uint8_t bootfile[128]; /* boot filename */
+ union {
+# if 1
+# define BOOTP_DHCPVEND 1024 /* DHCP extended vendor field size */
+# else
+# define BOOTP_DHCPVEND 312 /* DHCP standard vendor field size */
+# endif
+ uint8_t d[BOOTP_DHCPVEND]; /* raw array of vendor/dhcp options */
+ struct {
+ uint8_t magic[4]; /* DHCP magic cookie */
+# ifndef VM_RFC1048
+# define VM_RFC1048 0x63825363L /* ? */
+# endif
+ uint32_t flags; /* bootp flags/opcodes */
+ uint8_t pad[56]; /* I don't think intel knows what a
+ union does... */
+ } v;
+ } vendor;
+} PACKED BOOTPLAYER;
+
+#define PXENV_RESTART_TFTP 0x0073
+#define t_PXENV_RESTART_TFTP t_PXENV_TFTP_READ_FILE
+
+#define PXENV_START_BASE 0x0075
+typedef struct {
+ PXENV_STATUS_t Status;
+} PACKED t_PXENV_START_BASE;
+
+#define PXENV_STOP_BASE 0x0076
+typedef struct {
+ PXENV_STATUS_t Status;
+} PACKED t_PXENV_STOP_BASE;
diff --git a/sys/boot/i386/libi386/pxetramp.s b/sys/boot/i386/libi386/pxetramp.s
new file mode 100644
index 0000000..dcf1441
--- /dev/null
+++ b/sys/boot/i386/libi386/pxetramp.s
@@ -0,0 +1,38 @@
+#
+# Copyright (c) 2000 Peter Wemm
+# All rights reserved.
+#
+# Redistribution and use in source and binary forms are freely
+# permitted provided that the above copyright notice and this
+# paragraph and the following disclaimer are duplicated in all
+# such forms.
+#
+# This software is provided "AS IS" and without any express or
+# implied warranties, including, without limitation, the implied
+# warranties of merchantability and fitness for a particular
+# purpose.
+#
+# $FreeBSD$
+
+# ph33r this
+
+ .globl __bangpxeentry, __bangpxeseg, __bangpxeoff
+ .globl __pxenventry, __pxenvseg, __pxenvoff
+
+ .code16
+ .p2align 4,0x90
+__bangpxeentry:
+ push %dx # seg:data
+ push %ax # off:data
+ push %bx # int16 func
+ .byte 0x9a # far call
+__bangpxeoff: .word 0x0000 # offset
+__bangpxeseg: .word 0x0000 # segment
+ add $6, %sp # restore stack
+ .byte 0xcb # to vm86int
+#
+__pxenventry:
+ .byte 0x9a # far call
+__pxenvoff: .word 0x0000 # offset
+__pxenvseg: .word 0x0000 # segment
+ .byte 0xcb # to vm86int
diff --git a/sys/boot/i386/libi386/time.c b/sys/boot/i386/libi386/time.c
new file mode 100644
index 0000000..1db9a4a
--- /dev/null
+++ b/sys/boot/i386/libi386/time.c
@@ -0,0 +1,83 @@
+/*-
+ * Copyright (c) 1998 Michael Smith <msmith@freebsd.org>
+ * 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 <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <stand.h>
+#include <btxv86.h>
+#include "bootstrap.h"
+#include "libi386.h"
+
+/*
+ * Return the time in seconds since the beginning of the day.
+ *
+ * If we pass midnight, don't wrap back to 0.
+ *
+ * XXX uses undocumented BCD support from libstand.
+ */
+
+time_t
+time(time_t *t)
+{
+ static time_t lasttime, now;
+ int hr, minute, sec;
+
+ v86.ctl = 0;
+ v86.addr = 0x1a; /* int 0x1a, function 2 */
+ v86.eax = 0x0200;
+ v86int();
+
+ hr = bcd2bin((v86.ecx & 0xff00) >> 8); /* hour in %ch */
+ minute = bcd2bin(v86.ecx & 0xff); /* minute in %cl */
+ sec = bcd2bin((v86.edx & 0xff00) >> 8); /* second in %dh */
+
+ now = hr * 3600 + minute * 60 + sec;
+ if (now < lasttime)
+ now += 24 * 3600;
+ lasttime = now;
+
+ if (t != NULL)
+ *t = now;
+ return(now);
+}
+
+/*
+ * Use the BIOS Wait function to pause for (period) microseconds.
+ *
+ * Resolution of this function is variable, but typically around
+ * 1ms.
+ */
+void
+delay(int period)
+{
+ v86.ctl = 0;
+ v86.addr = 0x15; /* int 0x15, function 0x86 */
+ v86.eax = 0x8600;
+ v86.ecx = period >> 16;
+ v86.edx = period & 0xffff;
+ v86int();
+}
diff --git a/sys/boot/i386/libi386/vidconsole.c b/sys/boot/i386/libi386/vidconsole.c
new file mode 100644
index 0000000..bea6b1e
--- /dev/null
+++ b/sys/boot/i386/libi386/vidconsole.c
@@ -0,0 +1,632 @@
+/*-
+ * Copyright (c) 1998 Michael Smith (msmith@freebsd.org)
+ * Copyright (c) 1997 Kazutaka YOKOTA (yokota@zodiac.mech.utsunomiya-u.ac.jp)
+ * 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.
+ *
+ * Id: probe_keyboard.c,v 1.13 1997/06/09 05:10:55 bde Exp
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <stand.h>
+#include <bootstrap.h>
+#include <btxv86.h>
+#include <machine/psl.h>
+#include "libi386.h"
+
+#if KEYBOARD_PROBE
+#include <machine/cpufunc.h>
+
+static int probe_keyboard(void);
+#endif
+static void vidc_probe(struct console *cp);
+static int vidc_init(int arg);
+static void vidc_putchar(int c);
+static int vidc_getchar(void);
+static int vidc_ischar(void);
+
+static int vidc_started;
+
+#ifdef TERM_EMU
+#define MAXARGS 8
+#define DEFAULT_FGCOLOR 7
+#define DEFAULT_BGCOLOR 0
+
+void end_term(void);
+void bail_out(int c);
+void vidc_term_emu(int c);
+void get_pos(void);
+void curs_move(int x, int y);
+void write_char(int c, int fg, int bg);
+void scroll_up(int rows, int fg, int bg);
+void CD(void);
+void CM(void);
+void HO(void);
+
+static int args[MAXARGS], argc;
+static int fg_c, bg_c, curx, cury;
+static int esc;
+#endif
+
+
+struct console vidconsole = {
+ "vidconsole",
+ "internal video/keyboard",
+ 0,
+ vidc_probe,
+ vidc_init,
+ vidc_putchar,
+ vidc_getchar,
+ vidc_ischar
+};
+
+static void
+vidc_probe(struct console *cp)
+{
+
+ /* look for a keyboard */
+#if KEYBOARD_PROBE
+ if (probe_keyboard())
+#endif
+ {
+
+ cp->c_flags |= C_PRESENTIN;
+ }
+
+ /* XXX for now, always assume we can do BIOS screen output */
+ cp->c_flags |= C_PRESENTOUT;
+}
+
+static int
+vidc_init(int arg)
+{
+ int i;
+
+ if (vidc_started && arg == 0)
+ return (0);
+ vidc_started = 1;
+#ifdef TERM_EMU
+ /* Init terminal emulator */
+ end_term();
+ get_pos();
+ curs_move(curx, cury);
+ fg_c = DEFAULT_FGCOLOR;
+ bg_c = DEFAULT_BGCOLOR;
+#endif
+ for (i = 0; i < 10 && vidc_ischar(); i++)
+ (void)vidc_getchar();
+ return (0); /* XXX reinit? */
+}
+
+static void
+vidc_biosputchar(int c)
+{
+
+ v86.ctl = 0;
+ v86.addr = 0x10;
+ v86.eax = 0xe00 | (c & 0xff);
+ v86.ebx = 0x7;
+ v86int();
+}
+
+static void
+vidc_rawputchar(int c)
+{
+ int i;
+
+ if (c == '\t')
+ /* lame tab expansion */
+ for (i = 0; i < 8; i++)
+ vidc_rawputchar(' ');
+ else {
+#ifndef TERM_EMU
+ vidc_biosputchar(c);
+#else
+ /* Emulate AH=0eh (teletype output) */
+ switch(c) {
+ case '\a':
+ vidc_biosputchar(c);
+ return;
+ case '\r':
+ curx = 0;
+ curs_move(curx, cury);
+ return;
+ case '\n':
+ cury++;
+ if (cury > 24) {
+ scroll_up(1, fg_c, bg_c);
+ cury--;
+ } else {
+ curs_move(curx, cury);
+ }
+ return;
+ case '\b':
+ if (curx > 0) {
+ curx--;
+ curs_move(curx, cury);
+ /* write_char(' ', fg_c, bg_c); XXX destructive(!) */
+ return;
+ }
+ return;
+ default:
+ write_char(c, fg_c, bg_c);
+ curx++;
+ if (curx > 79) {
+ curx = 0;
+ cury++;
+ }
+ if (cury > 24) {
+ curx = 0;
+ scroll_up(1, fg_c, bg_c);
+ cury--;
+ }
+ }
+ curs_move(curx, cury);
+#endif
+ }
+}
+
+#ifdef TERM_EMU
+
+/* Get cursor position on the screen. Result is in edx. Sets
+ * curx and cury appropriately.
+ */
+void
+get_pos(void)
+{
+
+ v86.ctl = 0;
+ v86.addr = 0x10;
+ v86.eax = 0x0300;
+ v86.ebx = 0x0;
+ v86int();
+ curx = v86.edx & 0x00ff;
+ cury = (v86.edx & 0xff00) >> 8;
+}
+
+/* Move cursor to x rows and y cols (0-based). */
+void
+curs_move(int x, int y)
+{
+
+ v86.ctl = 0;
+ v86.addr = 0x10;
+ v86.eax = 0x0200;
+ v86.ebx = 0x0;
+ v86.edx = ((0x00ff & y) << 8) + (0x00ff & x);
+ v86int();
+ curx = x;
+ cury = y;
+ /* If there is ctrl char at this position, cursor would be invisible.
+ * Make it a space instead.
+ */
+ v86.ctl = 0;
+ v86.addr = 0x10;
+ v86.eax = 0x0800;
+ v86.ebx = 0x0;
+ v86int();
+#define isvisible(c) (((c) >= 32) && ((c) < 255))
+ if (!isvisible(v86.eax & 0x00ff)) {
+ write_char(' ', fg_c, bg_c);
+ }
+}
+
+/* Scroll up the whole window by a number of rows. If rows==0,
+ * clear the window. fg and bg are attributes for the new lines
+ * inserted in the window.
+ */
+void
+scroll_up(int rows, int fgcol, int bgcol)
+{
+
+ if (rows == 0)
+ rows = 25;
+ v86.ctl = 0;
+ v86.addr = 0x10;
+ v86.eax = 0x0600 + (0x00ff & rows);
+ v86.ebx = (bgcol << 12) + (fgcol << 8);
+ v86.ecx = 0x0;
+ v86.edx = 0x184f;
+ v86int();
+}
+
+/* Write character and attribute at cursor position. */
+void
+write_char(int c, int fgcol, int bgcol)
+{
+
+ v86.ctl = 0;
+ v86.addr = 0x10;
+ v86.eax = 0x0900 + (0x00ff & c);
+ v86.ebx = (bgcol << 4) + fgcol;
+ v86.ecx = 0x1;
+ v86int();
+}
+
+/**************************************************************/
+/*
+ * Screen manipulation functions. They use accumulated data in
+ * args[] and argc variables.
+ *
+ */
+
+/* Clear display from current position to end of screen */
+void
+CD(void)
+{
+
+ get_pos();
+ if (curx > 0) {
+ v86.ctl = 0;
+ v86.addr = 0x10;
+ v86.eax = 0x0600;
+ v86.ebx = (bg_c << 4) + fg_c;
+ v86.ecx = (cury << 8) + curx;
+ v86.edx = (cury << 8) + 79;
+ v86int();
+ if (++cury > 24) {
+ end_term();
+ return;
+ }
+ }
+ v86.ctl = 0;
+ v86.addr = 0x10;
+ v86.eax = 0x0600;
+ v86.ebx = (bg_c << 4) + fg_c;
+ v86.ecx = (cury << 8) + 0;
+ v86.edx = (24 << 8) + 79;
+ v86int();
+ end_term();
+}
+
+/* Absolute cursor move to args[0] rows and args[1] columns
+ * (the coordinates are 1-based).
+ */
+void
+CM(void)
+{
+
+ if (args[0] > 0)
+ args[0]--;
+ if (args[1] > 0)
+ args[1]--;
+ curs_move(args[1], args[0]);
+ end_term();
+}
+
+/* Home cursor (left top corner) */
+void
+HO(void)
+{
+
+ argc = 1;
+ args[0] = args[1] = 1;
+ CM();
+}
+
+/* Clear internal state of the terminal emulation code */
+void
+end_term(void)
+{
+
+ esc = 0;
+ argc = -1;
+}
+
+/* Gracefully exit ESC-sequence processing in case of misunderstanding */
+void
+bail_out(int c)
+{
+ char buf[16], *ch;
+ int i;
+
+ if (esc) {
+ vidc_rawputchar('\033');
+ if (esc != '\033')
+ vidc_rawputchar(esc);
+ for (i = 0; i <= argc; ++i) {
+ sprintf(buf, "%d", args[i]);
+ ch = buf;
+ while (*ch)
+ vidc_rawputchar(*ch++);
+ }
+ }
+ vidc_rawputchar(c);
+ end_term();
+}
+
+static void
+get_arg(c)
+{
+
+ if (argc < 0)
+ argc = 0;
+ args[argc] *= 10;
+ args[argc] += c - '0';
+}
+
+/* Emulate basic capabilities of cons25 terminal */
+void
+vidc_term_emu(int c)
+{
+ static int ansi_col[] = {
+ 0, 4, 2, 6, 1, 5, 3, 7,
+ };
+ int t;
+ int i;
+
+ switch (esc) {
+ case 0:
+ switch (c) {
+ case '\033':
+ esc = c;
+ break;
+ default:
+ vidc_rawputchar(c);
+ break;
+ }
+ break;
+
+ case '\033':
+ switch (c) {
+ case '[':
+ esc = c;
+ args[0] = 0;
+ argc = -1;
+ break;
+ default:
+ bail_out(c);
+ break;
+ }
+ break;
+
+ case '[':
+ switch (c) {
+ case ';':
+ if (argc < 0) /* XXX */
+ argc = 0;
+ else if (argc + 1 >= MAXARGS)
+ bail_out(c);
+ else
+ args[++argc] = 0;
+ break;
+ case 'H':
+ if (argc < 0)
+ HO();
+ else if (argc == 1)
+ CM();
+ else
+ bail_out(c);
+ break;
+ case 'J':
+ if (argc < 0)
+ CD();
+ else
+ bail_out(c);
+ break;
+ case 'm':
+ if (argc < 0) {
+ fg_c = DEFAULT_FGCOLOR;
+ bg_c = DEFAULT_BGCOLOR;
+ }
+ for (i = 0; i <= argc; ++i) {
+ switch (args[i]) {
+ case 0: /* back to normal */
+ fg_c = DEFAULT_FGCOLOR;
+ bg_c = DEFAULT_BGCOLOR;
+ break;
+ case 1: /* bold */
+ fg_c |= 0x8;
+ break;
+ case 4: /* underline */
+ case 5: /* blink */
+ bg_c |= 0x8;
+ break;
+ case 7: /* reverse */
+ t = fg_c;
+ fg_c = bg_c;
+ bg_c = t;
+ break;
+ case 30: case 31: case 32: case 33:
+ case 34: case 35: case 36: case 37:
+ fg_c = ansi_col[args[i] - 30];
+ break;
+ case 39: /* normal */
+ fg_c = DEFAULT_FGCOLOR;
+ break;
+ case 40: case 41: case 42: case 43:
+ case 44: case 45: case 46: case 47:
+ bg_c = ansi_col[args[i] - 40];
+ break;
+ case 49: /* normal */
+ bg_c = DEFAULT_BGCOLOR;
+ break;
+ }
+ }
+ end_term();
+ break;
+ default:
+ if (isdigit(c))
+ get_arg(c);
+ else
+ bail_out(c);
+ break;
+ }
+ break;
+
+ default:
+ bail_out(c);
+ break;
+ }
+}
+#endif
+
+static void
+vidc_putchar(int c)
+{
+#ifdef TERM_EMU
+ vidc_term_emu(c);
+#else
+ vidc_rawputchar(c);
+#endif
+}
+
+static int
+vidc_getchar(void)
+{
+
+ if (vidc_ischar()) {
+ v86.ctl = 0;
+ v86.addr = 0x16;
+ v86.eax = 0x0;
+ v86int();
+ return (v86.eax & 0xff);
+ } else {
+ return (-1);
+ }
+}
+
+static int
+vidc_ischar(void)
+{
+
+ v86.ctl = V86_FLAGS;
+ v86.addr = 0x16;
+ v86.eax = 0x100;
+ v86int();
+ return (!(v86.efl & PSL_Z));
+}
+
+#if KEYBOARD_PROBE
+
+#define PROBE_MAXRETRY 5
+#define PROBE_MAXWAIT 400
+#define IO_DUMMY 0x84
+#define IO_KBD 0x060 /* 8042 Keyboard */
+
+/* selected defines from kbdio.h */
+#define KBD_STATUS_PORT 4 /* status port, read */
+#define KBD_DATA_PORT 0 /* data port, read/write
+ * also used as keyboard command
+ * and mouse command port
+ */
+#define KBDC_ECHO 0x00ee
+#define KBDS_ANY_BUFFER_FULL 0x0001
+#define KBDS_INPUT_BUFFER_FULL 0x0002
+#define KBD_ECHO 0x00ee
+
+/* 7 microsec delay necessary for some keyboard controllers */
+static void
+delay7(void)
+{
+ /*
+ * I know this is broken, but no timer is available yet at this stage...
+ * See also comments in `delay1ms()'.
+ */
+ inb(IO_DUMMY); inb(IO_DUMMY);
+ inb(IO_DUMMY); inb(IO_DUMMY);
+ inb(IO_DUMMY); inb(IO_DUMMY);
+}
+
+/*
+ * This routine uses an inb to an unused port, the time to execute that
+ * inb is approximately 1.25uS. This value is pretty constant across
+ * all CPU's and all buses, with the exception of some PCI implentations
+ * that do not forward this I/O address to the ISA bus as they know it
+ * is not a valid ISA bus address, those machines execute this inb in
+ * 60 nS :-(.
+ *
+ */
+static void
+delay1ms(void)
+{
+ int i = 800;
+ while (--i >= 0)
+ (void)inb(0x84);
+}
+
+/*
+ * We use the presence/absence of a keyboard to determine whether the internal
+ * console can be used for input.
+ *
+ * Perform a simple test on the keyboard; issue the ECHO command and see
+ * if the right answer is returned. We don't do anything as drastic as
+ * full keyboard reset; it will be too troublesome and take too much time.
+ */
+static int
+probe_keyboard(void)
+{
+ int retry = PROBE_MAXRETRY;
+ int wait;
+ int i;
+
+ while (--retry >= 0) {
+ /* flush any noise */
+ while (inb(IO_KBD + KBD_STATUS_PORT) & KBDS_ANY_BUFFER_FULL) {
+ delay7();
+ inb(IO_KBD + KBD_DATA_PORT);
+ delay1ms();
+ }
+
+ /* wait until the controller can accept a command */
+ for (wait = PROBE_MAXWAIT; wait > 0; --wait) {
+ if (((i = inb(IO_KBD + KBD_STATUS_PORT))
+ & (KBDS_INPUT_BUFFER_FULL | KBDS_ANY_BUFFER_FULL)) == 0)
+ break;
+ if (i & KBDS_ANY_BUFFER_FULL) {
+ delay7();
+ inb(IO_KBD + KBD_DATA_PORT);
+ }
+ delay1ms();
+ }
+ if (wait <= 0)
+ continue;
+
+ /* send the ECHO command */
+ outb(IO_KBD + KBD_DATA_PORT, KBDC_ECHO);
+
+ /* wait for a response */
+ for (wait = PROBE_MAXWAIT; wait > 0; --wait) {
+ if (inb(IO_KBD + KBD_STATUS_PORT) & KBDS_ANY_BUFFER_FULL)
+ break;
+ delay1ms();
+ }
+ if (wait <= 0)
+ continue;
+
+ delay7();
+ i = inb(IO_KBD + KBD_DATA_PORT);
+#ifdef PROBE_KBD_BEBUG
+ printf("probe_keyboard: got 0x%x.\n", i);
+#endif
+ if (i == KBD_ECHO) {
+ /* got the right answer */
+ return (0);
+ }
+ }
+
+ return (1);
+}
+#endif /* KEYBOARD_PROBE */
diff --git a/sys/boot/i386/loader/Makefile b/sys/boot/i386/loader/Makefile
new file mode 100644
index 0000000..670bd9f
--- /dev/null
+++ b/sys/boot/i386/loader/Makefile
@@ -0,0 +1,130 @@
+# $FreeBSD$
+
+PROG= loader
+STRIP=
+NEWVERSWHAT= "bootstrap loader" i386
+BINDIR?= /boot
+INSTALLFLAGS= -b
+
+# architecture-specific loader code
+SRCS= main.c conf.c
+
+CFLAGS+= -ffreestanding
+# Enable PXE TFTP or NFS support, not both.
+.if defined(LOADER_TFTP_SUPPORT)
+CFLAGS+= -DLOADER_TFTP_SUPPORT
+.else
+CFLAGS+= -DLOADER_NFS_SUPPORT
+.endif
+
+# Enable PnP and ISA-PnP code.
+HAVE_PNP= yes
+HAVE_ISABUS= yes
+
+.if !defined(NOFORTH)
+# Enable BootForth
+BOOT_FORTH= yes
+CFLAGS+= -DBOOT_FORTH -I${.CURDIR}/../../ficl -I${.CURDIR}/../../ficl/i386
+.if exists(${.OBJDIR}/../../ficl/libficl.a)
+LIBFICL= ${.OBJDIR}/../../ficl/libficl.a
+.else
+LIBFICL= ${.CURDIR}/../../ficl/libficl.a
+.endif
+.endif
+
+.if defined(LOADER_BZIP2_SUPPORT)
+CFLAGS+= -DLOADER_BZIP2_SUPPORT
+.endif
+.if !defined(LOADER_NO_GZIP_SUPPORT)
+CFLAGS+= -DLOADER_GZIP_SUPPORT
+.endif
+
+# Always add MI sources
+.PATH: ${.CURDIR}/../../common
+.include <${.CURDIR}/../../common/Makefile.inc>
+CFLAGS+= -I${.CURDIR}/../../common
+CFLAGS+= -I${.CURDIR}/../../.. -I.
+
+CLEANFILES+= vers.c vers.o ${PROG}.list ${PROG}.bin ${PROG}.sym ${PROG}.help
+
+CFLAGS+= -Wall
+LDFLAGS= -nostdlib -static -Ttext 0x0
+
+# i386 standalone support library
+LIBI386= ${.OBJDIR}/../libi386/libi386.a
+CFLAGS+= -I${.CURDIR}/..
+
+# where to get libstand from
+#XXX need a better way to do this
+LIBSTAND= ${.CURDIR}/../../../../lib/libstand/libstand.a
+.if !exists(${LIBSTAND})
+LIBSTAND= ${.OBJDIR}/../../../../lib/libstand/libstand.a
+.if !exists(${LIBSTAND})
+LIBSTAND= -lstand
+.endif
+.endif
+CFLAGS+= -I${.CURDIR}/../../../../lib/libstand/
+
+# BTX components
+.if exists(${.OBJDIR}/../btx)
+BTXDIR= ${.OBJDIR}/../btx
+.else
+BTXDIR= ${.CURDIR}/../btx
+.endif
+BTXLDR= ${BTXDIR}/btxldr/btxldr
+BTXKERN= ${BTXDIR}/btx/btx
+BTXCRT= ${BTXDIR}/lib/crt0.o
+CFLAGS+= -I${.CURDIR}/../btx/lib
+
+# BTX is expecting ELF components
+CFLAGS+= -elf
+
+# Debug me!
+#CFLAGS+= -g
+#LDFLAGS+= -g
+
+vers.o: ${.CURDIR}/../../common/newvers.sh ${.CURDIR}/version
+ sh ${.CURDIR}/../../common/newvers.sh ${.CURDIR}/version ${NEWVERSWHAT}
+ ${CC} ${CFLAGS} -c vers.c
+
+${PROG}: ${PROG}.bin ${BTXLDR} ${BTXKERN} ${BTXCRT}
+ btxld -v -f aout -e ${LOADER_ADDRESS} -o ${.TARGET} -l ${BTXLDR} \
+ -b ${BTXKERN} ${PROG}.bin
+# /usr/bin/kzip ${.TARGET}
+# mv ${.TARGET}.kz ${.TARGET}
+
+${PROG}.bin: ${PROG}.sym
+ cp ${.ALLSRC} ${.TARGET}
+ strip -R .comment -R .note ${.TARGET}
+
+${PROG}.help: help.common help.i386
+ cat ${.ALLSRC} | awk -f ${.CURDIR}/../../common/merge_help.awk > ${.TARGET}
+
+.PATH: ${.CURDIR}/../../forth
+FILES= ${PROG}.help loader.4th support.4th loader.conf
+FILES+= screen.4th frames.4th beastie.4th
+FILESDIR_loader.conf= /boot/defaults
+
+.if !exists(${DESTDIR}/boot/loader.rc)
+FILES+= ${.CURDIR}/loader.rc
+.endif
+
+.include <${.CURDIR}/../Makefile.inc>
+
+# Cannot use ${OBJS} above this line
+.include <bsd.prog.mk>
+
+${PROG}.sym: ${OBJS} ${LIBI386} ${LIBSTAND} ${LIBFICL} vers.o
+ ${CC} ${LDFLAGS} -o ${.TARGET} ${BTXCRT} ${OBJS} vers.o \
+ ${LIBFICL} ${LIBI386} ${LIBSTAND}
+
+# If it's not there, don't consider it a target
+.if exists(${.CURDIR}/../../../i386/include)
+beforedepend ${OBJS}: machine
+
+machine:
+ ln -sf ${.CURDIR}/../../../i386/include machine
+
+.endif
+
+CLEANFILES+= machine
diff --git a/sys/boot/i386/loader/conf.c b/sys/boot/i386/loader/conf.c
new file mode 100644
index 0000000..e312959
--- /dev/null
+++ b/sys/boot/i386/loader/conf.c
@@ -0,0 +1,120 @@
+/*-
+ * Copyright (c) 1998 Michael Smith <msmith@freebsd.org>
+ * 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 <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <stand.h>
+#include <bootstrap.h>
+#include "libi386/libi386.h"
+
+/*
+ * We could use linker sets for some or all of these, but
+ * then we would have to control what ended up linked into
+ * the bootstrap. So it's easier to conditionalise things
+ * here.
+ *
+ * XXX rename these arrays to be consistent and less namespace-hostile
+ *
+ * XXX as libi386 and biosboot merge, some of these can become linker sets.
+ */
+
+#if defined(LOADER_NFS_SUPPORT) && defined(LOADER_TFTP_SUPPORT)
+#error "Cannot have both tftp and nfs support yet."
+#endif
+
+/* Exported for libstand */
+struct devsw *devsw[] = {
+ &bioscd,
+ &biosdisk,
+#if defined(LOADER_NFS_SUPPORT) || defined(LOADER_TFTP_SUPPORT)
+ &pxedisk,
+#endif
+ NULL
+};
+
+struct fs_ops *file_system[] = {
+ &ufs_fsops,
+ &ext2fs_fsops,
+ &dosfs_fsops,
+ &cd9660_fsops,
+ &splitfs_fsops,
+#ifdef LOADER_GZIP_SUPPORT
+ &gzipfs_fsops,
+#endif
+#ifdef LOADER_BZIP2_SUPPORT
+ &bzipfs_fsops,
+#endif
+#ifdef LOADER_NFS_SUPPORT
+ &nfs_fsops,
+#endif
+#ifdef LOADER_TFTP_SUPPORT
+ &tftp_fsops,
+#endif
+ NULL
+};
+
+/* Exported for i386 only */
+/*
+ * Sort formats so that those that can detect based on arguments
+ * rather than reading the file go first.
+ */
+extern struct file_format i386_elf;
+extern struct file_format amd64_elf;
+
+struct file_format *file_formats[] = {
+ &i386_elf,
+ &amd64_elf,
+ NULL
+};
+
+/*
+ * Consoles
+ *
+ * We don't prototype these in libi386.h because they require
+ * data structures from bootstrap.h as well.
+ */
+extern struct console vidconsole;
+extern struct console comconsole;
+extern struct console nullconsole;
+
+struct console *consoles[] = {
+ &vidconsole,
+ &comconsole,
+ &nullconsole,
+ NULL
+};
+
+extern struct pnphandler isapnphandler;
+extern struct pnphandler biospnphandler;
+extern struct pnphandler biospcihandler;
+
+struct pnphandler *pnphandlers[] = {
+ &biospnphandler, /* should go first, as it may set isapnp_readport */
+ &isapnphandler,
+ &biospcihandler,
+ NULL
+};
diff --git a/sys/boot/i386/loader/help.i386 b/sys/boot/i386/loader/help.i386
new file mode 100644
index 0000000..16e0fd3
--- /dev/null
+++ b/sys/boot/i386/loader/help.i386
@@ -0,0 +1,64 @@
+################################################################################
+# TACPI DControl ACPI module behaviour
+
+ $acpi_load
+
+ If set, the ACPI module will be loaded. Clear it with
+
+ unset acpi_load
+
+ $hint.acpi.0.disabled="1"
+
+ If set, the ACPI module won't be loaded.
+
+ Note that the ACPI autodetection sets a number of hints to
+ pass information to the ACPI module. These should not be
+ overridden, or system behaviour will be undefined.
+
+
+################################################################################
+# Treboot DReboot the system
+
+ reboot
+
+ Causes the system to immediately reboot.
+
+################################################################################
+# Theap DDisplay memory management statistics
+
+ heap
+
+ Requests debugging output from the heap manager. For debugging use
+ only.
+
+################################################################################
+# Tset Snum_ide_disks DSet the number of IDE disks
+
+ NOTE: this variable is deprecated, use root_disk_unit instead.
+
+ set num_ide_disks=<value>
+
+ When booting from a SCSI disk on a system with one or more IDE disks,
+ and where the IDE disks are the default boot device, it is necessary
+ to tell the kernel how many IDE disks there are in order to have it
+ correctly locate the SCSI disk you are booting from.
+
+################################################################################
+# Tset Sboot_userconfig DStart Userconfig
+
+ set boot_userconfig
+
+ Requests that the kernel's interactive device configuration program
+ be run when the kernel is booted.
+
+################################################################################
+# Tset Sroot_disk_unit DForce the root disk unit number.
+
+ set root_disk_unit=<value>
+
+ If the code which detects the disk unit number for the root disk is
+ confused, eg. by a mix of SCSI and IDE disks, or IDE disks with
+ gaps in the sequence (eg. no primary slave), the unit number can be
+ forced by setting this variable.
+
+################################################################################
diff --git a/sys/boot/i386/loader/loader.rc b/sys/boot/i386/loader/loader.rc
new file mode 100644
index 0000000..d650d99
--- /dev/null
+++ b/sys/boot/i386/loader/loader.rc
@@ -0,0 +1,17 @@
+\ Loader.rc
+\ $FreeBSD$
+\
+\ Includes additional commands
+include /boot/loader.4th
+
+\ Reads and processes loader.rc
+start
+
+\ Tests for password -- executes autoboot first if a password was defined
+check-password
+
+\ Load in the boot menu
+include /boot/beastie.4th
+
+\ Start the boot menu
+beastie-start
diff --git a/sys/boot/i386/loader/main.c b/sys/boot/i386/loader/main.c
new file mode 100644
index 0000000..77f1bc1
--- /dev/null
+++ b/sys/boot/i386/loader/main.c
@@ -0,0 +1,296 @@
+/*-
+ * Copyright (c) 1998 Michael Smith <msmith@freebsd.org>
+ * 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 <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+/*
+ * MD bootstrap main() and assorted miscellaneous
+ * commands.
+ */
+
+#include <stand.h>
+#include <string.h>
+#include <machine/bootinfo.h>
+#include <sys/reboot.h>
+
+#include "bootstrap.h"
+#include "libi386/libi386.h"
+#include "btxv86.h"
+
+#define KARGS_FLAGS_CD 0x1
+#define KARGS_FLAGS_PXE 0x2
+
+/* Arguments passed in from the boot1/boot2 loader */
+static struct
+{
+ u_int32_t howto;
+ u_int32_t bootdev;
+ u_int32_t bootflags;
+ u_int32_t pxeinfo;
+ u_int32_t res2;
+ u_int32_t bootinfo;
+} *kargs;
+
+static u_int32_t initial_howto;
+static u_int32_t initial_bootdev;
+static struct bootinfo *initial_bootinfo;
+
+struct arch_switch archsw; /* MI/MD interface boundary */
+
+static void extract_currdev(void);
+static int isa_inb(int port);
+static void isa_outb(int port, int value);
+void exit(int code);
+
+/* from vers.c */
+extern char bootprog_name[], bootprog_rev[], bootprog_date[], bootprog_maker[];
+
+/* XXX debugging */
+extern char end[];
+
+int
+main(void)
+{
+ int i;
+
+ /* Pick up arguments */
+ kargs = (void *)__args;
+ initial_howto = kargs->howto;
+ initial_bootdev = kargs->bootdev;
+ initial_bootinfo = kargs->bootinfo ? (struct bootinfo *)PTOV(kargs->bootinfo) : NULL;
+
+ /*
+ * Initialise the heap as early as possible. Once this is done, malloc() is usable.
+ */
+ bios_getmem();
+
+ setheap((void *)end, (void *)bios_basemem);
+
+ /*
+ * XXX Chicken-and-egg problem; we want to have console output early, but some
+ * console attributes may depend on reading from eg. the boot device, which we
+ * can't do yet.
+ *
+ * We can use printf() etc. once this is done.
+ * If the previous boot stage has requested a serial console, prefer that.
+ */
+ if (initial_howto & RB_SERIAL)
+ setenv("console", "comconsole", 1);
+ if (initial_howto & RB_MUTE)
+ setenv("console", "nullconsole", 1);
+ cons_probe();
+
+ /*
+ * Initialise the block cache
+ */
+ bcache_init(32, 512); /* 16k cache XXX tune this */
+
+ /*
+ * Special handling for PXE and CD booting.
+ */
+ if (kargs->bootinfo == NULL) {
+ /*
+ * We only want the PXE disk to try to init itself in the below
+ * walk through devsw if we actually booted off of PXE.
+ */
+ if (kargs->bootflags & KARGS_FLAGS_PXE)
+ pxe_enable(kargs->pxeinfo ? PTOV(kargs->pxeinfo) : NULL);
+ else if (kargs->bootflags & KARGS_FLAGS_CD)
+ bc_add(initial_bootdev);
+ }
+
+ /*
+ * March through the device switch probing for things.
+ */
+ for (i = 0; devsw[i] != NULL; i++)
+ if (devsw[i]->dv_init != NULL)
+ (devsw[i]->dv_init)();
+ printf("BIOS %dkB/%dkB available memory\n", bios_basemem / 1024, bios_extmem / 1024);
+ if (initial_bootinfo != NULL) {
+ initial_bootinfo->bi_basemem = bios_basemem / 1024;
+ initial_bootinfo->bi_extmem = bios_extmem / 1024;
+ }
+
+ /* detect ACPI for future reference */
+ biosacpi_detect();
+
+ printf("\n");
+ printf("%s, Revision %s\n", bootprog_name, bootprog_rev);
+ printf("(%s, %s)\n", bootprog_maker, bootprog_date);
+
+ extract_currdev(); /* set $currdev and $loaddev */
+ setenv("LINES", "24", 1); /* optional */
+
+ bios_getsmap();
+
+ archsw.arch_autoload = i386_autoload;
+ archsw.arch_getdev = i386_getdev;
+ archsw.arch_copyin = i386_copyin;
+ archsw.arch_copyout = i386_copyout;
+ archsw.arch_readin = i386_readin;
+ archsw.arch_isainb = isa_inb;
+ archsw.arch_isaoutb = isa_outb;
+
+ interact(); /* doesn't return */
+
+ /* if we ever get here, it is an error */
+ return (1);
+}
+
+/*
+ * Set the 'current device' by (if possible) recovering the boot device as
+ * supplied by the initial bootstrap.
+ *
+ * XXX should be extended for netbooting.
+ */
+static void
+extract_currdev(void)
+{
+ struct i386_devdesc new_currdev;
+ int major, biosdev = -1;
+
+ /* Assume we are booting from a BIOS disk by default */
+ new_currdev.d_dev = &biosdisk;
+
+ /* new-style boot loaders such as pxeldr and cdldr */
+ if (kargs->bootinfo == NULL) {
+ if ((kargs->bootflags & KARGS_FLAGS_CD) != 0) {
+ /* we are booting from a CD with cdboot */
+ new_currdev.d_dev = &bioscd;
+ new_currdev.d_kind.bioscd.unit = bc_bios2unit(initial_bootdev);
+ } else if ((kargs->bootflags & KARGS_FLAGS_PXE) != 0) {
+ /* we are booting from pxeldr */
+ new_currdev.d_dev = &pxedisk;
+ new_currdev.d_kind.netif.unit = 0;
+ } else {
+ /* we don't know what our boot device is */
+ new_currdev.d_kind.biosdisk.slice = -1;
+ new_currdev.d_kind.biosdisk.partition = 0;
+ biosdev = -1;
+ }
+ } else if ((initial_bootdev & B_MAGICMASK) != B_DEVMAGIC) {
+ /* The passed-in boot device is bad */
+ new_currdev.d_kind.biosdisk.slice = -1;
+ new_currdev.d_kind.biosdisk.partition = 0;
+ biosdev = -1;
+ } else {
+ new_currdev.d_kind.biosdisk.slice = (B_ADAPTOR(initial_bootdev) << 4) +
+ B_CONTROLLER(initial_bootdev) - 1;
+ new_currdev.d_kind.biosdisk.partition = B_PARTITION(initial_bootdev);
+ biosdev = initial_bootinfo->bi_bios_dev;
+ major = B_TYPE(initial_bootdev);
+
+ /*
+ * If we are booted by an old bootstrap, we have to guess at the BIOS
+ * unit number. We will loose if there is more than one disk type
+ * and we are not booting from the lowest-numbered disk type
+ * (ie. SCSI when IDE also exists).
+ */
+ if ((biosdev == 0) && (B_TYPE(initial_bootdev) != 2)) /* biosdev doesn't match major */
+ biosdev = 0x80 + B_UNIT(initial_bootdev); /* assume harddisk */
+ }
+ new_currdev.d_type = new_currdev.d_dev->dv_type;
+
+ /*
+ * If we are booting off of a BIOS disk and we didn't succeed in determining
+ * which one we booted off of, just use disk0: as a reasonable default.
+ */
+ if ((new_currdev.d_type == biosdisk.dv_type) &&
+ ((new_currdev.d_kind.biosdisk.unit = bd_bios2unit(biosdev)) == -1)) {
+ printf("Can't work out which disk we are booting from.\n"
+ "Guessed BIOS device 0x%x not found by probes, defaulting to disk0:\n", biosdev);
+ new_currdev.d_kind.biosdisk.unit = 0;
+ }
+ env_setenv("currdev", EV_VOLATILE, i386_fmtdev(&new_currdev),
+ i386_setcurrdev, env_nounset);
+ env_setenv("loaddev", EV_VOLATILE, i386_fmtdev(&new_currdev), env_noset,
+ env_nounset);
+}
+
+COMMAND_SET(reboot, "reboot", "reboot the system", command_reboot);
+
+static int
+command_reboot(int argc, char *argv[])
+{
+ int i;
+
+ for (i = 0; devsw[i] != NULL; ++i)
+ if (devsw[i]->dv_cleanup != NULL)
+ (devsw[i]->dv_cleanup)();
+
+ printf("Rebooting...\n");
+ delay(1000000);
+ __exit(0);
+}
+
+/* provide this for panic, as it's not in the startup code */
+void
+exit(int code)
+{
+ __exit(code);
+}
+
+COMMAND_SET(heap, "heap", "show heap usage", command_heap);
+
+static int
+command_heap(int argc, char *argv[])
+{
+ mallocstats();
+ printf("heap base at %p, top at %p\n", end, sbrk(0));
+ return(CMD_OK);
+}
+
+/* ISA bus access functions for PnP, derived from <machine/cpufunc.h> */
+static int
+isa_inb(int port)
+{
+ u_char data;
+
+ if (__builtin_constant_p(port) &&
+ (((port) & 0xffff) < 0x100) &&
+ ((port) < 0x10000)) {
+ __asm __volatile("inb %1,%0" : "=a" (data) : "id" ((u_short)(port)));
+ } else {
+ __asm __volatile("inb %%dx,%0" : "=a" (data) : "d" (port));
+ }
+ return(data);
+}
+
+static void
+isa_outb(int port, int value)
+{
+ u_char al = value;
+
+ if (__builtin_constant_p(port) &&
+ (((port) & 0xffff) < 0x100) &&
+ ((port) < 0x10000)) {
+ __asm __volatile("outb %0,%1" : : "a" (al), "id" ((u_short)(port)));
+ } else {
+ __asm __volatile("outb %0,%%dx" : : "a" (al), "d" (port));
+ }
+}
+
diff --git a/sys/boot/i386/loader/version b/sys/boot/i386/loader/version
new file mode 100644
index 0000000..7a2acaf
--- /dev/null
+++ b/sys/boot/i386/loader/version
@@ -0,0 +1,14 @@
+$FreeBSD$
+
+NOTE ANY CHANGES YOU MAKE TO THE BOOTBLOCKS HERE. The format of this
+file is important. Make sure the current version number is on line 6.
+
+1.1: New calling conventions for fopen.
+1.0: New semantics for finding the kernel, new boot.
+0.8: Set/getenv & cia, copyin/out.
+0.7: Supports large KVM
+0.6: Increased dictionary size -- supports loader.4th
+0.5: First release version
+0.2: Initial integration with BTX
+0.1: Initial i386 version, inspiration and some structure from the
+ NetBSD version.
diff --git a/sys/boot/i386/mbr/Makefile b/sys/boot/i386/mbr/Makefile
new file mode 100644
index 0000000..50add632
--- /dev/null
+++ b/sys/boot/i386/mbr/Makefile
@@ -0,0 +1,17 @@
+# $FreeBSD$
+
+PROG= mbr
+NOMAN=
+STRIP=
+BINDIR?= /boot
+BINMODE= 444
+
+ORG= 0x600
+
+mbr: mbr.o
+ ${LD} -N -e start -Ttext ${ORG} -o mbr.out mbr.o
+ objcopy -S -O binary mbr.out ${.TARGET}
+
+CLEANFILES+= mbr.out mbr.o
+
+.include <bsd.prog.mk>
diff --git a/sys/boot/i386/mbr/mbr.s b/sys/boot/i386/mbr/mbr.s
new file mode 100644
index 0000000..9cd39d2
--- /dev/null
+++ b/sys/boot/i386/mbr/mbr.s
@@ -0,0 +1,157 @@
+#
+# Copyright (c) 1999 Robert Nordier
+# All rights reserved.
+#
+# Redistribution and use in source and binary forms are freely
+# permitted provided that the above copyright notice and this
+# paragraph and the following disclaimer are duplicated in all
+# such forms.
+#
+# This software is provided "AS IS" and without any express or
+# implied warranties, including, without limitation, the implied
+# warranties of merchantability and fitness for a particular
+# purpose.
+#
+
+# $FreeBSD$
+
+# A 512 byte MBR boot manager that simply boots the active partition.
+
+ .set LOAD,0x7c00 # Load address
+ .set EXEC,0x600 # Execution address
+ .set PT_OFF,0x1be # Partition table
+ .set MAGIC,0xaa55 # Magic: bootable
+
+ .set NHRDRV,0x475 # Number of hard drives
+
+ .globl start # Entry point
+ .code16
+
+#
+# Setup the segment registers for flat addressing and setup the stack.
+#
+start: cld # String ops inc
+ xorw %ax,%ax # Zero
+ movw %ax,%es # Address
+ movw %ax,%ds # data
+ movw %ax,%ss # Set up
+ movw $LOAD,%sp # stack
+#
+# Relocate ourself to a lower address so that we are out of the way when
+# we load in the bootstrap from the partition to boot.
+#
+ movw $main-EXEC+LOAD,%si # Source
+ movw $main,%di # Destination
+ movw $0x200-(main-start),%cx # Byte count
+ rep # Relocate
+ movsb # code
+#
+# Jump to the relocated code.
+#
+ jmp main-LOAD+EXEC # To relocated code
+#
+# Scan the partition table looking for an active entry. Note that %ch is
+# zero from the repeated string instruction above. We save the offset of
+# the active partition in %si and scan the entire table to ensure that only
+# one partition is marked active.
+#
+main: xorw %si,%si # No active partition
+ movw $partbl,%bx # Partition table
+ movb $0x4,%cl # Number of entries
+main.1: cmpb %ch,(%bx) # Null entry?
+ je main.2 # Yes
+ jg err_pt # If 0x1..0x7f
+ testw %si,%si # Active already found?
+ jnz err_pt # Yes
+ movw %bx,%si # Point to active
+main.2: addb $0x10,%bl # Till
+ loop main.1 # done
+ testw %si,%si # Active found?
+ jnz main.3 # Yes
+ int $0x18 # BIOS: Diskless boot
+#
+# Ok, we've found a possible active partition. Check to see that the drive
+# is a valid hard drive number.
+#
+main.3: cmpb $0x80,%dl # Drive valid?
+ jb main.4 # No
+ movb NHRDRV,%dh # Calculate the highest
+ addb $0x80,%dh # drive number available
+ cmpb %dh,%dl # Within range?
+ jb main.5 # Yes
+main.4: movb (%si),%dl # Load drive
+#
+# Ok, now that we have a valid drive and partition entry, load the CHS from
+# the partition entry and read the sector from the disk.
+#
+main.5: movw %sp,%di # Save stack pointer
+ movb 0x1(%si),%dh # Load head
+ movw 0x2(%si),%cx # Load cylinder:sector
+ movw $LOAD,%bx # Transfer buffer
+ cmpb $0xff,%dh # Might we need to use LBA?
+ jnz main.7 # No.
+ cmpw $0xffff,%cx # Do we need to use LBA?
+ jnz main.7 # No.
+ pushw %cx # Save %cx
+ pushw %bx # Save %bx
+ movw $0x55aa,%bx # Magic
+ movb $0x41,%ah # BIOS: EDD extensions
+ int $0x13 # present?
+ jc main.6 # No.
+ cmpw $0xaa55,%bx # Magic ok?
+ jne main.6 # No.
+ testb $0x1,%cl # Packet mode present?
+ jz main.6 # No.
+ popw %bx # Restore %bx
+ pushl $0x0 # Set the LBA
+ pushl 0x8(%si) # address
+ pushw %es # Set the address of
+ pushw %bx # the transfer buffer
+ pushw $0x1 # Read 1 sector
+ pushw $0x10 # Packet length
+ movw %sp,%si # Packer pointer
+ movw $0x4200,%ax # BIOS: LBA Read from disk
+ jmp main.8 # Skip the CHS setup
+main.6: popw %bx # Restore %bx
+ popw %cx # Restore %cx
+main.7: movw $0x201,%ax # BIOS: Read from disk
+main.8: int $0x13 # Call the BIOS
+ movw %di,%sp # Restore stack
+ jc err_rd # If error
+#
+# Now that we've loaded the bootstrap, check for the 0xaa55 signature. If it
+# is present, execute the bootstrap we just loaded.
+#
+ cmpw $MAGIC,0x1fe(%bx) # Bootable?
+ jne err_os # No
+ jmp *%bx # Invoke bootstrap
+#
+# Various error message entry points.
+#
+err_pt: movw $msg_pt,%si # "Invalid partition
+ jmp putstr # table"
+
+err_rd: movw $msg_rd,%si # "Error loading
+ jmp putstr # operating system"
+
+err_os: movw $msg_os,%si # "Missing operating
+ jmp putstr # system"
+#
+# Output an ASCIZ string to the console via the BIOS.
+#
+putstr.0: movw $0x7,%bx # Page:attribute
+ movb $0xe,%ah # BIOS: Display
+ int $0x10 # character
+putstr: lodsb # Get character
+ testb %al,%al # End of string?
+ jnz putstr.0 # No
+putstr.1: jmp putstr.1 # Await reset
+
+msg_pt: .asciz "Invalid partition table"
+msg_rd: .asciz "Error loading operating system"
+msg_os: .asciz "Missing operating system"
+
+ .org PT_OFF
+
+partbl: .fill 0x10,0x4,0x0 # Partition table
+ .word MAGIC # Magic number
diff --git a/sys/boot/i386/pxeldr/Makefile b/sys/boot/i386/pxeldr/Makefile
new file mode 100644
index 0000000..f471595
--- /dev/null
+++ b/sys/boot/i386/pxeldr/Makefile
@@ -0,0 +1,60 @@
+# $FreeBSD$
+
+MAINTAINER=jhb@FreeBSD.org
+
+ORG= 0x7c00
+
+LDR= pxeldr
+BOOT= pxeboot
+LOADER= loader
+PROG= ${BOOT}
+MAN= ${BOOT}.8
+STRIP=
+BINDIR?= /boot
+M4?= m4
+
+.if defined(BOOT_PXELDR_PROBE_KEYBOARD)
+M4FLAGS+= -DPROBE_KEYBOARD
+.endif
+
+.if defined(BOOT_PXELDR_ALWAYS_SERIAL)
+M4FLAGS+= -DALWAYS_SERIAL
+.endif
+
+
+.if exists(${.OBJDIR}/../loader)
+LOADERBIN= ${.OBJDIR}/../loader/loader.bin
+.else
+LOADERBIN= ${.CURDIR}/../loader/loader.bin
+.endif
+
+# BTX components
+.if exists(${.OBJDIR}/../btx)
+BTXDIR= ${.OBJDIR}/../btx
+.else
+BTXDIR= ${.CURDIR}/../btx
+.endif
+BTXLDR= ${BTXDIR}/btxldr/btxldr
+BTXKERN= ${BTXDIR}/btx/btx
+BTXCRT= ${BTXDIR}/lib/crt0.o
+
+${BOOT}: ${LDR} ${LOADER}
+ cat ${LDR} ${LOADER} > ${.TARGET}.tmp
+ dd if=${.TARGET}.tmp of=${.TARGET} obs=2k conv=osync
+ rm ${.TARGET}.tmp
+
+${LOADER}: ${LOADERBIN} ${BTXLDR} ${BTXKERN} ${BTXCRT}
+ btxld -v -f aout -e ${LOADER_ADDRESS} -o ${.TARGET} -l ${BTXLDR} \
+ -b ${BTXKERN} ${LOADERBIN}
+
+${LDR}: ${LDR}.o
+ ${LD} -N -e start -Ttext ${ORG} -o ${LDR}.out ${LDR}.o
+ objcopy -S -O binary ${LDR}.out ${.TARGET}
+
+${LDR}.o: ${LDR}.s
+ (cd ${.CURDIR}; ${M4} ${M4FLAGS} ${LDR}.s) | \
+ ${AS} ${AFLAGS} -o ${.TARGET}
+
+CLEANFILES+= ${LDR} ${LDR}.out ${LDR}.o ${BOOT}.tmp ${LOADER}
+
+.include <bsd.prog.mk>
diff --git a/sys/boot/i386/pxeldr/pxeboot.8 b/sys/boot/i386/pxeldr/pxeboot.8
new file mode 100644
index 0000000..f8ea8a9
--- /dev/null
+++ b/sys/boot/i386/pxeldr/pxeboot.8
@@ -0,0 +1,123 @@
+.\" Copyright (c) 1999 Doug White
+.\" 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.
+.\"
+.\" $FreeBSD$
+.\"
+.\" Note: The date here should be updated whenever a non-trivial
+.\" change is made to the manual page.
+.Dd May 1, 2000
+.Dt PXEBOOT 8
+.Os
+.Sh NAME
+.Nm pxeboot
+.Nd Preboot Execution Environment (PXE) bootloader
+.Sh DESCRIPTION
+The
+.Nm
+bootloader is a modified version of the system third-stage bootstrap
+.Xr loader 8
+configured to run under Intel's Preboot Execution Environment (PXE) system.
+PXE is a form of smart boot ROM, built into Intel EtherExpress Pro/100 and
+3Com 3c905c Ethernet cards, and Ethernet-equipped Intel motherboards.
+PXE supports DHCP configuration and provides low-level NIC access services.
+The
+.Nm
+bootloader retrieves the kernel, modules,
+and other files either via NFS over UDP or by TFTP,
+selectable through compile-time options.
+In combination with a memory file system image or NFS-mounted root file system,
+.Nm
+allows for easy,
+EEPROM-burner free construction of diskless machines.
+.Pp
+The
+.Nm
+binary is loaded just like any other boot file,
+by specifying it in the DHCP server's configuration file.
+Below is a sample configuration for the ISC DHCP v2 server:
+.Bd -literal -offset indent
+option domain-name "example.com";
+option routers 10.0.0.1;
+option subnet-mask 255.255.255.0;
+option broadcast-address 10.0.0.255;
+option domain-name-servers 10.0.0.1;
+server-name "DHCPserver";
+server-identifier 10.0.0.1;
+
+default-lease-time 120;
+max-lease-time 120;
+
+subnet 10.0.0.0 netmask 255.255.255.0 {
+ filename "pxeboot";
+ range 10.0.0.10 10.0.0.254;
+}
+
+.Ed
+.Nm
+recognizes
+.Va next-server
+and
+.Va option root-path
+directives as the server and path to NFS mount for file requests,
+respectively, or the server to make TFTP requests to.
+Note that
+.Nm
+expects to fetch
+.Pa /boot/loader.rc
+from the specified server before loading any other files.
+.Pp
+In all other respects,
+.Nm
+acts just like
+.Xr loader 8 .
+.Pp
+As PXE is still in its infancy, some firmware versions may not work
+properly.
+The
+.Nm
+bootloader has been extensively tested on version 0.99 of Intel firmware;
+pre-release versions of the newer 2.0 firmware are known to have
+problems.
+Check with the device's manufacturer for their latest stable release.
+.Pp
+For further information on Intel's PXE specifications and Wired for
+Management (WfM) systems, see
+.Li http://developer.intel.com/ial/wfm/ .
+.Sh SEE ALSO
+.Xr loader 8
+.Sh HISTORY
+The
+.Nm
+bootloader first appeared in
+.Fx 4.1 .
+.Sh AUTHORS
+.An -nosplit
+The
+.Nm
+bootloader was written by
+.An John Baldwin Aq jhb@FreeBSD.org
+and
+.An Paul Saab Aq ps@FreeBSD.org .
+This manual page was written by
+.An Doug White Aq dwhite@FreeBSD.org .
diff --git a/sys/boot/i386/pxeldr/pxeldr.S b/sys/boot/i386/pxeldr/pxeldr.S
new file mode 100644
index 0000000..fa93c9c
--- /dev/null
+++ b/sys/boot/i386/pxeldr/pxeldr.S
@@ -0,0 +1,287 @@
+#
+# Copyright (c) 2000 John Baldwin
+# All rights reserved.
+#
+# Redistribution and use in source and binary forms are freely
+# permitted provided that the above copyright notice and this
+# paragraph and the following disclaimer are duplicated in all
+# such forms.
+#
+# This software is provided "AS IS" and without any express or
+# implied warranties, including, without limitation, the implied
+# warranties of merchantability and fitness for a particular
+# purpose.
+#
+
+# $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 booted over the network via PXE w/o having to write a
+# separate PXE-aware client just to load the loader.
+#
+
+#
+# Memory locations.
+#
+ .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
+ .set MEM_BIOS_KEYBOARD,0x496 # BDA byte with keyboard bit
+#
+# 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_PXE,0x2 # flag to indicate booting from
+ # PXE loader
+#
+# Boot howto bits
+#
+ .set RB_SERIAL,0x1000 # serial console
+#
+# 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
+#
+# Bit in MEM_BIOS_KEYBOARD that is set if an enhanced keyboard is present
+#
+ .set KEYBOARD_BIT,0x10
+#
+# We expect to be loaded by the BIOS at 0x7c00 (standard boot loader entry
+# point)
+#
+ .code16
+ .globl start
+ .org 0x0, 0x0
+#
+# BTX program loader for PXE network booting
+#
+start: cld # string ops inc
+ xorw %ax, %ax # zero %ax
+ movw %ax, %ss # setup the
+ movw $start, %sp # stack
+ movw %es, %cx # save PXENV+ segment
+ movw %ax, %ds # setup the
+ movw %ax, %es # data segments
+ andl $0xffff, %ecx # clear upper words
+ andl $0xffff, %ebx # of %ebx and %ecx
+ shll $4, %ecx # calculate the offset of
+ addl %ebx, %ecx # the PXENV+ struct and
+ pushl %ecx # save it on the stack
+ movw $welcome_msg, %si # %ds:(%si) -> welcome message
+ callw 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
+ # dwords
+ rep # Clear the arguments
+ stosl # to zero
+ orb $KARGS_FLAGS_PXE, 0x8(%bx) # kargs->bootflags |=
+ # KARGS_FLAGS_PXE
+ popl 0xc(%bx) # kargs->pxeinfo = *PXENV+
+ifdef(`ALWAYS_SERIAL',`
+#
+# set the RBX_SERIAL bit in the howto byte.
+ orl $RB_SERIAL, (%bx) # enable serial console
+')
+ifdef(`PROBE_KEYBOARD',`
+#
+# Look at the BIOS data area to see if we have an enhanced keyboard. If not,
+# set the RBX_SERIAL bit in the howto byte.
+ testb $KEYBOARD_BIT, MEM_BIOS_KEYBOARD # keyboard present?
+ jnz keyb # yes, so skip
+ orl $RB_SERIAL, (%bx) # enable serial console
+keyb:
+')
+#
+# Turn on the A20 address line
+#
+ callw 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
+ # the start of the text
+ # segment
+ movl end+AOUT_TEXT, %ecx # %ecx = length of the text
+ # segment
+ lgdt gdtdesc # setup our own gdt
+ cli # turn off interrupts
+ movl %cr0, %eax # Turn on
+ orb $0x1, %al # protected
+ movl %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
+ 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
+ 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
+ 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
+ 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
+ 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
+ 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
+ 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
+#
+# Now we just start up BTX and let it do the rest
+#
+ movw $jump_message, %si # Display the
+ callw putstr # jump message
+ ljmp $0,$MEM_BTX_ENTRY # Jump to the BTX entry point
+
+#
+# Display a null-terminated string
+#
+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
+ int $0x10 # call BIOS, print char in %al
+ jmp putstr # keep looping
+
+#
+# Enable A20
+#
+seta20: cli # Disable interrupts
+seta20.1: inb $0x64,%al # Get status
+ testb $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?
+ jnz seta20.2 # Yes
+ movb $0xdf,%al # Enable
+ outb %al,$0x60 # A20
+ sti # Enable interrupts
+ retw # To caller
+
+#
+# BTX client to start btxldr
+#
+ .code32
+btx_client: movl $(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
+ std # Go backwards
+push_arg: lodsl # Read argument
+ pushl %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
+ 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,0x0,0x9200,0xcf # SEL_SDATA
+ .word 0xffff,0x0,0x9200,0x0 # SEL_RDATA
+ .word 0xffff,0x0,0x9a00,0xcf # SEL_SCODE (32-bit)
+ .word 0xffff,0x0,0x9a00,0x8f # SEL_SCODE16 (16-bit)
+gdt.1:
+#
+# Pseudo-descriptors.
+#
+gdtdesc: .word gdt.1-gdt-1 # Limit
+ .long gdt # Base
+
+welcome_msg: .asciz "PXE 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"
+
+ .p2align 4
+end:
diff --git a/sys/boot/i386/pxeldr/pxeldr.s b/sys/boot/i386/pxeldr/pxeldr.s
new file mode 100644
index 0000000..fa93c9c
--- /dev/null
+++ b/sys/boot/i386/pxeldr/pxeldr.s
@@ -0,0 +1,287 @@
+#
+# Copyright (c) 2000 John Baldwin
+# All rights reserved.
+#
+# Redistribution and use in source and binary forms are freely
+# permitted provided that the above copyright notice and this
+# paragraph and the following disclaimer are duplicated in all
+# such forms.
+#
+# This software is provided "AS IS" and without any express or
+# implied warranties, including, without limitation, the implied
+# warranties of merchantability and fitness for a particular
+# purpose.
+#
+
+# $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 booted over the network via PXE w/o having to write a
+# separate PXE-aware client just to load the loader.
+#
+
+#
+# Memory locations.
+#
+ .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
+ .set MEM_BIOS_KEYBOARD,0x496 # BDA byte with keyboard bit
+#
+# 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_PXE,0x2 # flag to indicate booting from
+ # PXE loader
+#
+# Boot howto bits
+#
+ .set RB_SERIAL,0x1000 # serial console
+#
+# 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
+#
+# Bit in MEM_BIOS_KEYBOARD that is set if an enhanced keyboard is present
+#
+ .set KEYBOARD_BIT,0x10
+#
+# We expect to be loaded by the BIOS at 0x7c00 (standard boot loader entry
+# point)
+#
+ .code16
+ .globl start
+ .org 0x0, 0x0
+#
+# BTX program loader for PXE network booting
+#
+start: cld # string ops inc
+ xorw %ax, %ax # zero %ax
+ movw %ax, %ss # setup the
+ movw $start, %sp # stack
+ movw %es, %cx # save PXENV+ segment
+ movw %ax, %ds # setup the
+ movw %ax, %es # data segments
+ andl $0xffff, %ecx # clear upper words
+ andl $0xffff, %ebx # of %ebx and %ecx
+ shll $4, %ecx # calculate the offset of
+ addl %ebx, %ecx # the PXENV+ struct and
+ pushl %ecx # save it on the stack
+ movw $welcome_msg, %si # %ds:(%si) -> welcome message
+ callw 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
+ # dwords
+ rep # Clear the arguments
+ stosl # to zero
+ orb $KARGS_FLAGS_PXE, 0x8(%bx) # kargs->bootflags |=
+ # KARGS_FLAGS_PXE
+ popl 0xc(%bx) # kargs->pxeinfo = *PXENV+
+ifdef(`ALWAYS_SERIAL',`
+#
+# set the RBX_SERIAL bit in the howto byte.
+ orl $RB_SERIAL, (%bx) # enable serial console
+')
+ifdef(`PROBE_KEYBOARD',`
+#
+# Look at the BIOS data area to see if we have an enhanced keyboard. If not,
+# set the RBX_SERIAL bit in the howto byte.
+ testb $KEYBOARD_BIT, MEM_BIOS_KEYBOARD # keyboard present?
+ jnz keyb # yes, so skip
+ orl $RB_SERIAL, (%bx) # enable serial console
+keyb:
+')
+#
+# Turn on the A20 address line
+#
+ callw 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
+ # the start of the text
+ # segment
+ movl end+AOUT_TEXT, %ecx # %ecx = length of the text
+ # segment
+ lgdt gdtdesc # setup our own gdt
+ cli # turn off interrupts
+ movl %cr0, %eax # Turn on
+ orb $0x1, %al # protected
+ movl %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
+ 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
+ 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
+ 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
+ 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
+ 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
+ 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
+ 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
+#
+# Now we just start up BTX and let it do the rest
+#
+ movw $jump_message, %si # Display the
+ callw putstr # jump message
+ ljmp $0,$MEM_BTX_ENTRY # Jump to the BTX entry point
+
+#
+# Display a null-terminated string
+#
+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
+ int $0x10 # call BIOS, print char in %al
+ jmp putstr # keep looping
+
+#
+# Enable A20
+#
+seta20: cli # Disable interrupts
+seta20.1: inb $0x64,%al # Get status
+ testb $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?
+ jnz seta20.2 # Yes
+ movb $0xdf,%al # Enable
+ outb %al,$0x60 # A20
+ sti # Enable interrupts
+ retw # To caller
+
+#
+# BTX client to start btxldr
+#
+ .code32
+btx_client: movl $(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
+ std # Go backwards
+push_arg: lodsl # Read argument
+ pushl %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
+ 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,0x0,0x9200,0xcf # SEL_SDATA
+ .word 0xffff,0x0,0x9200,0x0 # SEL_RDATA
+ .word 0xffff,0x0,0x9a00,0xcf # SEL_SCODE (32-bit)
+ .word 0xffff,0x0,0x9a00,0x8f # SEL_SCODE16 (16-bit)
+gdt.1:
+#
+# Pseudo-descriptors.
+#
+gdtdesc: .word gdt.1-gdt-1 # Limit
+ .long gdt # Base
+
+welcome_msg: .asciz "PXE 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"
+
+ .p2align 4
+end:
OpenPOWER on IntegriCloud