summaryrefslogtreecommitdiffstats
path: root/stand/i386
diff options
context:
space:
mode:
authorkevans <kevans@FreeBSD.org>2018-02-12 01:08:44 +0000
committerkevans <kevans@FreeBSD.org>2018-02-12 01:08:44 +0000
commit7d97ee5b28b409c00bfaf12daf5ab497a6038b9d (patch)
tree245306b754606bcf49c0ff17b131b58609b6c7a6 /stand/i386
parent43b278e1b66cf4de337a17034087ea785031bd6f (diff)
downloadFreeBSD-src-7d97ee5b28b409c00bfaf12daf5ab497a6038b9d.zip
FreeBSD-src-7d97ee5b28b409c00bfaf12daf5ab497a6038b9d.tar.gz
MFC r325834,r325997,326502: Move sys/boot to stand/
This is effectively a direct commit to stable/11, due to differences between stable/11 and head. Changes to DTS in sys/boot/fdt/dts were often accompanied by kernel changes. Many of these were also risc-v updates that likely had many more dependencies to MFC. Because of this, sys/boot/fdt/dts remains as-is while everything else in sys/boot relocates to stand/. r325834: Move sys/boot to stand. Fix all references to new location r325997: Remove empty directories. r326502: Document the sys/boot -> stand move in hier.7 and the top-level README.
Diffstat (limited to 'stand/i386')
-rw-r--r--stand/i386/Makefile25
-rw-r--r--stand/i386/Makefile.inc36
-rw-r--r--stand/i386/boot.ldscript11
-rw-r--r--stand/i386/boot0/Makefile80
-rw-r--r--stand/i386/boot0/Makefile.depend11
-rw-r--r--stand/i386/boot0/boot0.S682
-rw-r--r--stand/i386/boot0sio/Makefile8
-rw-r--r--stand/i386/boot0sio/Makefile.depend11
-rw-r--r--stand/i386/boot2/Makefile99
-rw-r--r--stand/i386/boot2/Makefile.depend14
-rw-r--r--stand/i386/boot2/boot1.S373
-rw-r--r--stand/i386/boot2/boot2.c646
-rw-r--r--stand/i386/boot2/lib.h24
-rw-r--r--stand/i386/boot2/sio.S84
-rw-r--r--stand/i386/btx/Makefile5
-rw-r--r--stand/i386/btx/Makefile.inc3
-rw-r--r--stand/i386/btx/btx/Makefile35
-rw-r--r--stand/i386/btx/btx/Makefile.depend11
-rw-r--r--stand/i386/btx/btx/btx.S1082
-rw-r--r--stand/i386/btx/btxldr/Makefile23
-rw-r--r--stand/i386/btx/btxldr/Makefile.depend11
-rw-r--r--stand/i386/btx/btxldr/btxldr.S409
-rw-r--r--stand/i386/btx/lib/Makefile12
-rw-r--r--stand/i386/btx/lib/Makefile.depend11
-rw-r--r--stand/i386/btx/lib/btxcsu.S49
-rw-r--r--stand/i386/btx/lib/btxsys.s40
-rw-r--r--stand/i386/btx/lib/btxv86.h75
-rw-r--r--stand/i386/btx/lib/btxv86.s85
-rw-r--r--stand/i386/cdboot/Makefile20
-rw-r--r--stand/i386/cdboot/Makefile.depend11
-rw-r--r--stand/i386/cdboot/cdboot.S594
-rw-r--r--stand/i386/common/bootargs.h93
-rw-r--r--stand/i386/common/cons.c177
-rw-r--r--stand/i386/common/cons.h35
-rw-r--r--stand/i386/common/drv.c102
-rw-r--r--stand/i386/common/drv.h48
-rw-r--r--stand/i386/common/edd.h110
-rw-r--r--stand/i386/gptboot/Makefile75
-rw-r--r--stand/i386/gptboot/Makefile.depend19
-rw-r--r--stand/i386/gptboot/gptboot.8245
-rw-r--r--stand/i386/gptboot/gptboot.c648
-rw-r--r--stand/i386/gptboot/gptldr.S142
-rw-r--r--stand/i386/gptzfsboot/Makefile87
-rw-r--r--stand/i386/gptzfsboot/Makefile.depend19
-rw-r--r--stand/i386/gptzfsboot/gptzfsboot.8193
-rw-r--r--stand/i386/kgzldr/Makefile21
-rw-r--r--stand/i386/kgzldr/Makefile.depend12
-rw-r--r--stand/i386/kgzldr/boot.c129
-rw-r--r--stand/i386/kgzldr/crt.s83
-rw-r--r--stand/i386/kgzldr/kgzldr.h41
-rw-r--r--stand/i386/kgzldr/lib.c88
-rw-r--r--stand/i386/kgzldr/sio.s44
-rw-r--r--stand/i386/kgzldr/start.s45
-rw-r--r--stand/i386/libfirewire/Makefile20
-rw-r--r--stand/i386/libfirewire/Makefile.depend13
-rw-r--r--stand/i386/libfirewire/dconsole.c127
-rw-r--r--stand/i386/libfirewire/firewire.c484
-rw-r--r--stand/i386/libfirewire/fwohci.c479
-rw-r--r--stand/i386/libfirewire/fwohci.h162
-rw-r--r--stand/i386/libfirewire/fwohcireg.h369
-rw-r--r--stand/i386/libi386/Makefile61
-rw-r--r--stand/i386/libi386/Makefile.depend14
-rw-r--r--stand/i386/libi386/amd64_tramp.S113
-rw-r--r--stand/i386/libi386/biosacpi.c144
-rw-r--r--stand/i386/libi386/bioscd.c452
-rw-r--r--stand/i386/libi386/biosdisk.c1013
-rw-r--r--stand/i386/libi386/biosmem.c258
-rw-r--r--stand/i386/libi386/biospci.c588
-rw-r--r--stand/i386/libi386/biospnp.c292
-rw-r--r--stand/i386/libi386/biossmap.c159
-rw-r--r--stand/i386/libi386/bootinfo.c170
-rw-r--r--stand/i386/libi386/bootinfo32.c291
-rw-r--r--stand/i386/libi386/bootinfo64.c280
-rw-r--r--stand/i386/libi386/comconsole.c385
-rw-r--r--stand/i386/libi386/devicename.c206
-rw-r--r--stand/i386/libi386/elf32_freebsd.c84
-rw-r--r--stand/i386/libi386/elf64_freebsd.c126
-rw-r--r--stand/i386/libi386/i386_copy.c75
-rw-r--r--stand/i386/libi386/i386_module.c44
-rw-r--r--stand/i386/libi386/libi386.h156
-rw-r--r--stand/i386/libi386/multiboot.c467
-rw-r--r--stand/i386/libi386/multiboot.h225
-rw-r--r--stand/i386/libi386/multiboot_tramp.S51
-rw-r--r--stand/i386/libi386/nullconsole.c88
-rw-r--r--stand/i386/libi386/pread.c80
-rw-r--r--stand/i386/libi386/pxe.c540
-rw-r--r--stand/i386/libi386/pxe.h513
-rw-r--r--stand/i386/libi386/pxetramp.s38
-rw-r--r--stand/i386/libi386/relocater_tramp.S358
-rw-r--r--stand/i386/libi386/smbios.c454
-rw-r--r--stand/i386/libi386/smbios.h34
-rw-r--r--stand/i386/libi386/spinconsole.c112
-rw-r--r--stand/i386/libi386/time.c118
-rw-r--r--stand/i386/libi386/vidconsole.c632
-rw-r--r--stand/i386/loader/Makefile89
-rw-r--r--stand/i386/loader/Makefile.depend21
-rw-r--r--stand/i386/loader/chain.c134
-rw-r--r--stand/i386/loader/conf.c159
-rw-r--r--stand/i386/loader/help.i38654
-rw-r--r--stand/i386/loader/loader.rc18
-rw-r--r--stand/i386/loader/main.c477
-rw-r--r--stand/i386/loader/version14
-rw-r--r--stand/i386/mbr/Makefile17
-rw-r--r--stand/i386/mbr/Makefile.depend11
-rw-r--r--stand/i386/mbr/mbr.s157
-rw-r--r--stand/i386/pmbr/Makefile14
-rw-r--r--stand/i386/pmbr/Makefile.depend11
-rw-r--r--stand/i386/pmbr/pmbr.s252
-rw-r--r--stand/i386/pxeldr/Makefile47
-rw-r--r--stand/i386/pxeldr/Makefile.depend15
-rw-r--r--stand/i386/pxeldr/pxeboot.8158
-rw-r--r--stand/i386/pxeldr/pxeldr.S301
-rw-r--r--stand/i386/zfsboot/Makefile93
-rw-r--r--stand/i386/zfsboot/Makefile.depend16
-rw-r--r--stand/i386/zfsboot/zfsboot.8133
-rw-r--r--stand/i386/zfsboot/zfsboot.c1151
-rw-r--r--stand/i386/zfsboot/zfsldr.S283
-rw-r--r--stand/i386/zfsloader/Makefile7
-rw-r--r--stand/i386/zfsloader/Makefile.depend22
119 files changed, 20965 insertions, 0 deletions
diff --git a/stand/i386/Makefile b/stand/i386/Makefile
new file mode 100644
index 0000000..9a1663a
--- /dev/null
+++ b/stand/i386/Makefile
@@ -0,0 +1,25 @@
+# $FreeBSD$
+
+.include <bsd.init.mk>
+
+SUBDIR= mbr pmbr boot0 boot0sio btx boot2 cdboot gptboot \
+ libi386
+
+.if ${MK_LOADER_FIREWIRE} == "yes"
+SUBDIR+= libfirewire
+.endif
+
+SUBDIR+= loader
+
+# special boot programs, 'self-extracting boot2+loader'
+SUBDIR+= pxeldr
+
+.if ${MACHINE_CPUARCH} == "i386"
+SUBDIR+= kgzldr
+.endif
+
+.if ${MK_ZFS} != "no"
+SUBDIR+= zfsboot gptzfsboot zfsloader
+.endif
+
+.include <bsd.subdir.mk>
diff --git a/stand/i386/Makefile.inc b/stand/i386/Makefile.inc
new file mode 100644
index 0000000..6a4bea7
--- /dev/null
+++ b/stand/i386/Makefile.inc
@@ -0,0 +1,36 @@
+# Common defines for all of /stand/i386/
+#
+# $FreeBSD$
+
+LOADER_ADDRESS?=0x200000
+CFLAGS+= -march=i386 -ffreestanding
+CFLAGS.gcc+= -mpreferred-stack-boundary=2
+CFLAGS+= ${CFLAGS_NO_SIMD} -msoft-float
+LDFLAGS+= -nostdlib
+
+# BTX components
+BTXDIR= ${BOOTOBJ}/i386/btx
+BTXLDR= ${BTXDIR}/btxldr/btxldr
+BTXKERN= ${BTXDIR}/btx/btx
+BTXCRT= ${BTXDIR}/lib/crt0.o
+
+BTXSRC= ${BOOTSRC}/i386/btx
+BTXLIB= ${BTXSRC}/lib
+
+# compact binary with no padding between text, data, bss
+LDSCRIPT= ${BOOTSRC}/i386/boot.ldscript
+# LDFLAGS_BIN=-e start -Ttext ${ORG} -Wl,-T,${LDSCRIPT},-S,--oformat,binary
+# LD_FLAGS_BIN=-static -T ${LDSCRIPT} --gc-sections
+LDFLAGS_BIN=-e start -Ttext ${ORG} -Wl,-N,-S,--oformat,binary
+LD_FLAGS_BIN=-static -N --gc-sections
+
+.if ${MACHINE_CPUARCH} == "amd64"
+DO32=1
+.endif
+
+.if defined(LOADER_FIREWIRE_SUPPORT)
+MK_LOADER_FIREWIRE=yes
+.warning "LOADER_FIREWIRE_SUPPORT deprecated, please move to WITH_LOADER_FIREWIRE"
+.endif
+
+.include "../Makefile.inc"
diff --git a/stand/i386/boot.ldscript b/stand/i386/boot.ldscript
new file mode 100644
index 0000000..04ea39d
--- /dev/null
+++ b/stand/i386/boot.ldscript
@@ -0,0 +1,11 @@
+/* $FreeBSD$ */
+/* Merge text, data and bss together almost no padding */
+OUTPUT_FORMAT("elf32-i386-freebsd")
+OUTPUT_ARCH(i386)
+ENTRY(_start)
+SECTIONS {
+ . = 0x08048000 + SIZEOF_HEADERS;
+ .text : { *(.text) } =0x90909090 /* Pad with nops, if needed */
+ .data : { *(.data) } _edata = .;
+ .bss : { *(.bss) } _end = .;
+}
diff --git a/stand/i386/boot0/Makefile b/stand/i386/boot0/Makefile
new file mode 100644
index 0000000..39c0072
--- /dev/null
+++ b/stand/i386/boot0/Makefile
@@ -0,0 +1,80 @@
+# $FreeBSD$
+
+PROG?= boot0
+STRIP=
+BINMODE=${NOBINMODE}
+MAN=
+SRCS= ${PROG}.S
+
+# Additional options that you can specify with make OPTS="..."
+# (these only apply to boot0.S)
+#
+# -DVOLUME_SERIAL support volume serial number (NT, XP, Vista)
+# -DSIO do I/O using COM1:
+# -DPXE fallback to INT18/PXE with F6
+# -DCHECK_DRIVE enable checking drive number
+# -DONLY_F_KEYS accept only Fx keys in console
+# -DTEST print drive number on entry
+#
+OPTS ?= -DVOLUME_SERIAL -DPXE
+CFLAGS += ${OPTS}
+
+# Flags used in the boot0.S code:
+# 0x0f all valid partitions enabled.
+# 0x80 'packet', use BIOS EDD (LBA) extensions instead of CHS
+# to read from disk. boot0.S does not check that the extensions
+# are supported, but all modern BIOSes should have them.
+# 0x40 'noupdate', disable writing boot0 back to disk so that
+# the current selection is not preserved across reboots.
+# 0x20 'setdrv', override the drive number supplied by the bios
+# with the one in the boot sector.
+
+# Default boot flags:
+BOOT_BOOT0_FLAGS?= 0x8f
+
+# 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
+ORG=${BOOT_BOOT0_ORG}
+
+# Comm settings for boot0sio.
+# Bit(s) Description
+# 7-5 data rate (110,150,300,600,1200,2400,4800,9600 bps)
+# 4-3 parity (00 or 10 = none, 01 = odd, 11 = even)
+# 2 stop bits (set = 2, clear = 1)
+# 1-0 data bits (00 = 5, 01 = 6, 10 = 7, 11 = 8)
+.if !defined(BOOT_BOOT0_COMCONSOLE_SPEED)
+BOOT_COMCONSOLE_SPEED?= 9600
+.if ${BOOT_COMCONSOLE_SPEED} == 9600
+BOOT_BOOT0_COMCONSOLE_SPEED= "7 << 5 + 3"
+.elif ${BOOT_COMCONSOLE_SPEED} == 4800
+BOOT_BOOT0_COMCONSOLE_SPEED= "6 << 5 + 3"
+.elif ${BOOT_COMCONSOLE_SPEED} == 2400
+BOOT_BOOT0_COMCONSOLE_SPEED= "5 << 5 + 3"
+.elif ${BOOT_COMCONSOLE_SPEED} == 1200
+BOOT_BOOT0_COMCONSOLE_SPEED= "4 << 5 + 3"
+.elif ${BOOT_COMCONSOLE_SPEED} == 600
+BOOT_BOOT0_COMCONSOLE_SPEED= "3 << 5 + 3"
+.elif ${BOOT_COMCONSOLE_SPEED} == 300
+BOOT_BOOT0_COMCONSOLE_SPEED= "2 << 5 + 3"
+.elif ${BOOT_COMCONSOLE_SPEED} == 150
+BOOT_BOOT0_COMCONSOLE_SPEED= "1 << 5 + 3"
+.elif ${BOOT_COMCONSOLE_SPEED} == 110
+BOOT_BOOT0_COMCONSOLE_SPEED= "0 << 5 + 3"
+.else
+BOOT_BOOT0_COMCONSOLE_SPEED= "7 << 5 + 3"
+.endif
+.endif
+
+CFLAGS+=-DFLAGS=${BOOT_BOOT0_FLAGS} \
+ -DTICKS=${BOOT_BOOT0_TICKS} \
+ -DCOMSPEED=${BOOT_BOOT0_COMCONSOLE_SPEED}
+
+LDFLAGS+=${LDFLAGS_BIN}
+
+.include <bsd.prog.mk>
diff --git a/stand/i386/boot0/Makefile.depend b/stand/i386/boot0/Makefile.depend
new file mode 100644
index 0000000..f80275d
--- /dev/null
+++ b/stand/i386/boot0/Makefile.depend
@@ -0,0 +1,11 @@
+# $FreeBSD$
+# Autogenerated - do NOT edit!
+
+DIRDEPS = \
+
+
+.include <dirdeps.mk>
+
+.if ${DEP_RELDIR} == ${_DEP_RELDIR}
+# local dependencies - needed for -jN in clean tree
+.endif
diff --git a/stand/i386/boot0/boot0.S b/stand/i386/boot0/boot0.S
new file mode 100644
index 0000000..708f093
--- /dev/null
+++ b/stand/i386/boot0/boot0.S
@@ -0,0 +1,682 @@
+/*
+ * Copyright (c) 2008 Luigi Rizzo (mostly documentation)
+ * 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$
+ */
+
+/* build options: */
+#ifdef SIO /* use serial console on COM1. */
+#endif
+
+#ifdef PXE /* enable PXE/INT18 booting with F6 */
+#define SAVE_MORE_MEMORY
+#endif
+
+#ifdef CHECK_DRIVE /* make sure we boot from a HD. */
+#endif
+
+#ifdef ONLY_F_KEYS /* Only F1..F6, no digits on console */
+#endif
+
+#ifdef VOLUME_SERIAL /* support Volume serial number */
+#define B0_BASE 0x1ae /* move the internal data area */
+#define SAVE_MEMORY
+#else
+#define B0_BASE 0x1b2
+#endif
+
+#ifdef TEST /* enable some test code */
+#define SAVE_MEMORY
+#define SAVE_MORE_MEMORY
+#endif
+
+/*
+ * Note - this code uses many tricks to save space and fit in one sector.
+ * This includes using side effects of certain instructions, reusing
+ * register values from previous operations, etc.
+ * Be extremely careful when changing the code, even for simple things.
+ */
+
+/*
+ * BOOT BLOCK STRUCTURE
+ *
+ * This code implements a Master Boot Record (MBR) for an Intel/PC disk.
+ * It is 512 bytes long and it is normally loaded by the BIOS (or another
+ * bootloader) at 0:0x7c00. This code depends on %cs:%ip being 0:0x7c00
+ *
+ * The initial chunk of instructions is used as a signature by external
+ * tools (e.g. boot0cfg) which can manipulate the block itself.
+ *
+ * The area at offset 0x1b2 contains a magic string ('Drive '), also
+ * used as a signature to detect the block, and some variables that can
+ * be updated by boot0cfg (and optionally written back to the disk).
+ * These variables control the operation of the bootloader itself,
+ * e.g. which partitions to enable, the timeout, the use of LBA
+ * (called 'packet') or CHS mode, whether to force a drive number,
+ * and whether to write back the user's selection back to disk.
+ *
+ * As in every Master Boot Record, the partition table is at 0x1be,
+ * made of four 16-byte entries each containing:
+ *
+ * OFF SIZE DESCRIPTION
+ * 0 1 status (0x80: bootable, 0: non bootable)
+ * 1 3 start sector CHS
+ * 8:head, 6:sector, 2:cyl bit 9..8, 8:cyl bit 7..0
+ * 4 1 partition type
+ * 5 3 end sector CHS
+ * 8 4 LBA of first sector
+ * 12 4 partition size in sectors
+ *
+ * and followed by the two bytes 0x55, 0xAA (MBR signature).
+ */
+
+
+/*
+ * BOOT BLOCK OPERATION
+ *
+ * On entry, the registers contain the following values:
+ *
+ * %cs:%ip 0:0x7c00
+ * %dl drive number (0x80, 0x81, ... )
+ * %si pointer to the partition table from which we were loaded.
+ * Some boot code (e.g. syslinux) use this info to relocate
+ * themselves, so we want to pass a valid one to the next stage.
+ * NOTE: the use of %si is not a standard.
+ *
+ * This boot block first relocates itself at a different address (0:0x600),
+ * to free the space at 0:0x7c00 for the next stage boot block.
+ *
+ * It then initializes some memory at 0:0x800 and above (pointed by %bp)
+ * to store the original drive number (%dl) passed to us, and to construct a
+ * fake partition entry. The latter is used by the disk I/O routine and,
+ * in some cases, passed in %si to the next stage boot code.
+ *
+ * The variables at 0x1b2 are accessed as negative offsets from %bp.
+ *
+ * After the relocation, the code scans the partition table printing
+ * out enabled partition or disks, and waits for user input.
+ *
+ * When a partition is selected, or a timeout expires, the currently
+ * selected partition is used to load the next stage boot code,
+ * %dl and %si are set appropriately as when we were called, and
+ * control is transferred to the newly loaded code at 0:0x7c00.
+ */
+
+/*
+ * CONSTANTS
+ *
+ * NHRDRV is the address in segment 0 where the BIOS writes the
+ * total number of hard disks in the system.
+ * LOAD is the original load address and cannot be changed.
+ * ORIGIN is the relocation address. If you change it, you also need
+ * to change the value passed to the linker in the Makefile
+ * PRT_OFF is the location of the partition table (from the MBR standard).
+ * B0_OFF is the location of the data area, known to boot0cfg so
+ * it cannot be changed. Computed as a negative offset from 0x200
+ * MAGIC is the signature of a boot block.
+ */
+
+ .set NHRDRV,0x475 # Number of hard drives
+ .set ORIGIN,0x600 # Execution address
+ .set LOAD,0x7c00 # Load address
+
+ .set PRT_OFF,0x1be # Partition table
+ .set B0_OFF,(B0_BASE-0x200) # Offset of boot0 data
+
+ .set MAGIC,0xaa55 # Magic: bootable
+
+ .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,'#' # ASCII code for <BEL>
+ .set ASCII_CR,0x0D # ASCII code for <CR>
+
+/*
+ * Offsets of variables in the block at B0_OFF, and in the volatile
+ * data area, computed as displacement from %bp.
+ * We need to define them as constant as the assembler cannot
+ * compute them in its single pass.
+ */
+ .set _NXTDRV, B0_OFF+6 # Next drive
+ .set _OPT, B0_OFF+7 # Default option
+ .set _SETDRV, B0_OFF+8 # Drive to force
+ .set _FLAGS, B0_OFF+9 # Flags
+ .set SETDRV, 0x20 # the 'setdrv' flag
+ .set NOUPDATE, 0x40 # the 'noupdate' flag
+ .set USEPACKET, 0x80 # the 'packet' flag
+
+ /* ticks is at a fixed position */
+ .set _TICKS, (PRT_OFF - 0x200 - 2) # Timeout ticks
+ .set _MNUOPT, 0x10 # Saved menu entries
+
+ .set TLEN, (desc_ofs - bootable_ids) # size of bootable ids
+ .globl start # Entry point
+ .code16 # This runs in real mode
+
+/*
+ * MAIN ENTRY POINT
+ * Initialise segments and registers to known values.
+ * segments start at 0.
+ * The stack is immediately below the address we were loaded to.
+ * NOTE: the initial section of the code (up to movw $LOAD,%sp)
+ * is used by boot0cfg, together with the 'Drive ' string and
+ * the 0x55, 0xaa at the end, as an identifier for version 1.0
+ * of the boot code. Do not change it.
+ * In version 1.0 the parameter table (_NEXTDRV etc) is at 0x1b9
+ */
+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, 0x600 by default.
+ */
+ movw %sp,%si # Source
+ movw $start,%di # Destination
+ movw $0x100,%cx # Word count
+ rep # Relocate
+ movsw # code
+ /*
+ * After the code, (i.e. at %di+0, 0x800) create a partition entry,
+ * initialized to LBA 0 / CHS 0:0:1.
+ * Set %bp to point to the partition and also, with negative offsets,
+ * to the variables embedded in the bootblock (nextdrv and so on).
+ */
+ movw %di,%bp # Address variables
+ movb $0x8,%cl # Words to clear
+ rep # Zero
+ stosw # them
+ incb -0xe(%di) # Set the S field to 1
+
+ jmp main-LOAD+ORIGIN # Jump to relocated code
+
+main:
+#if defined(SIO) && COMSPEED != 0
+ /*
+ * Init the serial port. bioscom preserves the driver number in DX.
+ */
+ movw $COMSPEED,%ax # defined by Makefile
+ callw bioscom
+#endif
+
+ /*
+ * If the 'setdrv' flag is set in the boot sector, use the drive
+ * number from the boot sector at 'setdrv_num'.
+ * Optionally, do the same if the BIOS gives us an invalid number
+ * (note though that the override prevents booting from a floppy
+ * or a ZIP/flash drive in floppy emulation).
+ * The test costs 4 bytes of code so it is disabled by default.
+ */
+ testb $SETDRV,_FLAGS(%bp) # Set drive number?
+#ifndef CHECK_DRIVE /* disable drive checks */
+ jz save_curdrive # no, use the default
+#else
+ jnz disable_update # Yes
+ testb %dl,%dl # Drive number valid?
+ js save_curdrive # Possibly (0x80 set)
+#endif
+ /*
+ * Disable updates if the drive number is forced.
+ */
+disable_update: orb $NOUPDATE,_FLAGS(%bp) # Disable updates
+ movb _SETDRV(%bp),%dl # Use stored drive number
+
+ /*
+ * Whatever drive we decided to use, store it at (%bp). The byte
+ * is normally used for the state of the partition (0x80 or 0x00),
+ * but we abuse it as it is very convenient to access at offset 0.
+ * The value is read back after 'check_selection'
+ */
+save_curdrive: movb %dl, (%bp) # Save drive number
+ pushw %dx # Also in the stack
+#ifdef TEST /* test code, print internal bios drive */
+ rolb $1, %dl
+ movw $drive, %si
+ call putkey
+#endif
+ callw putn # Print a newline
+ /*
+ * 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. We use the latter trick to check for
+ * end of the loop without using an extra register (see start.5).
+ */
+ 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.
+ */
+read_entry: movb %ch,-0x4(%bx) # Zero active flag (ch == 0)
+ btw %dx,_FLAGS(%bp) # Entry enabled?
+ jnc next_entry # No
+ movb (%bx),%al # Load type
+ test %al, %al # skip empty partition
+ jz next_entry
+ /*
+ * Scan the table of bootable ids, which starts at %di and has
+ * length TLEN. On a match, %di points to the element following the
+ * match; the corresponding offset to the description is $(TLEN-1)
+ * bytes ahead. We use a count of TLEN+1 so if we don't find a match
+ * within the first TLEN entries, we hit the 'unknown' entry.
+ */
+ movw $bootable_ids,%di # Lookup tables
+ movb $(TLEN+1),%cl # Number of entries
+ repne # Locate
+ scasb # type
+ /*
+ * Get the matching element in the next array.
+ * The byte at $(TLEN-1)(%di) contains the offset of the description
+ * string from %di, so we add the number and print the string.
+ */
+ addw $(TLEN-1), %di # Adjust
+ movb (%di),%cl # Partition
+ addw %cx,%di # description
+ callw putx # Display it
+
+next_entry: incw %dx # Next item
+ addb $0x10,%bl # Next entry
+ jnc read_entry # Till done
+ /*
+ * We are past a 256 byte boundary: the partition table is finished.
+ * Add one to the drive number and check it is valid.
+ * Note that if we started from a floppy, %dl was 0 so we still
+ * get an entry for the next drive, which is the first Hard Disk.
+ */
+ popw %ax # Drive number
+ subb $0x80-0x1,%al # Does next
+ cmpb NHRDRV,%al # drive exist? (from BIOS?)
+ jb print_drive # Yes
+ /*
+ * If this is the only drive, don't display it as an option.
+ */
+ decw %ax # Already drive 0?
+ jz print_prompt # Yes
+ /*
+ * If it was illegal or we cycled through them, 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 "nxtdrv" location in case we want to save it to disk.
+ * This digit is also part of the printed drive string, so add 0x80
+ * to indicate end of string.
+ */
+print_drive: addb $'0'|0x80,%al # Save next
+ movb %al,_NXTDRV(%bp) # drive number
+ movw $drive,%di # Display
+ callw putx # item
+ /*
+ * Menu is complete, display a prompt followed by current selection.
+ * 'decw %si' makes the register point to the space after 'Boot: '
+ * so we do not see an extra CRLF on the screen.
+ */
+print_prompt: movw $prompt,%si # Display
+ callw putstr # prompt
+ movb _OPT(%bp),%dl # Display
+ decw %si # default
+ callw putkey # key
+ jmp start_input # Skip beep
+
+/*
+ * Here we have the code waiting for user input or a timeout.
+ */
+beep: movb $ASCII_BEL,%al # Input error, print or beep
+ callw putchr
+
+start_input:
+ /*
+ * Actual Start of input loop. Take note of time
+ */
+ xorb %ah,%ah # BIOS: Get
+ int $0x1a # system time
+ movw %dx,%di # Ticks when
+ addw _TICKS(%bp),%di # timeout
+read_key:
+ /*
+ * Busy loop, looking for keystrokes but keeping one eye on the time.
+ */
+#ifndef SIO
+ movb $0x1,%ah # BIOS: Check
+ int $0x16 # for keypress
+#else /* SIO */
+ movb $0x03,%ah # BIOS: Read COM
+ call bioscom
+ testb $0x01,%ah # Check line status
+ # (bit 1 indicates input)
+#endif /* SIO */
+ jnz got_key # Have input
+ xorb %ah,%ah # BIOS: int 0x1a, 00
+ int $0x1a # get system time
+ cmpw %di,%dx # Timeout?
+ jb read_key # No
+
+ /*
+ * Timed out or default selection
+ */
+use_default: movb _OPT(%bp),%al # Load default
+ orb $NOUPDATE,_FLAGS(%bp) # Disable updates
+ jmp check_selection # Join common code
+
+ /*
+ * Get the keystroke.
+ * ENTER or CR confirm the current selection (same as a timeout).
+ * Otherwise convert F1..F6 (or '1'..'6') to 0..5 and check if the
+ * selection is valid.
+ * The SIO code uses ascii chars, the console code uses scancodes.
+ */
+got_key:
+#ifndef SIO
+ xorb %ah,%ah # BIOS: int 0x16, 00
+ int $0x16 # get keypress
+ movb %ah,%al # move scan code to %al
+ cmpb $KEY_ENTER,%al
+#else
+ movb $0x02,%ah # BIOS: Receive
+ call bioscom
+ cmpb $ASCII_CR,%al
+#endif
+ je use_default # enter -> default
+ /*
+ * Check if the key is acceptable, and loop back if not.
+ * The console (non-SIO) code looks at scancodes and accepts
+ * both F1..F6 and 1..6 (the latter costs 6 bytes of code),
+ * relying on the fact that F1..F6 have higher scancodes than 1..6
+ * The SIO code only takes 1..6
+ */
+#ifdef SIO /* SIO mode, use ascii values */
+ subb $'1',%al # Subtract '1' ascii code
+#else /* console mode -- use scancodes */
+ subb $KEY_F1,%al /* Subtract F1 scan code */
+#if !defined(ONLY_F_KEYS)
+ cmpb $0x5,%al # F1..F6
+ jna 3f # Yes
+ subb $(KEY_1 - KEY_F1),%al # Less #1 scan code
+ 3:
+#endif /* ONLY_F_KEYS */
+#endif /* SIO */
+check_selection:
+ cmpb $0x5,%al # F1..F6 or 1..6 ?
+#ifdef PXE /* enable PXE/INT18 using F6 */
+ jne 1f;
+ int $0x18 # found F6, try INT18
+ 1:
+#endif /* PXE */
+ jae beep # Not in F1..F5, beep
+
+ /*
+ * We have a selection. 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.
+ */
+ cbtw # Extend (%ah=0 used later)
+ btw %ax,_MNUOPT(%bp) # Option enabled?
+ jnc beep # No
+ /*
+ * Save the info in the original tables
+ * for rewriting to the disk.
+ */
+ movb %al,_OPT(%bp) # Save option
+
+ /*
+ * Make %si and %bx point to the fake partition at LBA 0 (CHS 0:0:1).
+ * Because the correct address is already in %bp, just use it.
+ * Set %dl with the drive number saved in byte 0.
+ * If we have pressed F5 or 5, then this is a good, fake value
+ * to present to the next stage boot code.
+ */
+ movw %bp,%si # Partition for write
+ movb (%si),%dl # Drive number, saved above
+ movw %si,%bx # Partition for read
+ cmpb $0x4,%al # F5/#5 pressed?
+ pushf # Save results for later
+ je 1f # Yes, F5
+
+ /*
+ * F1..F4 was pressed, so make %bx point to the currently
+ * selected partition, and leave the drive number unchanged.
+ */
+ 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.
+ * Around the call, save the partition pointer to %bx and
+ * restore to %si which is where the next stage expects it.
+ */
+ 1: pushw %bx # Save
+ testb $NOUPDATE,_FLAGS(%bp) # No updates?
+ jnz 2f # skip update
+ movw $start,%bx # Data to write
+ movb $0x3,%ah # Write sector
+ callw intx13 # to disk
+ 2: popw %si # Restore
+
+ /*
+ * If going to next drive, replace drive with selected one.
+ * Remember to un-ascii it. Hey 0x80 is already set, cool!
+ */
+ popf # Restore %al test results
+ jne 3f # If not F5/#5
+ movb _NXTDRV(%bp),%dl # Next drive
+ subb $'0',%dl # number
+ /*
+ * Load selected bootsector to the LOAD location in RAM. If read
+ * fails or there is no 0x55aa marker, treat it as a bad selection.
+ */
+ 3: movw $LOAD,%bx # Address for read
+ movb $0x2,%ah # Read sector
+ callw intx13 # from disk
+ jc beep # If error
+ cmpw $MAGIC,0x1fe(%bx) # Bootable?
+ jne beep # No
+ pushw %si # Save ptr to selected part.
+ callw putn # Leave some space
+ popw %si # Restore, next stage uses it
+ jmp *%bx # Invoke bootstrap
+
+/*
+ * Display routines
+ * putkey prints the option selected in %dl (F1..F5 or 1..5) followed by
+ * the string at %si
+ * putx: print the option in %dl followed by the string at %di
+ * also record the drive as valid.
+ * putn: print a crlf
+ * putstr: print the string at %si
+ * putchr: print the char in al
+ */
+
+/*
+ * Display the option and record the drive as valid in the options.
+ * That last point is done using the btsw instruction which does
+ * a test and set. We don't care for the test part.
+ */
+putx: btsw %dx,_MNUOPT(%bp) # Enable menu option
+ movw $item,%si # Display
+ callw putkey # key
+ movw %di,%si # Display the rest
+ callw putstr # Display string
+
+putn: movw $crlf,%si # To next line
+ jmp putstr
+
+putkey:
+#ifndef SIO
+ movb $'F',%al # Display
+ callw putchr # 'F'
+#endif
+ movb $'1',%al # Prepare
+ addb %dl,%al # digit
+
+putstr.1: callw putchr # Display char
+putstr: lodsb # Get byte
+ testb $0x80,%al # End of string?
+ jz putstr.1 # No
+ andb $~0x80,%al # Clear MSB then print last
+
+putchr:
+#ifndef SIO
+ pushw %bx # Save
+ movw $0x7,%bx # Page:attribute
+ movb $0xe,%ah # BIOS: Display
+ int $0x10 # character
+ popw %bx # Restore
+#else /* SIO */
+ movb $0x01,%ah # BIOS: Send character
+bioscom:
+ pushw %dx # Save
+ xorw %dx,%dx # Use COM1
+ int $0x14 # BIOS: Serial I/O
+ popw %dx # Restore
+#endif /* SIO */
+ retw # To caller
+
+/* One-sector disk I/O routine */
+
+/*
+ * %dl: drive, %si partition entry, %es:%bx transfer buffer.
+ * Load the CHS values and possibly the LBA address from the block
+ * at %si, and use the appropriate method to load the sector.
+ * Don't use packet mode for a floppy.
+ */
+intx13: # Prepare CHS parameters
+ 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
+#ifndef CHECK_DRIVE /* floppy support */
+ testb %dl, %dl # is this a floppy ?
+ jz 1f # Yes, use CHS mode
+#endif
+ testb $USEPACKET,_FLAGS(%bp) # Use packet interface?
+ jz 1f # 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
+ 1: int $0x13 # BIOS: Disk I/O
+ movw %di,%sp # Restore
+ popw %si # Restore
+ retw # To caller
+
+/*
+ * Various menu strings. 'item' goes after 'prompt' to save space.
+ * Also use shorter versions to make room for the PXE/INT18 code.
+ */
+prompt:
+#ifdef PXE
+ .ascii "\nF6 PXE\r"
+#endif
+ .ascii "\nBoot:"
+item: .ascii " "; .byte ' '|0x80
+crlf: .ascii "\r"; .byte '\n'|0x80
+
+/* Partition type tables */
+
+bootable_ids:
+ /*
+ * These values indicate bootable types we know about.
+ * Corresponding descriptions are at desc_ofs:
+ * Entries don't need to be sorted.
+ */
+ .byte 0x83, 0xa5, 0xa6, 0xa9, 0x06, 0x07, 0x0b
+#ifndef SAVE_MORE_MEMORY
+ .byte 0x05 # extended partition
+#endif
+#ifndef SAVE_MEMORY /* other DOS partitions */
+ .byte 0x01 # FAT12
+ .byte 0x04 # FAT16 < 32M
+#endif
+
+desc_ofs:
+ /*
+ * Offsets that match the known types above, used to point to the
+ * actual partition name. The last entry must point to os_misc,
+ * which is used for non-matching names.
+ */
+ .byte os_linux-. # 131, Linux
+ .byte os_freebsd-. # 165, FreeBSD
+ .byte os_bsd-. # 166, OpenBSD
+ .byte os_bsd-. # 169, NetBSD
+ .byte os_dos-. # 6, FAT16 >= 32M
+ .byte os_win-. # 7, NTFS
+ .byte os_win-. # 11, FAT32
+
+#ifndef SAVE_MORE_MEMORY
+ .byte os_ext-. # 5, DOS Ext
+#endif
+#ifndef SAVE_MEMORY
+ .byte os_dos-. # 1, FAT12 DOS
+ .byte os_dos-. # 4, FAT16 <32M
+#endif
+ .byte os_misc-. # Unknown
+
+ /*
+ * And here are the strings themselves. The last byte of
+ * the string has bit 7 set.
+ */
+os_misc: .byte '?'|0x80
+os_dos:
+#ifndef SAVE_MORE_MEMORY /* 'DOS' remapped to 'WIN' if no room */
+ .ascii "DO"; .byte 'S'|0x80
+#endif
+os_win: .ascii "Wi"; .byte 'n'|0x80
+os_linux: .ascii "Linu"; .byte 'x'|0x80
+os_freebsd: .ascii "Free"
+os_bsd: .ascii "BS"; .byte 'D'|0x80
+#ifndef SAVE_MORE_MEMORY
+os_ext: .ascii "EX"; .byte 'T'|0x80
+#endif
+
+ .org (0x200 + B0_OFF),0x90
+/*
+ * The boot0 version 1.0 parameter table.
+ * Do not move it nor change the "Drive " string, boot0cfg
+ * uses its offset and content to identify the boot sector.
+ * The other fields 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_num: .byte 0x80 # Drive to force
+flags: .byte FLAGS # Flags
+#ifdef VOLUME_SERIAL
+ .byte 0xa8,0xa8,0xa8,0xa8 # Volume Serial Number
+#endif
+ticks: .word TICKS # Delay
+
+ .org PRT_OFF
+/*
+ * Here is the 64 byte partition table that fdisk would fiddle with.
+ */
+partbl: .fill 0x40,0x1,0x0 # Partition table
+ .word MAGIC # Magic number
+ .org 0x200 # again, safety check
+endblock:
diff --git a/stand/i386/boot0sio/Makefile b/stand/i386/boot0sio/Makefile
new file mode 100644
index 0000000..1321dd3
--- /dev/null
+++ b/stand/i386/boot0sio/Makefile
@@ -0,0 +1,8 @@
+# $FreeBSD$
+
+.PATH: ${.CURDIR}/../boot0
+
+PROGNAME= boot0sio
+CFLAGS+= -DSIO
+
+.include "${.CURDIR}/../boot0/Makefile"
diff --git a/stand/i386/boot0sio/Makefile.depend b/stand/i386/boot0sio/Makefile.depend
new file mode 100644
index 0000000..f80275d
--- /dev/null
+++ b/stand/i386/boot0sio/Makefile.depend
@@ -0,0 +1,11 @@
+# $FreeBSD$
+# Autogenerated - do NOT edit!
+
+DIRDEPS = \
+
+
+.include <dirdeps.mk>
+
+.if ${DEP_RELDIR} == ${_DEP_RELDIR}
+# local dependencies - needed for -jN in clean tree
+.endif
diff --git a/stand/i386/boot2/Makefile b/stand/i386/boot2/Makefile
new file mode 100644
index 0000000..c3146b3
--- /dev/null
+++ b/stand/i386/boot2/Makefile
@@ -0,0 +1,99 @@
+# $FreeBSD$
+
+.include <bsd.init.mk>
+
+FILES= boot boot1 boot2
+
+NM?= nm
+
+# A value of 0x80 enables LBA support.
+BOOT_BOOT1_FLAGS?= 0x80
+
+BOOT_COMCONSOLE_PORT?= 0x3f8
+BOOT_COMCONSOLE_SPEED?= 9600
+B2SIOFMT?= 0x3
+
+REL1= 0x700
+ORG1= 0x7c00
+ORG2= 0x2000
+
+# Decide level of UFS support.
+BOOT2_UFS?= UFS1_AND_UFS2
+#BOOT2_UFS?= UFS2_ONLY
+#BOOT2_UFS?= UFS1_ONLY
+
+CFLAGS+=-fomit-frame-pointer \
+ -mrtd \
+ -mregparm=3 \
+ -D${BOOT2_UFS} \
+ -DFLAGS=${BOOT_BOOT1_FLAGS} \
+ -DSIOPRT=${BOOT_COMCONSOLE_PORT} \
+ -DSIOFMT=${B2SIOFMT} \
+ -DSIOSPD=${BOOT_COMCONSOLE_SPEED} \
+ -I${LDRSRC} \
+ -I${BTXLIB} \
+ -Wall -Waggregate-return -Wbad-function-cast -Wno-cast-align \
+ -Wmissing-declarations -Wmissing-prototypes -Wnested-externs \
+ -Wpointer-arith -Wshadow -Wstrict-prototypes -Wwrite-strings \
+ -Winline
+
+CFLAGS.gcc+= -Os \
+ -fno-asynchronous-unwind-tables \
+ --param max-inline-insns-single=100
+.if ${COMPILER_TYPE} == "gcc" && ${COMPILER_VERSION} <= 40201
+CFLAGS.gcc+= -mno-align-long-strings
+.endif
+
+CFLAGS.clang+= -Oz ${CLANG_OPT_SMALL}
+
+LD_FLAGS+=${LD_FLAGS_BIN}
+
+CLEANFILES+= boot
+
+boot: boot1 boot2
+ cat boot1 boot2 > boot
+
+CLEANFILES+= boot1 boot1.out boot1.o
+
+boot1: boot1.out
+ ${OBJCOPY} -S -O binary boot1.out ${.TARGET}
+
+boot1.out: boot1.o
+ ${LD} ${LD_FLAGS} -e start -Ttext ${ORG1} -o ${.TARGET} boot1.o
+
+CLEANFILES+= boot2 boot2.ld boot2.ldr boot2.bin boot2.out boot2.o \
+ boot2.h sio.o
+
+BOOT2SIZE= 7680
+
+boot2: boot2.ld
+ @set -- `ls -l ${.ALLSRC}`; x=$$((${BOOT2SIZE}-$$5)); \
+ echo "$$x bytes available"; test $$x -ge 0
+ ${DD} if=${.ALLSRC} of=${.TARGET} obs=${BOOT2SIZE} conv=osync
+
+boot2.ld: boot2.ldr boot2.bin ${BTXKERN}
+ btxld -v -E ${ORG2} -f bin -b ${BTXKERN} -l boot2.ldr \
+ -o ${.TARGET} -P 1 boot2.bin
+
+boot2.ldr:
+ ${DD} if=/dev/zero of=${.TARGET} bs=512 count=1
+
+boot2.bin: boot2.out
+ ${OBJCOPY} -S -O binary boot2.out ${.TARGET}
+
+boot2.out: ${BTXCRT} boot2.o sio.o
+ ${LD} ${LD_FLAGS} -Ttext ${ORG2} -o ${.TARGET} ${.ALLSRC}
+
+SRCS= boot2.c boot2.h
+
+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}` > ${.TARGET}
+
+.include <bsd.prog.mk>
+
+# XXX: clang integrated-as doesn't grok .codeNN directives yet
+CFLAGS.boot1.S= ${CLANG_NO_IAS}
diff --git a/stand/i386/boot2/Makefile.depend b/stand/i386/boot2/Makefile.depend
new file mode 100644
index 0000000..d5a67a6
--- /dev/null
+++ b/stand/i386/boot2/Makefile.depend
@@ -0,0 +1,14 @@
+# $FreeBSD$
+# Autogenerated - do NOT edit!
+
+DIRDEPS = \
+ include \
+ sys/boot/i386/btx/btx \
+ sys/boot/i386/btx/lib \
+
+
+.include <dirdeps.mk>
+
+.if ${DEP_RELDIR} == ${_DEP_RELDIR}
+# local dependencies - needed for -jN in clean tree
+.endif
diff --git a/stand/i386/boot2/boot1.S b/stand/i386/boot2/boot1.S
new file mode 100644
index 0000000..984ab52
--- /dev/null
+++ b/stand/i386/boot2/boot1.S
@@ -0,0 +1,373 @@
+/*
+ * 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,0x8c00 # 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. Note that boot1 does not use this relocated copy
+ * of itself while loading boot2; however, BTX reclaims the memory
+ * used by boot1 during its initialization. As a result, boot2 uses
+ * xread from the relocated copy.
+ */
+ 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 0x8c00 as our transfer buffer. Thus,
+ * boot1 ends up at 0x8c00, and boot2 starts at 0x8c00 + 0x200 = 0x8e00.
+ * The first part of boot2 is the disklabel, which is 0x200 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
+
+/*
+ * Enable A20 so we can access memory above 1 meg.
+ * Use the zero-valued %cx as a timeout for embedded hardware which do not
+ * have a keyboard controller.
+ */
+seta20: cli # Disable interrupts
+seta20.1: dec %cx # Timeout?
+ jz seta20.3 # Yes
+ 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
+seta20.3: sti # Enable interrupts
+
+ jmp start+MEM_JMP-MEM_ORG # Start BTX
+
+
+/*
+ * 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 $0xf000,$0xfff0 # 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: testb $FL_PACKET,%cs:MEM_REL+flags-start # LBA support enabled?
+ jz read.1 # No, use CHS
+ cmpb $0x80,%dl # Hard drive?
+ jb read.1 # No, use CHS
+ mov $0x55aa,%bx # Magic
+ push %dx # Save
+ movb $0x41,%ah # BIOS: Check
+ int $0x13 # extensions present
+ pop %dx # Restore
+ jc read.1 # If error, use CHS
+ cmp $0xaa55,%bx # Magic?
+ jne read.1 # No, so use CHS
+ testb $0x1,%cl # Packet interface?
+ jz read.1 # No, so use CHS
+ mov %bp,%si # Disk packet
+ movb $0x42,%ah # BIOS: Extended
+ int $0x13 # read
+ retw # To caller
+read.1: 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 ereturn # No, return an error
+ 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.1 # If not done
+read.6: 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/stand/i386/boot2/boot2.c b/stand/i386/boot2/boot2.c
new file mode 100644
index 0000000..cc5d76f
--- /dev/null
+++ b/stand/i386/boot2/boot2.c
@@ -0,0 +1,646 @@
+/*-
+ * 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"
+#include "paths.h"
+#include "rbx.h"
+
+/* Define to 0 to omit serial support */
+#ifndef SERIAL
+#define SERIAL 1
+#endif
+
+#define IO_KEYBOARD 1
+#define IO_SERIAL 2
+
+#if SERIAL
+#define DO_KBD (ioctrl & IO_KEYBOARD)
+#define DO_SIO (ioctrl & IO_SERIAL)
+#else
+#define DO_KBD (1)
+#define DO_SIO (0)
+#endif
+
+#define SECOND 18 /* Circa that many ticks in a second. */
+
+#define ARGS 0x900
+#define NOPT 14
+#define NDEV 3
+#define MEM_BASE 0x12
+#define MEM_EXT 0x15
+
+#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] = "DhaCcdgmnpqrsv"; /* Also 'P', 'S' */
+static const unsigned char flags[NOPT] = {
+ RBX_DUAL,
+ RBX_SERIAL,
+ RBX_ASKNAME,
+ RBX_CDROM,
+ RBX_CONFIG,
+ RBX_KDB,
+ RBX_GDB,
+ RBX_MUTE,
+ RBX_NOINTR,
+ RBX_PAUSE,
+ RBX_QUIET,
+ 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;
+ uint8_t slice;
+ uint8_t part;
+ unsigned start;
+ int init;
+} dsk;
+static char cmd[512], cmddup[512], knamebuf[1024];
+static const char *kname;
+uint32_t opts;
+static struct bootinfo bootinfo;
+#if SERIAL
+static int comspeed = SIOSPD;
+static uint8_t ioctrl = IO_KEYBOARD;
+#endif
+
+int main(void);
+void exit(int);
+static void load(void);
+static int parse(void);
+static int dskread(void *, unsigned, unsigned);
+static void printf(const char *,...);
+static void putchar(int);
+static int drvread(void *, unsigned, unsigned);
+static int keyhit(unsigned);
+static int xputc(int);
+static int xgetc(int);
+static inline int getc(int);
+
+static void memcpy(void *, const void *, int);
+static void
+memcpy(void *dst, const void *src, int len)
+{
+ const char *s = src;
+ char *d = dst;
+
+ while (len--)
+ *d++ = *s++;
+}
+
+static inline int
+strcmp(const char *s1, const char *s2)
+{
+ for (; *s1 == *s2 && *s1; s1++, s2++);
+ return (unsigned char)*s1 - (unsigned char)*s2;
+}
+
+#define UFS_SMALL_CGBASE
+#include "ufsread.c"
+
+static int
+xfsread(ufs_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 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)
+{
+ uint8_t autoboot;
+ ufs_ino_t ino;
+ size_t nbyte;
+
+ dmadat = (void *)(roundup2(__base + (int32_t)&_end, 0x10000) - __base);
+ v86.ctl = V86_FLAGS;
+ v86.efl = PSL_RESERVED_DEFAULT | PSL_I;
+ 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);
+
+ /* Process configuration file */
+
+ autoboot = 1;
+
+ if ((ino = lookup(PATH_CONFIG)) ||
+ (ino = lookup(PATH_DOTCONFIG))) {
+ nbyte = fsread(ino, cmd, sizeof(cmd) - 1);
+ cmd[nbyte] = '\0';
+ }
+
+ if (*cmd) {
+ memcpy(cmddup, cmd, sizeof(cmd));
+ if (parse())
+ autoboot = 0;
+ if (!OPT_CHECK(RBX_QUIET))
+ printf("%s: %s", PATH_CONFIG, cmddup);
+ /* 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 (!kname) {
+ kname = PATH_LOADER;
+ if (autoboot && !keyhit(3*SECOND)) {
+ load();
+ kname = PATH_KERNEL;
+ }
+ }
+
+ /* Present the user with the boot2 prompt. */
+
+ for (;;) {
+ if (!autoboot || !OPT_CHECK(RBX_QUIET))
+ printf("\nFreeBSD/x86 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 (DO_SIO)
+ sio_flush();
+ if (!autoboot || keyhit(3*SECOND))
+ getstr();
+ else if (!autoboot || !OPT_CHECK(RBX_QUIET))
+ 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;
+ static Elf32_Phdr ep[2];
+ static Elf32_Shdr es[2];
+ caddr_t p;
+ ufs_ino_t ino;
+ uint32_t addr;
+ int k;
+ uint8_t 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) {
+ 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;
+ } else if (IS_ELF(hdr.eh)) {
+ fs_off = hdr.eh.e_phoff;
+ for (j = k = 0; k < hdr.eh.e_phnum && j < 2; k++) {
+ 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++) {
+ *(Elf32_Word *)p = 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);
+ } else {
+ printf("Invalid %s\n", "format");
+ return;
+ }
+
+ 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], dsk.slice, dsk.unit, dsk.part),
+ 0, 0, 0, VTOP(&bootinfo));
+}
+
+static int
+parse()
+{
+ char *arg = cmd;
+ char *ep, *p, *q;
+ const char *cp;
+ unsigned int drv;
+ int c, i, j;
+ size_t k;
+
+ while ((c = *arg++)) {
+ if (c == ' ' || c == '\t' || c == '\n')
+ continue;
+ for (p = arg; *p && *p != '\n' && *p != ' ' && *p != '\t'; p++);
+ ep = p;
+ if (*p)
+ *p++ = 0;
+ if (c == '-') {
+ while ((c = *arg++)) {
+ if (c == 'P') {
+ if (*(uint8_t *)PTOV(0x496) & 0x10) {
+ cp = "yes";
+ } else {
+ opts |= OPT_SET(RBX_DUAL) | OPT_SET(RBX_SERIAL);
+ cp = "no";
+ }
+ printf("Keyboard: %s\n", cp);
+ continue;
+#if SERIAL
+ } else if (c == 'S') {
+ j = 0;
+ while ((unsigned int)(i = *arg++ - '0') <= 9)
+ j = j * 10 + i;
+ if (j > 0 && i == -'0') {
+ comspeed = j;
+ break;
+ }
+ /* Fall through to error below ('S' not in optstr[]). */
+#endif
+ }
+ for (i = 0; c != optstr[i]; i++)
+ if (i == NOPT - 1)
+ return -1;
+ opts ^= OPT_SET(flags[i]);
+ }
+#if SERIAL
+ ioctrl = OPT_CHECK(RBX_DUAL) ? (IO_SERIAL|IO_KEYBOARD) :
+ OPT_CHECK(RBX_SERIAL) ? IO_SERIAL : IO_KEYBOARD;
+ if (DO_SIO) {
+ if (sio_init(115200 / comspeed) != 0)
+ ioctrl &= ~IO_SERIAL;
+ }
+#endif
+ } 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 + 1)
+ 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;
+ }
+ k = ep - arg;
+ if (k > 0) {
+ if (k >= sizeof(knamebuf))
+ return -1;
+ memcpy(knamebuf, arg, k + 1);
+ kname = knamebuf;
+ }
+ }
+ arg = p;
+ }
+ return 0;
+}
+
+static int
+dskread(void *buf, unsigned lba, unsigned nblk)
+{
+ struct dos_partition *dp;
+ struct disklabel *d;
+ char *sec;
+ unsigned i;
+ uint8_t sl;
+ const char *reason;
+
+ 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) {
+ reason = "slice";
+ goto error;
+ }
+ 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) {
+ reason = "label";
+ goto error;
+ }
+ } 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) {
+ reason = "partition";
+ goto error;
+ }
+ 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);
+error:
+ printf("Invalid %s\n", reason);
+ return -1;
+}
+
+static void
+printf(const char *fmt,...)
+{
+ va_list ap;
+ static 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;
+
+ if (!OPT_CHECK(RBX_QUIET)) {
+ xputc(c = c << 8 | c >> 24);
+ xputc('\b');
+ }
+ 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 (OPT_CHECK(RBX_NOINTR))
+ return 0;
+ t0 = 0;
+ for (;;) {
+ if (xgetc(1))
+ return 1;
+ t1 = *(uint32_t *)PTOV(0x46c);
+ if (!t0)
+ t0 = t1;
+ if ((uint32_t)(t1 - t0) >= ticks)
+ return 0;
+ }
+}
+
+static int
+xputc(int c)
+{
+ if (DO_KBD)
+ putc(c);
+ if (DO_SIO)
+ sio_putc(c);
+ return c;
+}
+
+static int
+getc(int fn)
+{
+ v86.addr = 0x16;
+ v86.eax = fn << 8;
+ v86int();
+ return fn == 0 ? v86.eax & 0xff : !V86_ZR(v86.efl);
+}
+
+static int
+xgetc(int fn)
+{
+ if (OPT_CHECK(RBX_NOINTR))
+ return 0;
+ for (;;) {
+ if (DO_KBD && getc(1))
+ return fn ? 1 : getc(0);
+ if (DO_SIO && sio_ischar())
+ return fn ? 1 : sio_getc();
+ if (fn)
+ return 0;
+ }
+}
diff --git a/stand/i386/boot2/lib.h b/stand/i386/boot2/lib.h
new file mode 100644
index 0000000..d8d3317
--- /dev/null
+++ b/stand/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$
+ */
+
+int sio_init(int) __attribute__((regparm (3)));
+int sio_flush(void);
+void sio_putc(int) __attribute__((regparm (3)));
+int sio_getc(void);
+int sio_ischar(void);
diff --git a/stand/i386/boot2/sio.S b/stand/i386/boot2/sio.S
new file mode 100644
index 0000000..ca9d0a2
--- /dev/null
+++ b/stand/i386/boot2/sio.S
@@ -0,0 +1,84 @@
+/*
+ * 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
+
+ .globl sio_init
+ .globl sio_flush
+ .globl sio_putc
+ .globl sio_getc
+ .globl sio_ischar
+
+/* int sio_init(int div) */
+
+sio_init: pushl %eax
+ movw $SIO_PRT+0x3,%dx # Data format reg
+ movb $SIO_FMT|0x80,%al # Set format
+ outb %al,(%dx) # and DLAB
+ subb $0x3,%dl # Divisor latch reg
+ popl %eax
+ outw %ax,(%dx) # BPS
+ movw $SIO_PRT+0x3,%dx # Data format reg
+ 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
+ # Fallthrough
+
+/* int sio_flush(void) */
+
+sio_flush: xorl %ecx,%ecx # Timeout
+ movb $0x80,%ch # counter
+sio_flush.1: call sio_ischar # Check for character
+ jz sio_flush.2 # Till none
+ loop sio_flush.1 # or counter is zero
+ movb $1, %al # Exhausted all tries
+sio_flush.2: ret # To caller
+
+/* void sio_putc(int c) */
+
+sio_putc: pushl %eax
+ 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
+ popl %eax # Get the character
+ subb $0x5,%dl # Transmitter hold reg
+ outb %al,(%dx) # Write character
+sio_putc.2: ret # 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/stand/i386/btx/Makefile b/stand/i386/btx/Makefile
new file mode 100644
index 0000000..39f78ed
--- /dev/null
+++ b/stand/i386/btx/Makefile
@@ -0,0 +1,5 @@
+# $FreeBSD$
+
+SUBDIR= btx btxldr lib
+
+.include <bsd.subdir.mk>
diff --git a/stand/i386/btx/Makefile.inc b/stand/i386/btx/Makefile.inc
new file mode 100644
index 0000000..265f86d
--- /dev/null
+++ b/stand/i386/btx/Makefile.inc
@@ -0,0 +1,3 @@
+# $FreeBSD$
+
+.include "../Makefile.inc"
diff --git a/stand/i386/btx/btx/Makefile b/stand/i386/btx/btx/Makefile
new file mode 100644
index 0000000..f42e20d
--- /dev/null
+++ b/stand/i386/btx/btx/Makefile
@@ -0,0 +1,35 @@
+# $FreeBSD$
+
+.include <bsd.init.mk>
+
+PROG= btx
+INTERNALPROG=
+MAN=
+SRCS= btx.S
+
+.if defined(BOOT_BTX_NOHANG)
+BOOT_BTX_FLAGS=0x1
+.else
+BOOT_BTX_FLAGS=0x0
+.endif
+
+CFLAGS+=-DBTX_FLAGS=${BOOT_BTX_FLAGS}
+CFLAGS+=-I${BOOTSRC}/i386/common
+
+.if defined(BTX_SERIAL)
+BOOT_COMCONSOLE_PORT?= 0x3f8
+BOOT_COMCONSOLE_SPEED?= 9600
+B2SIOFMT?= 0x3
+
+CFLAGS+=-DBTX_SERIAL -DSIOPRT=${BOOT_COMCONSOLE_PORT} \
+ -DSIOFMT=${B2SIOFMT} -DSIOSPD=${BOOT_COMCONSOLE_SPEED}
+.endif
+
+ORG= 0x9000
+
+LDFLAGS+=${LDFLAGS_BIN}
+
+.include <bsd.prog.mk>
+
+# XXX: clang integrated-as doesn't grok .codeNN directives yet
+CFLAGS.btx.S= ${CLANG_NO_IAS}
diff --git a/stand/i386/btx/btx/Makefile.depend b/stand/i386/btx/btx/Makefile.depend
new file mode 100644
index 0000000..f80275d
--- /dev/null
+++ b/stand/i386/btx/btx/Makefile.depend
@@ -0,0 +1,11 @@
+# $FreeBSD$
+# Autogenerated - do NOT edit!
+
+DIRDEPS = \
+
+
+.include <dirdeps.mk>
+
+.if ${DEP_RELDIR} == ${_DEP_RELDIR}
+# local dependencies - needed for -jN in clean tree
+.endif
diff --git a/stand/i386/btx/btx/btx.S b/stand/i386/btx/btx/btx.S
new file mode 100644
index 0000000..87d09a5
--- /dev/null
+++ b/stand/i386/btx/btx/btx.S
@@ -0,0 +1,1082 @@
+/*
+ * 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$
+ */
+
+#include <bootargs.h>
+
+/*
+ * Memory layout.
+ */
+ .set MEM_BTX,0x1000 # Start of BTX memory
+ .set MEM_ESP0,0x1800 # Supervisor stack
+ .set MEM_BUF,0x1800 # Scratch buffer
+ .set MEM_ESPR,0x5e00 # Real mode stack
+ .set MEM_IDT,0x5e00 # IDT
+ .set MEM_TSS,0x5f98 # TSS
+ .set MEM_MAP,0x6000 # I/O bit map
+ .set MEM_TSS_END,0x7fff # End of TSS
+ .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
+/*
+ * Fields in %eflags.
+ */
+ .set PSL_RESERVED_DEFAULT,0x00000002
+ .set PSL_T,0x00000100 # Trap flag
+ .set PSL_I,0x00000200 # Interrupt enable flag
+ .set PSL_D,0x00000400 # String instruction direction
+ .set PSL_NT,0x00004000 # Nested task flag
+ .set PSL_VM,0x00020000 # Virtual 8086 mode flag
+ .set PSL_AC,0x00040000 # Alignment check flag
+/*
+ * 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_MAP,0x66 # I/O bit map base
+/*
+ * System calls.
+ */
+ .set SYS_EXIT,0x0 # Exit
+ .set SYS_EXEC,0x1 # Exec
+/*
+ * 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
+/*
+ * V86 control flags.
+ */
+ .set V86F_ADDR,0x10000 # Segment:offset address
+ .set V86F_CALLF,0x20000 # Emulate far call
+ .set V86F_FLAGS,0x40000 # Return flags
+/*
+ * 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_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 _TSSIO,MEM_MAP-MEM_TSS # TSS I/O base
+ .set _TSSLM,MEM_TSS_END-MEM_TSS # 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 0x2 # 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
+ rep # Zero-fill
+ stosw # memory
+/*
+ * Update real mode IDT for reflecting hardware interrupts.
+ */
+ mov $intr20,%bx # Address first handler
+ mov $0x10,%cx # Number of handlers
+ mov $0x20*4,%di # First real mode IDT entry
+init.0: mov %bx,(%di) # Store IP
+ inc %di # Address next
+ inc %di # entry
+ stosw # Store CS
+ add $4,%bx # Next handler
+ loop init.0 # Next IRQ
+/*
+ * Create IDT.
+ */
+ mov $MEM_IDT,%di
+ 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 $_TSSIO,TSS_MAP(%di) # Set I/O bit map base
+/*
+ * Bring up the system.
+ */
+ mov $0x2820,%bx # Set protected mode
+ callw setpic # IRQ offsets
+ lidt idtdesc # Set IDT
+ lgdt gdtdesc # Set GDT
+ mov %cr0,%eax # Switch to protected
+ inc %ax # 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 $ARGSPACE,%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
+#endif
+ 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 %cs: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
+/*
+ * 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 except # General protection
+ push $0xe # Int 0xe: #PF
+ jmp except # Page fault
+intx10: push $0x10 # Int 0x10: #MF
+ jmp ex_noc # Floating-point error
+/*
+ * 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
+ pushl %gs # Set GS
+ pushl %fs # Set FS
+ pushl %ds # Set DS
+ pushl %es # Set ES
+ cmpw $SEL_SCODE,0x44(%esp,1) # Supervisor mode?
+ jne except.1 # No
+ pushl %ss # Set SS
+ jmp except.2 # Join common code
+except.1: pushl 0x50(%esp,1) # Set SS
+except.2: pushl 0x50(%esp,1) # Set ESP
+ 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 $PSL_T,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
+
+/*
+ * 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
+
+/*
+ * Protected Mode 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
+
+/*
+ * Invoke real mode interrupt/function call from user mode with arguments.
+ */
+intx31: pushl $-1 # Dummy int no for btx_v86
+/*
+ * Invoke real mode interrupt/function call from protected mode.
+ *
+ * We place a trampoline on the user stack that will return to rret_tramp
+ * which will reenter protected mode and then finally return to the user
+ * client.
+ *
+ * Kernel frame %esi points to: Real mode stack frame at MEM_ESPR:
+ *
+ * -0x00 user %ss -0x04 kernel %esp (with full frame)
+ * -0x04 user %esp -0x08 btx_v86 pointer
+ * -0x08 user %eflags -0x0c flags (only used if interrupt)
+ * -0x0c user %cs -0x10 real mode CS:IP return trampoline
+ * -0x10 user %eip -0x12 real mode flags
+ * -0x14 int no -0x16 real mode CS:IP (target)
+ * -0x18 %eax
+ * -0x1c %ecx
+ * -0x20 %edx
+ * -0x24 %ebx
+ * -0x28 %esp
+ * -0x2c %ebp
+ * -0x30 %esi
+ * -0x34 %edi
+ * -0x38 %gs
+ * -0x3c %fs
+ * -0x40 %ds
+ * -0x44 %es
+ * -0x48 zero %eax (hardware int only)
+ * -0x4c zero %ecx (hardware int only)
+ * -0x50 zero %edx (hardware int only)
+ * -0x54 zero %ebx (hardware int only)
+ * -0x58 zero %esp (hardware int only)
+ * -0x5c zero %ebp (hardware int only)
+ * -0x60 zero %esi (hardware int only)
+ * -0x64 zero %edi (hardware int only)
+ * -0x68 zero %gs (hardware int only)
+ * -0x6c zero %fs (hardware int only)
+ * -0x70 zero %ds (hardware int only)
+ * -0x74 zero %es (hardware int only)
+ */
+int_hw: cld # String ops inc
+ pusha # Save gp regs
+ pushl %gs # Save
+ pushl %fs # seg
+ pushl %ds # regs
+ pushl %es
+ push $SEL_SDATA # Set up
+ popl %ds # to
+ pushl %ds # address
+ popl %es # data
+ leal 0x44(%esp,1),%esi # Base of frame
+ movl %esp,MEM_ESPR-0x04 # Save kernel stack pointer
+ movl -0x14(%esi),%eax # Get Int no
+ cmpl $-1,%eax # Hardware interrupt?
+ jne intusr.1 # Yes
+/*
+ * v86 calls save the btx_v86 pointer on the real mode stack and read
+ * the address and flags from the btx_v86 structure. For interrupt
+ * handler invocations (VM86 INTx requests), disable interrupts,
+ * tracing, and alignment checking while the handler runs.
+ */
+ movl $MEM_USR,%ebx # User base
+ movl %ebx,%edx # address
+ addl -0x4(%esi),%ebx # User ESP
+ movl (%ebx),%ebp # btx_v86 pointer
+ addl %ebp,%edx # Flatten btx_v86 ptr
+ movl %edx,MEM_ESPR-0x08 # Save btx_v86 ptr
+ movl V86_ADDR(%edx),%eax # Get int no/address
+ movl V86_CTL(%edx),%edx # Get control flags
+ movl -0x08(%esi),%ebx # Save user flags in %ebx
+ testl $V86F_ADDR,%edx # Segment:offset?
+ jnz intusr.4 # Yes
+ andl $~(PSL_I|PSL_T|PSL_AC),%ebx # Disable interrupts, tracing,
+ # and alignment checking for
+ # interrupt handler
+ jmp intusr.3 # Skip hardware interrupt
+/*
+ * Hardware interrupts store a NULL btx_v86 pointer and use the
+ * address (interrupt number) from the stack with empty flags. Also,
+ * push a dummy frame of zeros onto the stack for all the general
+ * purpose and segment registers and clear %eflags. This gives the
+ * hardware interrupt handler a clean slate.
+ */
+intusr.1: xorl %edx,%edx # Control flags
+ movl %edx,MEM_ESPR-0x08 # NULL btx_v86 ptr
+ movl $12,%ecx # Frame is 12 dwords
+intusr.2: pushl $0x0 # Fill frame
+ loop intusr.2 # with zeros
+ movl $PSL_RESERVED_DEFAULT,%ebx # Set clean %eflags
+/*
+ * Look up real mode IDT entry for hardware interrupts and VM86 INTx
+ * requests.
+ */
+intusr.3: shll $0x2,%eax # Scale
+ movl (%eax),%eax # Load int vector
+ jmp intusr.5 # Skip CALLF test
+/*
+ * Panic if V86F_CALLF isn't set with V86F_ADDR.
+ */
+intusr.4: testl $V86F_CALLF,%edx # Far call?
+ jnz intusr.5 # Ok
+ movl %edx,0x30(%esp,1) # Place VM86 flags in int no
+ movl $badvm86,%esi # Display bad
+ call putstr # VM86 call
+ popl %es # Restore
+ popl %ds # seg
+ popl %fs # regs
+ popl %gs
+ popal # Restore gp regs
+ jmp ex_noc # Panic
+/*
+ * %eax now holds the segment:offset of the function.
+ * %ebx now holds the %eflags to pass to real mode.
+ * %edx now holds the V86F_* flags.
+ */
+intusr.5: movw %bx,MEM_ESPR-0x12 # Pass user flags to real mode
+ # target
+/*
+ * If this is a v86 call, copy the seg regs out of the btx_v86 structure.
+ */
+ movl MEM_ESPR-0x08,%ecx # Get btx_v86 ptr
+ jecxz intusr.6 # Skip for hardware ints
+ leal -0x44(%esi),%edi # %edi => kernel stack seg regs
+ pushl %esi # Save
+ leal V86_ES(%ecx),%esi # %esi => btx_v86 seg regs
+ movl $4,%ecx # Copy seg regs
+ rep # from btx_v86
+ movsl # to kernel stack
+ popl %esi # Restore
+intusr.6: movl -0x08(%esi),%ebx # Copy user flags to real
+ movl %ebx,MEM_ESPR-0x0c # mode return trampoline
+ movl $rret_tramp,%ebx # Set return trampoline
+ movl %ebx,MEM_ESPR-0x10 # CS:IP
+ movl %eax,MEM_ESPR-0x16 # Real mode target CS:IP
+ ljmpw $SEL_RCODE,$intusr.7 # Change to 16-bit segment
+ .code16
+intusr.7: movl %cr0,%eax # Leave
+ dec %al # protected
+ movl %eax,%cr0 # mode
+ ljmpw $0x0,$intusr.8
+intusr.8: xorw %ax,%ax # Reset %ds
+ movw %ax,%ds # and
+ movw %ax,%ss # %ss
+ lidt ivtdesc # Set IVT
+ popl %es # Restore
+ popl %ds # seg
+ popl %fs # regs
+ popl %gs
+ popal # Restore gp regs
+ movw $MEM_ESPR-0x16,%sp # Switch to real mode stack
+ iret # Call target routine
+/*
+ * For the return to real mode we setup a stack frame like this on the real
+ * mode stack. Note that callf calls won't pop off the flags, but we just
+ * ignore that by repositioning %sp to be just above the btx_v86 pointer
+ * so it is aligned. The stack is relative to MEM_ESPR.
+ *
+ * -0x04 kernel %esp
+ * -0x08 btx_v86
+ * -0x0c %eax
+ * -0x10 %ecx
+ * -0x14 %edx
+ * -0x18 %ebx
+ * -0x1c %esp
+ * -0x20 %ebp
+ * -0x24 %esi
+ * -0x28 %edi
+ * -0x2c %gs
+ * -0x30 %fs
+ * -0x34 %ds
+ * -0x38 %es
+ * -0x3c %eflags
+ */
+rret_tramp: movw $MEM_ESPR-0x08,%sp # Reset stack pointer
+ pushal # Save gp regs
+ pushl %gs # Save
+ pushl %fs # seg
+ pushl %ds # regs
+ pushl %es
+ pushfl # Save %eflags
+ pushl $PSL_RESERVED_DEFAULT|PSL_D # Use clean %eflags with
+ popfl # string ops dec
+ xorw %ax,%ax # Reset seg
+ movw %ax,%ds # regs
+ movw %ax,%es # (%ss is already 0)
+ lidt idtdesc # Set IDT
+ lgdt gdtdesc # Set GDT
+ mov %cr0,%eax # Switch to protected
+ inc %ax # mode
+ mov %eax,%cr0 #
+ ljmp $SEL_SCODE,$rret_tramp.1 # To 32-bit code
+ .code32
+rret_tramp.1: xorl %ecx,%ecx # Zero
+ movb $SEL_SDATA,%cl # Setup
+ movw %cx,%ss # 32-bit
+ movw %cx,%ds # seg
+ movw %cx,%es # regs
+ movl MEM_ESPR-0x04,%esp # Switch to kernel stack
+ leal 0x44(%esp,1),%esi # Base of frame
+ andb $~0x2,tss_desc+0x5 # Clear TSS busy
+ movb $SEL_TSS,%cl # Set task
+ ltr %cx # register
+/*
+ * Now we are back in protected mode. The kernel stack frame set up
+ * before entering real mode is still intact. For hardware interrupts,
+ * leave the frame unchanged.
+ */
+ cmpl $0,MEM_ESPR-0x08 # Leave saved regs unchanged
+ jz rret_tramp.3 # for hardware ints
+/*
+ * For V86 calls, copy the registers off of the real mode stack onto
+ * the kernel stack as we want their updated values. Also, initialize
+ * the segment registers on the kernel stack.
+ *
+ * Note that the %esp in the kernel stack after this is garbage, but popa
+ * ignores it, so we don't have to fix it up.
+ */
+ leal -0x18(%esi),%edi # Kernel stack GP regs
+ pushl %esi # Save
+ movl $MEM_ESPR-0x0c,%esi # Real mode stack GP regs
+ movl $8,%ecx # Copy GP regs from
+ rep # real mode stack
+ movsl # to kernel stack
+ movl $SEL_UDATA,%eax # Selector for data seg regs
+ movl $4,%ecx # Initialize %ds,
+ rep # %es, %fs, and
+ stosl # %gs
+/*
+ * For V86 calls, copy the saved seg regs on the real mode stack back
+ * over to the btx_v86 structure. Also, conditionally update the
+ * saved eflags on the kernel stack based on the flags from the user.
+ */
+ movl MEM_ESPR-0x08,%ecx # Get btx_v86 ptr
+ leal V86_GS(%ecx),%edi # %edi => btx_v86 seg regs
+ leal MEM_ESPR-0x2c,%esi # %esi => real mode seg regs
+ xchgl %ecx,%edx # Save btx_v86 ptr
+ movl $4,%ecx # Copy seg regs
+ rep # from real mode stack
+ movsl # to btx_v86
+ popl %esi # Restore
+ movl V86_CTL(%edx),%edx # Read V86 control flags
+ testl $V86F_FLAGS,%edx # User wants flags?
+ jz rret_tramp.3 # No
+ movl MEM_ESPR-0x3c,%eax # Read real mode flags
+ andl $~(PSL_T|PSL_NT),%eax # Clear unsafe flags
+ movw %ax,-0x08(%esi) # Update user flags (low 16)
+/*
+ * Return to the user task
+ */
+rret_tramp.3: popl %es # Restore
+ popl %ds # seg
+ popl %fs # regs
+ popl %gs
+ popal # Restore gp regs
+ addl $4,%esp # Discard int no
+ iret # Return to user 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
+ 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
+ testl $PSL_VM,0x50(%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
+
+/*
+ * int 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
+ call sio_getc.1 # Get character
+
+/*
+ * int sio_flush(void)
+ */
+sio_flush: xorl %eax,%eax # Return value
+ xorl %ecx,%ecx # Timeout
+ movb $0x80,%ch # counter
+sio_flush.1: call sio_ischar # Check for character
+ jz sio_flush.2 # Till none
+ loop sio_flush.1 # or counter is zero
+ movb $1, %al # Exhausted all tries
+sio_flush.2: 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
+#else
+/*
+ * 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
+#endif
+
+ .code16
+/*
+ * Real Mode Hardware interrupt jump table.
+ */
+intr20: push $0x8 # Int 0x20: IRQ0
+ jmp int_hwr # V86 int 0x8
+ push $0x9 # Int 0x21: IRQ1
+ jmp int_hwr # V86 int 0x9
+ push $0xa # Int 0x22: IRQ2
+ jmp int_hwr # V86 int 0xa
+ push $0xb # Int 0x23: IRQ3
+ jmp int_hwr # V86 int 0xb
+ push $0xc # Int 0x24: IRQ4
+ jmp int_hwr # V86 int 0xc
+ push $0xd # Int 0x25: IRQ5
+ jmp int_hwr # V86 int 0xd
+ push $0xe # Int 0x26: IRQ6
+ jmp int_hwr # V86 int 0xe
+ push $0xf # Int 0x27: IRQ7
+ jmp int_hwr # V86 int 0xf
+ push $0x70 # Int 0x28: IRQ8
+ jmp int_hwr # V86 int 0x70
+ push $0x71 # Int 0x29: IRQ9
+ jmp int_hwr # V86 int 0x71
+ push $0x72 # Int 0x2a: IRQ10
+ jmp int_hwr # V86 int 0x72
+ push $0x73 # Int 0x2b: IRQ11
+ jmp int_hwr # V86 int 0x73
+ push $0x74 # Int 0x2c: IRQ12
+ jmp int_hwr # V86 int 0x74
+ push $0x75 # Int 0x2d: IRQ13
+ jmp int_hwr # V86 int 0x75
+ push $0x76 # Int 0x2e: IRQ14
+ jmp int_hwr # V86 int 0x76
+ push $0x77 # Int 0x2f: IRQ15
+ jmp int_hwr # V86 int 0x77
+/*
+ * Reflect hardware interrupts in real mode.
+ */
+int_hwr: push %ax # Save
+ push %ds # Save
+ push %bp # Save
+ mov %sp,%bp # Address stack frame
+ xchg %bx,6(%bp) # Swap BX, int no
+ xor %ax,%ax # Set %ds:%bx to
+ shl $2,%bx # point to
+ mov %ax,%ds # IDT entry
+ mov (%bx),%ax # Load IP
+ mov 2(%bx),%bx # Load CS
+ xchg %ax,4(%bp) # Swap saved %ax,%bx with
+ xchg %bx,6(%bp) # CS:IP of handler
+ pop %bp # Restore
+ pop %ds # Restore
+ lret # Jump to handler
+
+ .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
+tss_desc: .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
+/*
+ * Bad VM86 call panic
+ */
+badvm86: .asciz "Invalid VM86 Request\n"
+
+/*
+ * End of BTX memory.
+ */
+ .p2align 4
+break:
diff --git a/stand/i386/btx/btxldr/Makefile b/stand/i386/btx/btxldr/Makefile
new file mode 100644
index 0000000..3279597
--- /dev/null
+++ b/stand/i386/btx/btxldr/Makefile
@@ -0,0 +1,23 @@
+# $FreeBSD$
+
+.include <bsd.init.mk>
+
+PROG= btxldr
+INTERNALPROG=
+MAN=
+SRCS= btxldr.S
+
+CFLAGS+=-DLOADER_ADDRESS=${LOADER_ADDRESS}
+CFLAGS+=-I${BOOTSRC}/i386/common
+
+.if defined(BTXLDR_VERBOSE)
+CFLAGS+=-DBTXLDR_VERBOSE
+.endif
+
+ORG=${LOADER_ADDRESS}
+LDFLAGS+=${LDFLAGS_BIN}
+
+.include <bsd.prog.mk>
+
+# XXX: clang integrated-as doesn't grok .codeNN directives yet
+CFLAGS.btxldr.S= ${CLANG_NO_IAS}
diff --git a/stand/i386/btx/btxldr/Makefile.depend b/stand/i386/btx/btxldr/Makefile.depend
new file mode 100644
index 0000000..f80275d
--- /dev/null
+++ b/stand/i386/btx/btxldr/Makefile.depend
@@ -0,0 +1,11 @@
+# $FreeBSD$
+# Autogenerated - do NOT edit!
+
+DIRDEPS = \
+
+
+.include <dirdeps.mk>
+
+.if ${DEP_RELDIR} == ${_DEP_RELDIR}
+# local dependencies - needed for -jN in clean tree
+.endif
diff --git a/stand/i386/btx/btxldr/btxldr.S b/stand/i386/btx/btxldr/btxldr.S
new file mode 100644
index 0000000..1a0f5f4
--- /dev/null
+++ b/stand/i386/btx/btxldr/btxldr.S
@@ -0,0 +1,409 @@
+/*
+ * 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$
+ */
+
+#include <bootargs.h>
+
+#define RBX_MUTE 0x10 /* -m */
+#define OPT_SET(opt) (1 << (opt))
+
+/*
+ * 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
+ testl $OPT_SET(RBX_MUTE), 4(%esp) # Check first argument
+ setnz muted # for RBX_MUTE, set flag
+ 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
+#endif
+ 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),%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
+#endif
+ movl BA_BOOTINFO+4(%esp),%esi # Source: bootinfo
+ cmpl $0x0, %esi # If the bootinfo pointer
+ je start_null_bi # is null, don't copy it
+ movl BI_SIZE(%esi),%ecx # Allocate space
+ subl %ecx,%ebp # for bootinfo
+ movl %ebp,%edi # Destination
+ rep # Copy
+ movsb # it
+ movl %ebp,BA_BOOTINFO+4(%esp) # Update pointer
+ movl %edi,%ebp # Restore base pointer
+#ifdef BTXLDR_VERBOSE
+ movl $m_rel_bi,%esi # Display
+ movl %ebp,%eax # bootinfo
+ call hexout # relocation
+ call putstr # message
+#endif
+start_null_bi: movl $BOOTARGS_SIZE,%ecx # Fixed size of arguments
+ testl $KARGS_FLAGS_EXTARG, BA_BOOTFLAGS+4(%esp) # Check for extra data
+ jz start_fixed # Skip if the flag is not set
+ addl BOOTARGS_SIZE+4(%esp),%ecx # Add size of variable args
+start_fixed: subl $ARGOFF,%ebp # Place args at fixed offset
+ leal 0x4(%esp),%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
+#endif
+/*
+ * 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
+#endif
+ rep # Relocate
+ movsb # BTX
+ movl %esi,%ebx # Keep place
+#ifdef BTXLDR_VERBOSE
+ movl $m_rel_btx,%esi # Restore
+ popl %eax # parameters
+ call hexout # and
+#endif
+ popl %ebp # display
+#ifdef BTXLDR_VERBOSE
+ movl %ebp,%eax # the
+ call hexout # relocation
+ call putstr # message
+#endif
+ addl $PAG_SIZ,%ebp # Display
+#ifdef BTXLDR_VERBOSE
+ movl $m_base,%esi # the
+ movl %ebp,%eax # user
+ call hexout # base
+ call putstr # address
+#endif
+/*
+ * 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
+#endif
+ 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
+#endif
+ pushl %esi # Save
+ pushl %ecx # working 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 %esi # registers
+start.6: addl $0x20,%edx # To next entry
+ loop start.4 # Till done
+#ifdef BTXLDR_VERBOSE
+ movl $m_done,%esi # Display done
+ call putstr # message
+#endif
+ 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: testb $1,muted # Check muted
+ jnz putchr.5 # do a nop
+ 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
+putchr.5: 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"
+#endif
+
+/*
+ * Flags
+ */
+muted: .byte 0x0
+
+/*
+ * Uninitialized data area.
+ */
+buf: # Scratch buffer
diff --git a/stand/i386/btx/lib/Makefile b/stand/i386/btx/lib/Makefile
new file mode 100644
index 0000000..8de6255
--- /dev/null
+++ b/stand/i386/btx/lib/Makefile
@@ -0,0 +1,12 @@
+# $FreeBSD$
+
+.include <bsd.init.mk>
+
+PROG= crt0.o
+INTERNALPROG=
+MAN=
+SRCS= btxcsu.S btxsys.s btxv86.s
+CFLAGS+=-I${BOOTSRC}/i386/common
+LDFLAGS+=-Wl,-r
+
+.include <bsd.prog.mk>
diff --git a/stand/i386/btx/lib/Makefile.depend b/stand/i386/btx/lib/Makefile.depend
new file mode 100644
index 0000000..f80275d
--- /dev/null
+++ b/stand/i386/btx/lib/Makefile.depend
@@ -0,0 +1,11 @@
+# $FreeBSD$
+# Autogenerated - do NOT edit!
+
+DIRDEPS = \
+
+
+.include <dirdeps.mk>
+
+.if ${DEP_RELDIR} == ${_DEP_RELDIR}
+# local dependencies - needed for -jN in clean tree
+.endif
diff --git a/stand/i386/btx/lib/btxcsu.S b/stand/i386/btx/lib/btxcsu.S
new file mode 100644
index 0000000..c46f809
--- /dev/null
+++ b/stand/i386/btx/lib/btxcsu.S
@@ -0,0 +1,49 @@
+#
+# 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$
+
+#include <bootargs.h>
+
+#
+# BTX C startup code (ELF).
+#
+
+#
+# Globals.
+#
+ .global _start
+#
+# Client entry point.
+#
+_start: cld
+ pushl %eax
+ movl $_edata,%edi
+ movl $_end,%ecx
+ subl %edi, %ecx
+ xorb %al, %al
+ rep
+ stosb
+ popl __base
+ 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/stand/i386/btx/lib/btxsys.s b/stand/i386/btx/lib/btxsys.s
new file mode 100644
index 0000000..9c77b42
--- /dev/null
+++ b/stand/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/stand/i386/btx/lib/btxv86.h b/stand/i386/btx/lib/btxv86.h
new file mode 100644
index 0000000..f04ce5e
--- /dev/null
+++ b/stand/i386/btx/lib/btxv86.h
@@ -0,0 +1,75 @@
+/*
+ * 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>
+#include <machine/psl.h>
+
+/*
+ * Memory buffer space for real mode IO.
+ * Just one page is not much, but the space is rather limited.
+ * See ../btx/btx.S for details.
+ */
+#define V86_IO_BUFFER 0x8000
+#define V86_IO_BUFFER_SIZE 0x1000
+
+#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)
+
+#define V86_CY(x) ((x) & PSL_C)
+#define V86_ZR(x) ((x) & PSL_Z)
+
+void __exit(int) __attribute__((__noreturn__));
+void __exec(caddr_t, ...);
+
+#endif /* !_BTXV86_H_ */
diff --git a/stand/i386/btx/lib/btxv86.s b/stand/i386/btx/lib/btxv86.s
new file mode 100644
index 0000000..0d7d111
--- /dev/null
+++ b/stand/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/stand/i386/cdboot/Makefile b/stand/i386/cdboot/Makefile
new file mode 100644
index 0000000..7acd404
--- /dev/null
+++ b/stand/i386/cdboot/Makefile
@@ -0,0 +1,20 @@
+# $FreeBSD$
+
+.include <bsd.init.mk>
+
+PROG= cdboot
+STRIP=
+BINMODE=${NOBINMODE}
+MAN=
+SRCS= ${PROG}.S
+
+CFLAGS+=-I${BOOTSRC}/i386/common
+
+ORG= 0x7c00
+
+LDFLAGS+=${LDFLAGS_BIN}
+
+.include <bsd.prog.mk>
+
+# XXX: clang integrated-as doesn't grok .codeNN directives yet
+CFLAGS.cdboot.S= ${CLANG_NO_IAS}
diff --git a/stand/i386/cdboot/Makefile.depend b/stand/i386/cdboot/Makefile.depend
new file mode 100644
index 0000000..f80275d
--- /dev/null
+++ b/stand/i386/cdboot/Makefile.depend
@@ -0,0 +1,11 @@
+# $FreeBSD$
+# Autogenerated - do NOT edit!
+
+DIRDEPS = \
+
+
+.include <dirdeps.mk>
+
+.if ${DEP_RELDIR} == ${_DEP_RELDIR}
+# local dependencies - needed for -jN in clean tree
+.endif
diff --git a/stand/i386/cdboot/cdboot.S b/stand/i386/cdboot/cdboot.S
new file mode 100644
index 0000000..9c3fb15
--- /dev/null
+++ b/stand/i386/cdboot/cdboot.S
@@ -0,0 +1,594 @@
+#
+# Copyright (c) 2001 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$
+
+#
+# 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.
+#
+
+#include <bootargs.h>
+
+#
+# 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
+#
+# 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
+ orb $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
+#
+# Try to look up the loader binary using the paths in the loader_paths
+# array.
+#
+ mov $loader_paths,%si # Point to start of array
+lookup_path: push %si # Save file name pointer
+ call lookup # Try to find file
+ pop %di # Restore file name pointer
+ jnc lookup_found # Found this file
+ xor %al,%al # Look for next
+ mov $0xffff,%cx # path name by
+ repnz # scanning for
+ scasb # nul char
+ mov %di,%si # Point %si at next path
+ mov (%si),%al # Get first char of next path
+ or %al,%al # Is it double nul?
+ jnz lookup_path # No, try it.
+ mov $msg_failed,%si # Failed message
+ jmp error # Halt
+lookup_found: # Found a loader file
+#
+# Load the binary into the buffer. Due to real mode addressing limitations
+# we have to read it in 64k chunks.
+#
+ mov DIR_SIZE(%bx),%eax # Read file length
+ add $SECTOR_SIZE-1,%eax # Convert length to sectors
+ shr $SECTOR_SHIFT,%eax
+ cmp $BUFFER_LEN,%eax
+ jbe load_sizeok
+ mov $msg_load2big,%si # Error message
+ 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: CF = 0 (success), BX = pointer to record
+# CF = 1 (not found)
+#
+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 message
+ call putstr
+ stc # Set carry
+ ret
+ jmp error
+lookup_done: mov $msg_lookupok,%si # Success message
+ call putstr
+ clc # Clear carry
+ ret
+
+#
+# Lookup file at [SI] in directory whose record is at [BX].
+#
+# Trashes: All but returns
+# Returns: CF = 0 (success), BX = pointer to record, SI = next path item
+# CF = 1 (not found), SI = preserved
+#
+find_file: mov 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
+ je ff.match # We have a winner!
+ pop %si # Restore
+ jmp ff.nextrec # Keep looking.
+ff.match: add $2,%sp # Discard saved %si
+ clc # Clear carry
+ ret
+
+#
+# Load DH sectors starting at LBA EAX into [EBX].
+#
+# Trashes: EAX
+#
+read: push %si # Save
+ push %cx # Save since some BIOSs trash
+ 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 %cx # Restore
+ pop %si
+ 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
+ mov %al,twiddle_index # Save index for next call
+ xlat # Get char
+ call putc # Output it
+ mov $8,%al # Backspace
+ call putc # Output it
+ pop %bx # Restore
+ pop %ax # Restore
+ ret
+
+#
+# Enable A20. Put an upper limit on the amount of time we wait for the
+# keyboard controller to get ready (65K x ISA access time). If
+# we wait more than that amount, the hardware is probably
+# legacy-free and simply doesn't have a keyboard controller.
+# Thus, the A20 line is already enabled.
+#
+seta20: cli # Disable interrupts
+ xor %cx,%cx # Clear
+seta20.1: inc %cx # Increment, overflow?
+ jz seta20.3 # Yes
+ 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
+seta20.3: 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.2\r\n\n"
+msg_bootinfo: .asciz "Building the boot loader arguments\r\n"
+msg_relocate: .asciz "Relocating the loader and the BTX\r\n"
+msg_jump: .asciz "Starting the BTX loader\r\n"
+msg_badread: .ascii "Read Error: 0x"
+hex_error: .asciz "00\r\n"
+msg_novd: .asciz "Could not find Primary Volume Descriptor\r\n"
+msg_lookup: .asciz "Looking up "
+msg_lookup2: .asciz "... "
+msg_lookupok: .asciz "Found\r\n"
+msg_lookupfail: .asciz "File not found\r\n"
+msg_load2big: .asciz "File too big\r\n"
+msg_failed: .asciz "Boot failed\r\n"
+twiddle_chars: .ascii "|/-\\"
+loader_paths: .asciz "/BOOT/LOADER"
+ .asciz "/boot/loader"
+ .byte 0
+
diff --git a/stand/i386/common/bootargs.h b/stand/i386/common/bootargs.h
new file mode 100644
index 0000000..df55807
--- /dev/null
+++ b/stand/i386/common/bootargs.h
@@ -0,0 +1,93 @@
+/*-
+ * Copyright (c) 2012 Andriy Gapon <avg@FreeBSD.org>
+ * 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 _BOOT_I386_ARGS_H_
+#define _BOOT_I386_ARGS_H_
+
+#define KARGS_FLAGS_CD 0x1
+#define KARGS_FLAGS_PXE 0x2
+#define KARGS_FLAGS_ZFS 0x4
+#define KARGS_FLAGS_EXTARG 0x8 /* variably sized extended argument */
+
+#define BOOTARGS_SIZE 24 /* sizeof(struct bootargs) */
+#define BA_BOOTFLAGS 8 /* offsetof(struct bootargs, bootflags) */
+#define BA_BOOTINFO 20 /* offsetof(struct bootargs, bootinfo) */
+#define BI_SIZE 48 /* offsetof(struct bootinfo, bi_size) */
+
+/*
+ * We reserve some space above BTX allocated stack for the arguments
+ * and certain data that could hang off them. Currently only struct bootinfo
+ * is supported in that category. The bootinfo is placed at the top
+ * of the arguments area and the actual arguments are placed at ARGOFF offset
+ * from the top and grow towards the top. Hopefully we have enough space
+ * for bootinfo and the arguments to not run into each other.
+ * Arguments area below ARGOFF is reserved for future use.
+ */
+#define ARGSPACE 0x1000 /* total size of the BTX args area */
+#define ARGOFF 0x800 /* actual args offset within the args area */
+#define ARGADJ (ARGSPACE - ARGOFF)
+
+#ifndef __ASSEMBLER__
+
+struct bootargs
+{
+ uint32_t howto;
+ uint32_t bootdev;
+ uint32_t bootflags;
+ union {
+ struct {
+ uint32_t pxeinfo;
+ uint32_t reserved;
+ };
+ uint64_t zfspool;
+ };
+ uint32_t bootinfo;
+
+ /*
+ * If KARGS_FLAGS_EXTARG is set in bootflags, then the above fields
+ * are followed by a uint32_t field that specifies a size of the
+ * extended arguments (including the size field).
+ */
+};
+
+#ifdef LOADER_GELI_SUPPORT
+#include <crypto/intake.h>
+#endif
+
+struct geli_boot_args
+{
+ uint32_t size;
+ union {
+ char gelipw[256];
+ struct {
+ char notapw; /*
+ * single null byte to stop keybuf
+ * being interpreted as a password
+ */
+ uint32_t keybuf_sentinel;
+#ifdef LOADER_GELI_SUPPORT
+ struct keybuf *keybuf;
+#else
+ void *keybuf;
+#endif
+ };
+ };
+};
+
+#endif /*__ASSEMBLER__*/
+
+#endif /* !_BOOT_I386_ARGS_H_ */
diff --git a/stand/i386/common/cons.c b/stand/i386/common/cons.c
new file mode 100644
index 0000000..5fb1a93
--- /dev/null
+++ b/stand/i386/common/cons.c
@@ -0,0 +1,177 @@
+/*-
+ * 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 <machine/psl.h>
+
+#include <btxv86.h>
+
+#include "lib.h"
+#include "rbx.h"
+#include "util.h"
+#include "cons.h"
+
+#define SECOND 18 /* Circa that many ticks in a second. */
+
+uint8_t ioctrl = IO_KEYBOARD;
+
+void
+putc(int c)
+{
+
+ v86.ctl = V86_FLAGS;
+ v86.addr = 0x10;
+ v86.eax = 0xe00 | (c & 0xff);
+ v86.ebx = 0x7;
+ v86int();
+}
+
+void
+xputc(int c)
+{
+
+ if (ioctrl & IO_KEYBOARD)
+ putc(c);
+ if (ioctrl & IO_SERIAL)
+ sio_putc(c);
+}
+
+void
+putchar(int c)
+{
+
+ if (c == '\n')
+ xputc('\r');
+ xputc(c);
+}
+
+int
+getc(int fn)
+{
+
+ v86.ctl = V86_FLAGS;
+ v86.addr = 0x16;
+ v86.eax = fn << 8;
+ v86int();
+
+ if (fn == 0)
+ return (v86.eax);
+
+ if (V86_ZR(v86.efl))
+ return (0);
+ return (v86.eax);
+}
+
+int
+xgetc(int fn)
+{
+
+ if (OPT_CHECK(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);
+ }
+ /* NOTREACHED */
+}
+
+int
+getchar(void)
+{
+
+ return (xgetc(0));
+}
+
+int
+keyhit(unsigned int secs)
+{
+ uint32_t t0, t1, c;
+
+ if (OPT_CHECK(RBX_NOINTR))
+ return (0);
+ secs *= SECOND;
+ t0 = 0;
+ for (;;) {
+ /*
+ * The extra comparison is an attempt to work around
+ * what appears to be a bug in QEMU and Bochs. Both emulators
+ * sometimes report a key-press with scancode one and ascii zero
+ * when no such key is pressed in reality. As far as I can tell,
+ * this only happens shortly after a reboot.
+ */
+ c = xgetc(1);
+ if (c != 0 && c != 0x0100)
+ return (1);
+ if (secs > 0) {
+ t1 = *(uint32_t *)PTOV(0x46c);
+ if (!t0)
+ t0 = t1;
+ if (t1 < t0 || t1 >= t0 + secs)
+ return (0);
+ }
+ }
+ /* NOTREACHED */
+}
+
+void
+getstr(char *cmdstr, size_t cmdstrsize)
+{
+ char *s;
+ int c;
+
+ s = cmdstr;
+ for (;;) {
+ c = xgetc(0);
+
+ /* Translate some extended codes. */
+ switch (c) {
+ case 0x5300: /* delete */
+ c = '\177';
+ break;
+ default:
+ c &= 0xff;
+ break;
+ }
+
+ switch (c) {
+ case '\177':
+ case '\b':
+ if (s > cmdstr) {
+ s--;
+ printf("\b \b");
+ }
+ break;
+ case '\n':
+ case '\r':
+ *s = 0;
+ return;
+ default:
+ if (c >= 0x20 && c <= 0x7e) {
+ if (s - cmdstr < cmdstrsize - 1)
+ *s++ = c;
+ putchar(c);
+ }
+ break;
+ }
+ }
+}
diff --git a/stand/i386/common/cons.h b/stand/i386/common/cons.h
new file mode 100644
index 0000000..73474fb
--- /dev/null
+++ b/stand/i386/common/cons.h
@@ -0,0 +1,35 @@
+/*-
+ * 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 _CONS_H_
+#define _CONS_H_
+
+#define IO_KEYBOARD 1
+#define IO_SERIAL 2
+
+extern uint8_t ioctrl;
+
+void putc(int c);
+void xputc(int c);
+void putchar(int c);
+int getc(int fn);
+int xgetc(int fn);
+int getchar(void);
+int keyhit(unsigned int secs);
+void getstr(char *cmdstr, size_t cmdstrsize);
+
+#endif /* !_CONS_H_ */
diff --git a/stand/i386/common/drv.c b/stand/i386/common/drv.c
new file mode 100644
index 0000000..a805c41
--- /dev/null
+++ b/stand/i386/common/drv.c
@@ -0,0 +1,102 @@
+/*-
+ * Copyright (c) 1998 Robert Nordier
+ * Copyright (c) 2010 Pawel Jakub Dawidek <pjd@FreeBSD.org>
+ * 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 <btxv86.h>
+
+#include "rbx.h"
+#include "util.h"
+#include "drv.h"
+#include "edd.h"
+
+static struct edd_params params;
+
+uint64_t
+drvsize(struct dsk *dskp)
+{
+
+ params.len = sizeof(struct edd_params);
+ v86.ctl = V86_FLAGS;
+ v86.addr = 0x13;
+ v86.eax = 0x4800;
+ v86.edx = dskp->drive;
+ v86.ds = VTOPSEG(&params);
+ v86.esi = VTOPOFF(&params);
+ v86int();
+ if (V86_CY(v86.efl)) {
+ printf("error %u\n", v86.eax >> 8 & 0xff);
+ return (0);
+ }
+ return (params.sectors);
+}
+
+static struct edd_packet packet;
+
+int
+drvread(struct dsk *dskp, void *buf, daddr_t lba, unsigned nblk)
+{
+ static unsigned c = 0x2d5c7c2f;
+
+ if (!OPT_CHECK(RBX_QUIET))
+ printf("%c\b", c = c << 8 | c >> 24);
+ packet.len = sizeof(struct edd_packet);
+ packet.count = nblk;
+ packet.off = VTOPOFF(buf);
+ packet.seg = VTOPSEG(buf);
+ packet.lba = lba;
+ v86.ctl = V86_FLAGS;
+ v86.addr = 0x13;
+ v86.eax = 0x4200;
+ v86.edx = dskp->drive;
+ v86.ds = VTOPSEG(&packet);
+ v86.esi = VTOPOFF(&packet);
+ v86int();
+ if (V86_CY(v86.efl)) {
+ printf("%s: error %u lba %u\n",
+ BOOTPROG, v86.eax >> 8 & 0xff, lba);
+ return (-1);
+ }
+ return (0);
+}
+
+#if defined(GPT) || defined(ZFS)
+int
+drvwrite(struct dsk *dskp, void *buf, daddr_t lba, unsigned nblk)
+{
+
+ packet.len = sizeof(struct edd_packet);
+ packet.count = nblk;
+ packet.off = VTOPOFF(buf);
+ packet.seg = VTOPSEG(buf);
+ packet.lba = lba;
+ v86.ctl = V86_FLAGS;
+ v86.addr = 0x13;
+ v86.eax = 0x4300;
+ v86.edx = dskp->drive;
+ v86.ds = VTOPSEG(&packet);
+ v86.esi = VTOPOFF(&packet);
+ v86int();
+ if (V86_CY(v86.efl)) {
+ printf("error %u lba %u\n", v86.eax >> 8 & 0xff, lba);
+ return (-1);
+ }
+ return (0);
+}
+#endif /* GPT || ZFS */
diff --git a/stand/i386/common/drv.h b/stand/i386/common/drv.h
new file mode 100644
index 0000000..c0995df
--- /dev/null
+++ b/stand/i386/common/drv.h
@@ -0,0 +1,48 @@
+/*-
+ * Copyright (c) 2010 Pawel Jakub Dawidek <pjd@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 AUTHORS 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 AUTHORS 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$
+ */
+
+#ifndef _DRV_H_
+#define _DRV_H_
+
+struct dsk {
+ unsigned int drive;
+ unsigned int type;
+ unsigned int unit;
+ unsigned int slice;
+ int part;
+ daddr_t start;
+ uint64_t size;
+};
+
+int drvread(struct dsk *dskp, void *buf, daddr_t lba, unsigned nblk);
+#if defined(GPT) || defined(ZFS)
+int drvwrite(struct dsk *dskp, void *buf, daddr_t lba, unsigned nblk);
+#endif /* GPT || ZFS */
+uint64_t drvsize(struct dsk *dskp);
+
+#endif /* !_DRV_H_ */
diff --git a/stand/i386/common/edd.h b/stand/i386/common/edd.h
new file mode 100644
index 0000000..7d1f450
--- /dev/null
+++ b/stand/i386/common/edd.h
@@ -0,0 +1,110 @@
+/*-
+ * Copyright (c) 2011 Hudson River Trading LLC
+ * Written by: 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.
+ *
+ * $FreeBSD$
+ */
+
+#ifndef _EDD_H_
+#define _EDD_H_
+
+/* Supported interfaces for "Check Extensions Present". */
+#define EDD_INTERFACE_FIXED_DISK 0x01
+#define EDD_INTERFACE_EJECT 0x02
+#define EDD_INTERFACE_EDD 0x04
+
+struct edd_packet {
+ uint16_t len;
+ uint16_t count;
+ uint16_t off;
+ uint16_t seg;
+ uint64_t lba;
+};
+
+struct edd_packet_v3 {
+ uint16_t len;
+ uint16_t count;
+ uint16_t off;
+ uint16_t seg;
+ uint64_t lba;
+ uint64_t phys_addr;
+};
+
+struct edd_params {
+ uint16_t len;
+ uint16_t flags;
+ uint32_t cylinders;
+ uint32_t heads;
+ uint32_t sectors_per_track;
+ uint64_t sectors;
+ uint16_t sector_size;
+ uint16_t edd_params_seg;
+ uint16_t edd_params_off;
+} __packed;
+
+struct edd_device_path_v3 {
+ uint16_t key;
+ uint8_t len;
+ uint8_t reserved[3];
+ char host_bus[4];
+ char interface[8];
+ uint64_t interface_path;
+ uint64_t device_path;
+ uint8_t reserved2[1];
+ uint8_t checksum;
+} __packed;
+
+struct edd_params_v3 {
+ struct edd_params params;
+ struct edd_device_path_v3 device_path;
+} __packed;
+
+struct edd_device_path_v4 {
+ uint16_t key;
+ uint8_t len;
+ uint8_t reserved[3];
+ char host_bus[4];
+ char interface[8];
+ uint64_t interface_path;
+ uint64_t device_path[2];
+ uint8_t reserved2[1];
+ uint8_t checksum;
+} __packed;
+
+struct edd_params_v4 {
+ struct edd_params params;
+ struct edd_device_path_v4 device_path;
+} __packed;
+
+#define EDD_FLAGS_DMA_BOUNDARY_HANDLING 0x0001
+#define EDD_FLAGS_REMOVABLE_MEDIA 0x0002
+#define EDD_FLAGS_WRITE_VERIFY 0x0004
+#define EDD_FLAGS_MEDIA_CHANGE_NOTIFICATION 0x0008
+#define EDD_FLAGS_LOCKABLE_MEDIA 0x0010
+#define EDD_FLAGS_NO_MEDIA_PRESENT 0x0020
+
+#define EDD_DEVICE_PATH_KEY 0xbedd
+
+#endif /* !_EDD_H_ */
diff --git a/stand/i386/gptboot/Makefile b/stand/i386/gptboot/Makefile
new file mode 100644
index 0000000..9e62007
--- /dev/null
+++ b/stand/i386/gptboot/Makefile
@@ -0,0 +1,75 @@
+# $FreeBSD$
+
+HAVE_GELI= yes
+
+.include <bsd.init.mk>
+
+.PATH: ${BOOTSRC}/i386/boot2 ${BOOTSRC}/i386/common ${SASRC}
+
+FILES= gptboot
+MAN= gptboot.8
+
+NM?= nm
+
+BOOT_COMCONSOLE_PORT?= 0x3f8
+BOOT_COMCONSOLE_SPEED?= 9600
+B2SIOFMT?= 0x3
+
+REL1= 0x700
+ORG1= 0x7c00
+ORG2= 0x0
+
+# Decide level of UFS support.
+GPTBOOT_UFS?= UFS1_AND_UFS2
+#GPTBOOT_UFS?= UFS2_ONLY
+#GPTBOOT_UFS?= UFS1_ONLY
+
+CFLAGS+=-DBOOTPROG=\"gptboot\" \
+ -O1 \
+ -DGPT \
+ -D${GPTBOOT_UFS} \
+ -DSIOPRT=${BOOT_COMCONSOLE_PORT} \
+ -DSIOFMT=${B2SIOFMT} \
+ -DSIOSPD=${BOOT_COMCONSOLE_SPEED} \
+ -I${LDRSRC} \
+ -I${BOOTSRC}/i386/common \
+ -I${BTXLIB} \
+ -I${BOOTSRC}/i386/boot2 \
+ -Wall -Waggregate-return -Wbad-function-cast -Wno-cast-align \
+ -Wmissing-declarations -Wmissing-prototypes -Wnested-externs \
+ -Wpointer-arith -Wshadow -Wstrict-prototypes -Wwrite-strings \
+ -Winline -Wno-pointer-sign
+
+CFLAGS.gcc+= --param max-inline-insns-single=100
+
+LD_FLAGS+=${LD_FLAGS_BIN}
+
+CLEANFILES+= gptboot
+
+gptboot: gptldr.bin gptboot.bin ${BTXKERN}
+ btxld -v -E ${ORG2} -f bin -b ${BTXKERN} -l gptldr.bin \
+ -o ${.TARGET} gptboot.bin
+
+CLEANFILES+= gptldr.bin gptldr.out gptldr.o
+
+gptldr.bin: gptldr.out
+ ${OBJCOPY} -S -O binary gptldr.out ${.TARGET}
+
+gptldr.out: gptldr.o
+ ${LD} ${LD_FLAGS} -e start -Ttext ${ORG1} -o ${.TARGET} gptldr.o
+
+CLEANFILES+= gptboot.bin gptboot.out gptboot.o sio.o crc32.o drv.o \
+ cons.o ${OPENCRYPTO_XTS}
+
+gptboot.bin: gptboot.out
+ ${OBJCOPY} -S -O binary gptboot.out ${.TARGET}
+
+gptboot.out: ${BTXCRT} gptboot.o sio.o crc32.o drv.o cons.o ${OPENCRYPTO_XTS}
+ ${LD} ${LD_FLAGS} -Ttext ${ORG2} -o ${.TARGET} ${.ALLSRC} ${LIBGELIBOOT} ${LIBSA32}
+
+gptboot.o: ${SASRC}/ufsread.c
+
+.include <bsd.prog.mk>
+
+# XXX: clang integrated-as doesn't grok .codeNN directives yet
+CFLAGS.gptldr.S= ${CLANG_NO_IAS}
diff --git a/stand/i386/gptboot/Makefile.depend b/stand/i386/gptboot/Makefile.depend
new file mode 100644
index 0000000..295be1a
--- /dev/null
+++ b/stand/i386/gptboot/Makefile.depend
@@ -0,0 +1,19 @@
+# $FreeBSD$
+# Autogenerated - do NOT edit!
+
+DIRDEPS = \
+ include \
+ include/xlocale \
+ lib/libmd \
+ lib/libstand \
+ sys/boot/geli \
+ sys/boot/i386/btx/btx \
+ sys/boot/i386/btx/lib \
+ sys/boot/libstand32 \
+
+
+.include <dirdeps.mk>
+
+.if ${DEP_RELDIR} == ${_DEP_RELDIR}
+# local dependencies - needed for -jN in clean tree
+.endif
diff --git a/stand/i386/gptboot/gptboot.8 b/stand/i386/gptboot/gptboot.8
new file mode 100644
index 0000000..a7afd81
--- /dev/null
+++ b/stand/i386/gptboot/gptboot.8
@@ -0,0 +1,245 @@
+.\" Copyright (c) 2013 Warren Block
+.\" 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 AUTHORS 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 AUTHORS 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$
+.\"
+.Dd February 5, 2014
+.Dt GPTBOOT 8
+.Os
+.Sh NAME
+.Nm gptboot
+.Nd GPT bootcode for UFS on BIOS-based computers
+.Sh DESCRIPTION
+.Nm
+is used on BIOS-based computers to boot from a UFS partition on a
+GPT-partitioned disk.
+.Nm
+is installed in a
+.Cm freebsd-boot
+partition with
+.Xr gpart 8 .
+.Sh IMPLEMENTATION NOTES
+The GPT standard allows a variable number of partitions, but
+.Nm
+only boots from tables with 128 partitions or less.
+.Sh PARTITION ATTRIBUTES
+.Nm
+checks and manages several attributes of GPT UFS partitions.
+.Bl -tag -width ".Cm bootfailed"
+.It Cm bootme
+Attempt to boot from this partition.
+If more than one partition has the
+.Cm bootme
+attribute set,
+.Nm
+will attempt to boot each one until successful.
+.It Cm bootonce
+Attempt to boot from this partition only one time.
+Setting this attribute with
+.Xr gpart 8
+automatically also sets the
+.Cm bootme
+attribute.
+Multiple partitions may have the
+.Cm bootonce
+and
+.Cm bootme
+attributes set.
+.It Cm bootfailed
+The
+.Cm bootfailed
+attribute marks partitions that had the
+.Cm bootonce
+attribute set, but failed to boot.
+This attribute is managed by the system.
+See
+.Sx "BOOTING"
+and
+.Sx "POST-BOOT ACTIONS"
+below for details.
+.El
+.Sh USAGE
+For normal usage, the user does not have to set or manage any of the
+partition attributes.
+.Nm
+will boot from the first UFS partition found.
+.Pp
+The
+.Cm bootonce
+attribute can be used for testing an upgraded operating system on
+an already-working computer.
+The existing system partition is left untouched, and the new version
+of the operating system to be tested is installed on another partition.
+The
+.Cm bootonce
+attribute is set on that new test partition.
+The next boot is attempted from the test partition.
+Success or failure will be shown in the system log files.
+After a successful boot of the test partition, a user script can check
+the logs and change the
+.Cm bootme
+attributes so the test partition becomes the new system partition.
+Because the
+.Cm bootonce
+attribute is cleared after an attempted boot, a failed boot will not
+leave the system attempting to boot from a partition that will never
+succeed.
+Instead, the system will boot from the older, known-working operating
+system that has not been modified.
+If the
+.Cm bootme
+attribute is set on any partitions, booting will be attempted from them
+first.
+If no partitions with
+.Cm bootme
+attributes are found, booting will be attempted from the first UFS
+partition found.
+.Sh BOOTING
+.Nm
+first reads the partition table.
+All
+.Cm freebsd-ufs
+partitions with only the
+.Cm bootonce
+attribute set, indicating a failed boot, are set to
+.Cm bootfailed .
+.Nm
+then scans through all of the
+.Cm freebsd-ufs
+partitions.
+Boot behavior depends on the combination of
+.Cm bootme
+and
+.Cm bootonce
+attributes set on those partitions.
+.Bl -tag -width ".Cm bootonce + .Cm bootme"
+.It Cm bootonce + Cm bootme
+Highest priority: booting is attempted from each of the
+.Cm freebsd-ufs
+partitions with both of these attributes.
+On each partition, the
+.Cm bootme
+attribute is removed and the boot attempted.
+.It Cm bootme
+Middle priority: booting is attempted from each of the
+.Cm freebsd-ufs
+partitions with the
+.Cm bootme
+attribute.
+.El
+.Pp
+If neither
+.Cm bootonce
+nor
+.Cm bootme
+attributes are found on any partitions, booting is attempted from the
+first
+.Cm freebsd-ufs
+partition on the disk.
+.Sh POST-BOOT ACTIONS
+The startup script
+.Pa /etc/rc.d/gptboot
+checks the attributes of
+.Cm freebsd-ufs
+partitions on all GPT disks.
+Partitions with the
+.Cm bootfailed
+attribute generate a
+.Dq boot from X failed
+system log message.
+Partitions with only the
+.Cm bootonce
+attribute, indicating a partition that successfully booted, generate a
+.Dq boot from X succeeded
+system log message.
+The
+.Cm bootfailed
+attributes are cleared from all the partitions.
+The
+.Cm bootonce
+attribute is cleared from the partition that successfully booted.
+There is normally only one of these.
+.Sh FILES
+.Bl -tag -width /boot/gptboot -compact
+.It Pa /boot/gptboot
+bootcode binary
+.It Pa /boot.config
+parameters for the boot blocks
+.Pq optional
+.El
+.Sh EXAMPLES
+.Nm
+is installed in a
+.Cm freebsd-boot
+partition, usually the first partition on the disk.
+A
+.Dq protective MBR
+.Po
+see
+.Xr gpart 8
+.Pc
+is typically installed in combination with
+.Nm .
+.Pp
+Install
+.Nm
+on the
+.Pa ada0
+drive:
+.Bd -literal -offset indent
+gpart bootcode -b /boot/pmbr -p /boot/gptboot -i 1 ada0
+.Ed
+.Pp
+.Nm
+can also be installed without the PMBR:
+.Bd -literal -offset indent
+gpart bootcode -p /boot/gptboot -i 1 ada0
+.Ed
+.Pp
+Set the
+.Cm bootme
+attribute for partition 2:
+.Bd -literal -offset indent
+gpart set -a bootme -i 2 ada0
+.Ed
+.Pp
+Set the
+.Cm bootonce
+attribute for partition 2, automatically also setting the
+.Cm bootme
+attribute:
+.Bd -literal -offset indent
+gpart set -a bootonce -i 2 ada0
+.Ed
+.Sh SEE ALSO
+.Xr boot.config 5 ,
+.Xr rc.conf 5 ,
+.Xr boot 8 ,
+.Xr gpart 8
+.Sh HISTORY
+.Nm
+appeared in FreeBSD 7.1.
+.Sh AUTHORS
+This manual page written by
+.An Warren Block Aq wblock@FreeBSD.org .
diff --git a/stand/i386/gptboot/gptboot.c b/stand/i386/gptboot/gptboot.c
new file mode 100644
index 0000000..dd06f0c
--- /dev/null
+++ b/stand/i386/gptboot/gptboot.c
@@ -0,0 +1,648 @@
+/*-
+ * 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/gpt.h>
+#include <sys/dirent.h>
+#include <sys/reboot.h>
+
+#include <machine/bootinfo.h>
+#include <machine/elf.h>
+#include <machine/pc/bios.h>
+#include <machine/psl.h>
+
+#include <stdarg.h>
+
+#include <a.out.h>
+
+#include <btxv86.h>
+
+#include "bootargs.h"
+#include "lib.h"
+#include "rbx.h"
+#include "drv.h"
+#include "util.h"
+#include "cons.h"
+#include "gpt.h"
+#include "paths.h"
+
+#define ARGS 0x900
+#define NOPT 14
+#define NDEV 3
+#define MEM_BASE 0x12
+#define MEM_EXT 0x15
+
+#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 uuid_t freebsd_ufs_uuid = GPT_ENT_TYPE_FREEBSD_UFS;
+static const char optstr[NOPT] = "DhaCcdgmnpqrsv"; /* Also 'P', 'S' */
+static const unsigned char flags[NOPT] = {
+ RBX_DUAL,
+ RBX_SERIAL,
+ RBX_ASKNAME,
+ RBX_CDROM,
+ RBX_CONFIG,
+ RBX_KDB,
+ RBX_GDB,
+ RBX_MUTE,
+ RBX_NOINTR,
+ RBX_PAUSE,
+ RBX_QUIET,
+ RBX_DFLTROOT,
+ RBX_SINGLE,
+ RBX_VERBOSE
+};
+uint32_t opts;
+
+static const char *const dev_nm[NDEV] = {"ad", "da", "fd"};
+static const unsigned char dev_maj[NDEV] = {30, 4, 2};
+
+static struct dsk dsk;
+static char kname[1024];
+static int comspeed = SIOSPD;
+static struct bootinfo bootinfo;
+#ifdef LOADER_GELI_SUPPORT
+static struct geli_boot_args geliargs;
+#endif
+
+static vm_offset_t high_heap_base;
+static uint32_t bios_basemem, bios_extmem, high_heap_size;
+
+static struct bios_smap smap;
+
+/*
+ * The minimum amount of memory to reserve in bios_extmem for the heap.
+ */
+#define HEAP_MIN (3 * 1024 * 1024)
+
+static char *heap_next;
+static char *heap_end;
+
+void exit(int);
+static void load(void);
+static int parse_cmds(char *, int *);
+static int dskread(void *, daddr_t, unsigned);
+void *malloc(size_t n);
+void free(void *ptr);
+#ifdef LOADER_GELI_SUPPORT
+static int vdev_read(void *vdev __unused, void *priv, off_t off, void *buf,
+ size_t bytes);
+#endif
+
+void *
+malloc(size_t n)
+{
+ char *p = heap_next;
+ if (p + n > heap_end) {
+ printf("malloc failure\n");
+ for (;;)
+ ;
+ /* NOTREACHED */
+ return (0);
+ }
+ heap_next += n;
+ return (p);
+}
+
+void
+free(void *ptr)
+{
+
+ return;
+}
+
+#include "ufsread.c"
+#include "gpt.c"
+#ifdef LOADER_GELI_SUPPORT
+#include "geliboot.c"
+static char gelipw[GELI_PW_MAXLEN];
+static struct keybuf *gelibuf;
+#endif
+
+static inline int
+xfsread(ufs_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 void
+bios_getmem(void)
+{
+ uint64_t size;
+
+ /* Parse system memory map */
+ v86.ebx = 0;
+ do {
+ v86.ctl = V86_FLAGS;
+ v86.addr = MEM_EXT; /* int 0x15 function 0xe820*/
+ v86.eax = 0xe820;
+ v86.ecx = sizeof(struct bios_smap);
+ v86.edx = SMAP_SIG;
+ v86.es = VTOPSEG(&smap);
+ v86.edi = VTOPOFF(&smap);
+ v86int();
+ if ((v86.efl & 1) || (v86.eax != SMAP_SIG))
+ break;
+ /* look for a low-memory segment that's large enough */
+ if ((smap.type == SMAP_TYPE_MEMORY) && (smap.base == 0) &&
+ (smap.length >= (512 * 1024)))
+ bios_basemem = smap.length;
+ /* look for the first segment in 'extended' memory */
+ if ((smap.type == SMAP_TYPE_MEMORY) && (smap.base == 0x100000)) {
+ bios_extmem = smap.length;
+ }
+
+ /*
+ * Look for the largest segment in 'extended' memory beyond
+ * 1MB but below 4GB.
+ */
+ if ((smap.type == SMAP_TYPE_MEMORY) && (smap.base > 0x100000) &&
+ (smap.base < 0x100000000ull)) {
+ size = smap.length;
+
+ /*
+ * If this segment crosses the 4GB boundary, truncate it.
+ */
+ if (smap.base + size > 0x100000000ull)
+ size = 0x100000000ull - smap.base;
+
+ if (size > high_heap_size) {
+ high_heap_size = size;
+ high_heap_base = smap.base;
+ }
+ }
+ } 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;
+ }
+
+ /*
+ * If we have extended memory and did not find a suitable heap
+ * region in the SMAP, use the last 3MB of 'extended' memory as a
+ * high heap candidate.
+ */
+ if (bios_extmem >= HEAP_MIN && high_heap_size < HEAP_MIN) {
+ high_heap_size = HEAP_MIN;
+ high_heap_base = bios_extmem + 0x100000 - HEAP_MIN;
+ }
+}
+
+static int
+gptinit(void)
+{
+
+ if (gptread(&freebsd_ufs_uuid, &dsk, dmadat->secbuf) == -1) {
+ printf("%s: unable to load GPT\n", BOOTPROG);
+ return (-1);
+ }
+ if (gptfind(&freebsd_ufs_uuid, &dsk, dsk.part) == -1) {
+ printf("%s: no UFS partition was found\n", BOOTPROG);
+ return (-1);
+ }
+#ifdef LOADER_GELI_SUPPORT
+ if (geli_taste(vdev_read, &dsk, (gpttable[curent].ent_lba_end -
+ gpttable[curent].ent_lba_start)) == 0) {
+ if (geli_havekey(&dsk) != 0 && geli_passphrase(&gelipw,
+ dsk.unit, 'p', curent + 1, &dsk) != 0) {
+ printf("%s: unable to decrypt GELI key\n", BOOTPROG);
+ return (-1);
+ }
+ }
+#endif
+
+ dsk_meta = 0;
+ return (0);
+}
+
+int
+main(void)
+{
+ char cmd[512], cmdtmp[512];
+ ssize_t sz;
+ int autoboot, dskupdated;
+ ufs_ino_t ino;
+
+ dmadat = (void *)(roundup2(__base + (int32_t)&_end, 0x10000) - __base);
+
+ bios_getmem();
+
+ if (high_heap_size > 0) {
+ heap_end = PTOV(high_heap_base + high_heap_size);
+ heap_next = PTOV(high_heap_base);
+ } else {
+ heap_next = (char *)dmadat + sizeof(*dmadat);
+ heap_end = (char *)PTOV(bios_basemem);
+ }
+
+ v86.ctl = V86_FLAGS;
+ v86.efl = PSL_RESERVED_DEFAULT | PSL_I;
+ dsk.drive = *(uint8_t *)PTOV(ARGS);
+ dsk.type = dsk.drive & DRV_HARD ? TYPE_AD : TYPE_FD;
+ dsk.unit = dsk.drive & DRV_MASK;
+ dsk.part = -1;
+ dsk.start = 0;
+ bootinfo.bi_version = BOOTINFO_VERSION;
+ bootinfo.bi_size = sizeof(bootinfo);
+ bootinfo.bi_basemem = bios_basemem / 1024;
+ bootinfo.bi_extmem = bios_extmem / 1024;
+ bootinfo.bi_memsizes_valid++;
+ bootinfo.bi_bios_dev = dsk.drive;
+
+#ifdef LOADER_GELI_SUPPORT
+ geli_init();
+#endif
+ /* Process configuration file */
+
+ if (gptinit() != 0)
+ return (-1);
+
+ autoboot = 1;
+ *cmd = '\0';
+
+ for (;;) {
+ *kname = '\0';
+ if ((ino = lookup(PATH_CONFIG)) ||
+ (ino = lookup(PATH_DOTCONFIG))) {
+ sz = fsread(ino, cmd, sizeof(cmd) - 1);
+ cmd[(sz < 0) ? 0 : sz] = '\0';
+ }
+ if (*cmd != '\0') {
+ memcpy(cmdtmp, cmd, sizeof(cmdtmp));
+ if (parse_cmds(cmdtmp, &dskupdated))
+ break;
+ if (dskupdated && gptinit() != 0)
+ break;
+ if (!OPT_CHECK(RBX_QUIET))
+ printf("%s: %s", PATH_CONFIG, cmd);
+ *cmd = '\0';
+ }
+
+ if (autoboot && keyhit(3)) {
+ if (*kname == '\0')
+ memcpy(kname, PATH_LOADER, sizeof(PATH_LOADER));
+ break;
+ }
+ autoboot = 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 (*kname != '\0')
+ load();
+ memcpy(kname, PATH_LOADER, sizeof(PATH_LOADER));
+ load();
+ memcpy(kname, PATH_KERNEL, sizeof(PATH_KERNEL));
+ load();
+ gptbootfailed(&dsk);
+ if (gptfind(&freebsd_ufs_uuid, &dsk, -1) == -1)
+ break;
+ dsk_meta = 0;
+ }
+
+ /* Present the user with the boot2 prompt. */
+
+ for (;;) {
+ if (!OPT_CHECK(RBX_QUIET)) {
+ printf("\nFreeBSD/x86 boot\n"
+ "Default: %u:%s(%up%u)%s\n"
+ "boot: ",
+ dsk.drive & DRV_MASK, dev_nm[dsk.type], dsk.unit,
+ dsk.part, kname);
+ }
+ if (ioctrl & IO_SERIAL)
+ sio_flush();
+ *cmd = '\0';
+ if (keyhit(0))
+ getstr(cmd, sizeof(cmd));
+ else if (!OPT_CHECK(RBX_QUIET))
+ putchar('\n');
+ if (parse_cmds(cmd, &dskupdated)) {
+ putchar('\a');
+ continue;
+ }
+ if (dskupdated && gptinit() != 0)
+ continue;
+ load();
+ }
+ /* NOTREACHED */
+}
+
+/* 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;
+ static Elf32_Phdr ep[2];
+ static Elf32_Shdr es[2];
+ caddr_t p;
+ ufs_ino_t ino;
+ uint32_t addr, x;
+ int fmt, i, j;
+
+ if (!(ino = lookup(kname))) {
+ if (!ls) {
+ printf("%s: No %s on %u:%s(%up%u)\n", BOOTPROG,
+ kname, dsk.drive & DRV_MASK, dev_nm[dsk.type], dsk.unit,
+ dsk.part);
+ }
+ 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;
+#ifdef LOADER_GELI_SUPPORT
+ geliargs.size = sizeof(geliargs);
+ explicit_bzero(gelipw, sizeof(gelipw));
+ gelibuf = malloc(sizeof(struct keybuf) + (GELI_MAX_KEYS * sizeof(struct keybuf_ent)));
+ geli_fill_keybuf(gelibuf);
+ geliargs.notapw = '\0';
+ geliargs.keybuf_sentinel = KEYBUF_SENTINEL;
+ geliargs.keybuf = gelibuf;
+#endif
+ __exec((caddr_t)addr, RB_BOOTINFO | (opts & RBX_MASK),
+ MAKEBOOTDEV(dev_maj[dsk.type], dsk.part + 1, dsk.unit, 0xff),
+ KARGS_FLAGS_EXTARG, 0, 0, VTOP(&bootinfo)
+#ifdef LOADER_GELI_SUPPORT
+ , geliargs
+#endif
+ );
+}
+
+static int
+parse_cmds(char *cmdstr, int *dskupdated)
+{
+ char *arg = cmdstr;
+ char *ep, *p, *q;
+ const char *cp;
+ unsigned int drv;
+ int c, i, j;
+
+ *dskupdated = 0;
+ while ((c = *arg++)) {
+ if (c == ' ' || c == '\t' || c == '\n')
+ continue;
+ for (p = arg; *p && *p != '\n' && *p != ' ' && *p != '\t'; p++);
+ ep = p;
+ if (*p)
+ *p++ = 0;
+ if (c == '-') {
+ while ((c = *arg++)) {
+ if (c == 'P') {
+ if (*(uint8_t *)PTOV(0x496) & 0x10) {
+ cp = "yes";
+ } else {
+ opts |= OPT_SET(RBX_DUAL) | OPT_SET(RBX_SERIAL);
+ cp = "no";
+ }
+ printf("Keyboard: %s\n", cp);
+ continue;
+ } else if (c == 'S') {
+ j = 0;
+ while ((unsigned int)(i = *arg++ - '0') <= 9)
+ j = j * 10 + i;
+ if (j > 0 && i == -'0') {
+ comspeed = j;
+ break;
+ }
+ /* Fall through to error below ('S' not in optstr[]). */
+ }
+ for (i = 0; c != optstr[i]; i++)
+ if (i == NOPT - 1)
+ return -1;
+ opts ^= OPT_SET(flags[i]);
+ }
+ ioctrl = OPT_CHECK(RBX_DUAL) ? (IO_SERIAL|IO_KEYBOARD) :
+ OPT_CHECK(RBX_SERIAL) ? IO_SERIAL : IO_KEYBOARD;
+ if (ioctrl & IO_SERIAL) {
+ if (sio_init(115200 / comspeed) != 0)
+ ioctrl &= ~IO_SERIAL;
+ }
+ } 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] != 'p' || dsk.unit > 9)
+ return -1;
+ arg += 2;
+ dsk.part = *arg - '0';
+ if (dsk.part < 1 || dsk.part > 9)
+ return -1;
+ arg++;
+ if (arg[0] != ')')
+ return -1;
+ arg++;
+ if (drv == -1)
+ drv = dsk.unit;
+ dsk.drive = (dsk.type <= TYPE_MAXHARD
+ ? DRV_HARD : 0) + drv;
+ *dskupdated = 1;
+ }
+ if ((i = ep - arg)) {
+ if ((size_t)i >= sizeof(kname))
+ return -1;
+ memcpy(kname, arg, i + 1);
+ }
+ }
+ arg = p;
+ }
+ return 0;
+}
+
+static int
+dskread(void *buf, daddr_t lba, unsigned nblk)
+{
+ int err;
+
+ err = drvread(&dsk, buf, lba + dsk.start, nblk);
+
+#ifdef LOADER_GELI_SUPPORT
+ if (err == 0 && is_geli(&dsk) == 0) {
+ /* Decrypt */
+ if (geli_read(&dsk, lba * DEV_BSIZE, buf, nblk * DEV_BSIZE))
+ return (err);
+ }
+#endif
+
+ return (err);
+}
+
+#ifdef LOADER_GELI_SUPPORT
+/*
+ * Read function compartible with the ZFS callback, required to keep the GELI
+ * Implementation the same for both UFS and ZFS
+ */
+static int
+vdev_read(void *vdev __unused, void *priv, off_t off, void *buf, size_t bytes)
+{
+ char *p;
+ daddr_t lba;
+ unsigned int nb;
+ struct dsk *dskp = (struct dsk *) priv;
+
+ if ((off & (DEV_BSIZE - 1)) || (bytes & (DEV_BSIZE - 1)))
+ return (-1);
+
+ p = buf;
+ lba = off / DEV_BSIZE;
+ lba += dskp->start;
+
+ while (bytes > 0) {
+ nb = bytes / DEV_BSIZE;
+ if (nb > VBLKSIZE / DEV_BSIZE)
+ nb = VBLKSIZE / DEV_BSIZE;
+ if (drvread(dskp, dmadat->blkbuf, lba, nb))
+ return (-1);
+ memcpy(p, dmadat->blkbuf, nb * DEV_BSIZE);
+ p += nb * DEV_BSIZE;
+ lba += nb;
+ bytes -= nb * DEV_BSIZE;
+ }
+
+ return (0);
+}
+#endif /* LOADER_GELI_SUPPORT */
diff --git a/stand/i386/gptboot/gptldr.S b/stand/i386/gptboot/gptldr.S
new file mode 100644
index 0000000..088122f
--- /dev/null
+++ b/stand/i386/gptboot/gptldr.S
@@ -0,0 +1,142 @@
+/*-
+ * Copyright (c) 2007 Yahoo!, Inc.
+ * All rights reserved.
+ * Written by: John Baldwin <jhb@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.
+ * 3. Neither the name of the author nor the names of any co-contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ *
+ * Partly from: src/sys/boot/i386/boot2/boot1.S 1.31
+ */
+
+/* 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
+
+/* Misc. Constants */
+ .set SIZ_PAG,0x1000 # Page size
+ .set SIZ_SEC,0x200 # Sector size
+ .set COPY_BLKS,0x8 # Number of blocks
+ # to copy for boot2
+ .set COPY_BLK_SZ,0x8000 # Copy in 32k blocks; must be
+ # a multiple of 16 bytes
+
+ .globl start
+ .code16
+
+/*
+ * Copy BTX and boot2 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.
+ */
+start: xor %cx,%cx # Zero
+ mov %cx,%es # Address
+ mov %cx,%ds # data
+ mov %cx,%ss # Set up
+ mov $start,%sp # stack
+
+/*
+ * BTX is right after us at 'end'. We read the length of BTX out of
+ * its header to find boot2. We need to copy boot2 to MEM_USR and BTX
+ * to MEM_BTX. Since those might overlap, we have to copy boot2
+ * backwards first and then copy BTX. We aren't sure exactly how long
+ * boot2 is, but it's currently under 128kB so we'll copy 4 blocks of 32kB
+ * each; this can be adjusted via COPY_BLK and COPY_BLK_SZ above.
+ */
+ mov $end,%bx # BTX
+ mov 0xa(%bx),%si # Get BTX length and set
+ add %bx,%si # %si to start of boot2
+ dec %si # Set %ds:%si to point at the
+ mov %si,%ax # last byte we want to copy
+ shr $4,%ax # from boot2, with %si made as
+ add $(COPY_BLKS*COPY_BLK_SZ/16),%ax # small as possible.
+ and $0xf,%si #
+ mov %ax,%ds #
+ mov $MEM_USR/16,%ax # Set %es:(-1) to point at
+ add $(COPY_BLKS*COPY_BLK_SZ/16),%ax # the last byte we
+ mov %ax,%es # want to copy boot2 into.
+ mov $COPY_BLKS,%bx # Copy COPY_BLKS 32k blocks
+copyloop:
+ add $COPY_BLK_SZ,%si # Adjust %ds:%si to point at
+ mov %ds,%ax # the end of the next 32k to
+ sub $COPY_BLK_SZ/16,%ax # copy from boot2
+ mov %ax,%ds
+ mov $COPY_BLK_SZ-1,%di # Adjust %es:%di to point at
+ mov %es,%ax # the end of the next 32k into
+ sub $COPY_BLK_SZ/16,%ax # which we want boot2 copied
+ mov %ax,%es
+ mov $COPY_BLK_SZ,%cx # Copy 32k
+ std
+ rep movsb
+ dec %bx
+ jnz copyloop
+ mov %cx,%ds # Reset %ds and %es
+ mov %cx,%es
+ mov $end,%bx # BTX
+ mov 0xa(%bx),%cx # Get BTX length and set
+ mov %bx,%si # %si to end of BTX
+ mov $MEM_BTX,%di # %di -> end of BTX at
+ add %cx,%si # MEM_BTX
+ add %cx,%di
+ dec %si
+ dec %di
+ rep movsb # Move BTX
+ cld # String ops inc
+/*
+ * Enable A20 so we can access memory above 1 meg.
+ * Use the zero-valued %cx as a timeout for embedded hardware which do not
+ * have a keyboard controller.
+ */
+seta20: cli # Disable interrupts
+seta20.1: dec %cx # Timeout?
+ jz seta20.3 # Yes
+ 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
+seta20.3: sti # Enable interrupts
+
+/*
+ * Save drive number from BIOS so boot2 can see it and start BTX.
+ */
+ movb %dl,MEM_ARG
+ jmp MEM_JMP # Start BTX
+end:
diff --git a/stand/i386/gptzfsboot/Makefile b/stand/i386/gptzfsboot/Makefile
new file mode 100644
index 0000000..97f1954
--- /dev/null
+++ b/stand/i386/gptzfsboot/Makefile
@@ -0,0 +1,87 @@
+# $FreeBSD$
+
+HAVE_GPT= yes
+
+.include <bsd.init.mk>
+
+.PATH: ${BOOTSRC}/i386/boot2 ${BOOTSRC}/i386/gptboot \
+ ${BOOTSRC}/i386/zfsboot ${BOOTSRC}/i386/common \
+ ${SASRC}
+
+FILES= gptzfsboot
+MAN= gptzfsboot.8
+
+NM?= nm
+
+BOOT_COMCONSOLE_PORT?= 0x3f8
+BOOT_COMCONSOLE_SPEED?= 9600
+B2SIOFMT?= 0x3
+
+REL1= 0x700
+ORG1= 0x7c00
+ORG2= 0x0
+
+CFLAGS+=-DBOOTPROG=\"gptzfsboot\" \
+ -O1 \
+ -DGPT -DZFS -DBOOT2 \
+ -DSIOPRT=${BOOT_COMCONSOLE_PORT} \
+ -DSIOFMT=${B2SIOFMT} \
+ -DSIOSPD=${BOOT_COMCONSOLE_SPEED} \
+ -I${LDRSRC} \
+ -I${BOOTSRC}/i386/common \
+ -I${ZFSSRC} \
+ -I${SYSDIR}/cddl/boot/zfs \
+ -I${BOOTSRC}/i386/btx/lib \
+ -I${BOOTSRC}/i386/boot2 \
+ -Wall -Waggregate-return -Wbad-function-cast \
+ -Wmissing-declarations -Wmissing-prototypes -Wnested-externs \
+ -Wpointer-arith -Wshadow -Wstrict-prototypes -Wwrite-strings \
+ -Winline -Wno-pointer-sign
+
+NO_WCAST_ALIGN=
+
+.if ${COMPILER_TYPE} == "clang" || \
+ (${COMPILER_TYPE} == "gcc" && ${COMPILER_VERSION} > 40201)
+CFLAGS+= -Wno-tentative-definition-incomplete-type
+.endif
+
+.if ${MACHINE} == "amd64"
+LIBZFSBOOT=${BOOTOBJ}/zfs32/libzfsboot.a
+.else
+LIBZFSBOOT=${BOOTOBJ}/zfs/libzfsboot.a
+.endif
+
+CFLAGS.gcc+= --param max-inline-insns-single=100
+
+LD_FLAGS+=${LD_FLAGS_BIN}
+
+CLEANFILES+= gptzfsboot
+
+gptzfsboot: gptldr.bin gptzfsboot.bin ${BTXKERN}
+ btxld -v -E ${ORG2} -f bin -b ${BTXKERN} -l gptldr.bin \
+ -o ${.TARGET} gptzfsboot.bin
+
+CLEANFILES+= gptldr.bin gptldr.out gptldr.o
+
+gptldr.bin: gptldr.out
+ ${OBJCOPY} -S -O binary gptldr.out ${.TARGET}
+
+gptldr.out: gptldr.o
+ ${LD} ${LD_FLAGS} -e start -Ttext ${ORG1} -o ${.TARGET} gptldr.o
+
+CLEANFILES+= gptzfsboot.bin gptzfsboot.out zfsboot.o sio.o cons.o \
+ drv.o gpt.o util.o ${OPENCRYPTO_XTS}
+
+gptzfsboot.bin: gptzfsboot.out
+ ${OBJCOPY} -S -O binary gptzfsboot.out ${.TARGET}
+
+gptzfsboot.out: ${BTXCRT} zfsboot.o sio.o gpt.o drv.o cons.o util.o \
+ ${OPENCRYPTO_XTS}
+ ${LD} ${LD_FLAGS} -Ttext ${ORG2} -o ${.TARGET} ${.ALLSRC} ${LIBGELIBOOT} ${LIBZFSBOOT} ${LIBSA32}
+
+zfsboot.o: ${ZFSSRC}/zfsimpl.c
+
+.include <bsd.prog.mk>
+
+# XXX: clang integrated-as doesn't grok .codeNN directives yet
+CFLAGS.gptldr.S= ${CLANG_NO_IAS}
diff --git a/stand/i386/gptzfsboot/Makefile.depend b/stand/i386/gptzfsboot/Makefile.depend
new file mode 100644
index 0000000..295be1a
--- /dev/null
+++ b/stand/i386/gptzfsboot/Makefile.depend
@@ -0,0 +1,19 @@
+# $FreeBSD$
+# Autogenerated - do NOT edit!
+
+DIRDEPS = \
+ include \
+ include/xlocale \
+ lib/libmd \
+ lib/libstand \
+ sys/boot/geli \
+ sys/boot/i386/btx/btx \
+ sys/boot/i386/btx/lib \
+ sys/boot/libstand32 \
+
+
+.include <dirdeps.mk>
+
+.if ${DEP_RELDIR} == ${_DEP_RELDIR}
+# local dependencies - needed for -jN in clean tree
+.endif
diff --git a/stand/i386/gptzfsboot/gptzfsboot.8 b/stand/i386/gptzfsboot/gptzfsboot.8
new file mode 100644
index 0000000..66feb6d
--- /dev/null
+++ b/stand/i386/gptzfsboot/gptzfsboot.8
@@ -0,0 +1,193 @@
+.\" Copyright (c) 2014 Andriy Gapon <avg@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 AUTHORS 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 AUTHORS 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$
+.\"
+.Dd September 15, 2014
+.Dt GPTZFSBOOT 8
+.Os
+.Sh NAME
+.Nm gptzfsboot
+.Nd GPT bootcode for ZFS on BIOS-based computers
+.Sh DESCRIPTION
+.Nm
+is used on BIOS-based computers to boot from a filesystem in
+a ZFS pool.
+.Nm
+is installed in a
+.Cm freebsd-boot
+partition of a GPT-partitioned disk with
+.Xr gpart 8 .
+.Sh IMPLEMENTATION NOTES
+The GPT standard allows a variable number of partitions, but
+.Nm
+only boots from tables with 128 partitions or less.
+.Sh BOOTING
+.Nm
+tries to find all ZFS pools that are composed of BIOS-visible
+hard disks or partitions on them.
+.Nm
+looks for ZFS device labels on all visible disks and in discovered
+supported partitions for all supported partition scheme types.
+The search starts with the disk from which
+.Nm
+itself was loaded.
+Other disks are probed in BIOS defined order.
+After a disk is probed and
+.Nm
+determines that the whole disk is not a ZFS pool member, the
+individual partitions are probed in their partition table order.
+Currently GPT and MBR partition schemes are supported.
+With the GPT scheme, only partitions of type
+.Cm freebsd-zfs
+are probed.
+The first pool seen during probing is used as a default boot pool.
+.Pp
+The filesystem specified by the
+.Cm bootfs
+property of the pool is used as a default boot filesystem.
+If the
+.Cm bootfs
+property is not set, then the root filesystem of the pool is used as
+the default.
+.Xr zfsloader 8
+is loaded from the boot filesystem.
+If
+.Pa /boot.config
+or
+.Pa /boot/config
+is present in the boot filesystem, boot options are read from it
+in the same way as
+.Xr boot 8 .
+.Pp
+The ZFS GUIDs of the first successfully probed device and the first
+detected pool are made available to
+.Xr zfsloader 8
+in the
+.Cm vfs.zfs.boot.primary_vdev
+and
+.Cm vfs.zfs.boot.primary_pool
+variables.
+.Sh USAGE
+Normally
+.Nm
+will boot in fully automatic mode.
+However, like
+.Xr boot 8 ,
+it is possible to interrupt the automatic boot process and interact with
+.Nm
+through a prompt.
+.Nm
+accepts all the options that
+.Xr boot 8
+supports.
+.Pp
+The filesystem specification and the path to
+.Xr zfsloader 8
+are different from
+.Xr boot 8 .
+The format is
+.Pp
+.Sm off
+.Oo zfs:pool/filesystem: Oc Oo /path/to/loader Oc
+.Sm on
+.Pp
+Both the filesystem and the path can be specified.
+If only a path is specified, then the default filesystem is used.
+If only a pool and filesystem are specified, then
+.Pa /boot/zfsloader
+is used as a path.
+.Pp
+Additionally, the
+.Ic status
+command can be used to query information about discovered pools.
+The output format is similar to that of
+.Cm zpool status
+.Pq see Xr zpool 8 .
+.Pp
+The configured or automatically determined ZFS boot filesystem is
+stored in the
+.Xr zfsloader 8
+.Cm loaddev
+variable, and also set as the initial value of the
+.Cm currdev
+variable.
+.Sh FILES
+.Bl -tag -width /boot/gptzfsboot -compact
+.It Pa /boot/gptzfsboot
+boot code binary
+.It Pa /boot.config
+parameters for the boot block
+.Pq optional
+.It Pa /boot/config
+alternative parameters for the boot block
+.Pq optional
+.El
+.Sh EXAMPLES
+.Nm
+is typically installed in combination with a
+.Dq protective MBR
+.Po
+see
+.Xr gpart 8
+.Pc .
+To install
+.Nm
+on the
+.Pa ada0
+drive:
+.Bd -literal -offset indent
+gpart bootcode -b /boot/pmbr -p /boot/gptzfsboot -i 1 ada0
+.Ed
+.Pp
+.Nm
+can also be installed without the PMBR:
+.Bd -literal -offset indent
+gpart bootcode -p /boot/gptzfsboot -i 1 ada0
+.Ed
+.Sh SEE ALSO
+.Xr boot.config 5 ,
+.Xr boot 8 ,
+.Xr gpart 8 ,
+.Xr loader 8 ,
+.Xr zfsloader 8 ,
+.Xr zpool 8
+.Sh HISTORY
+.Nm
+appeared in FreeBSD 7.3.
+.Sh AUTHORS
+This manual page was written by
+.An Andriy Gapon Aq avg@FreeBSD.org .
+.Sh BUGS
+.Nm
+looks for ZFS meta-data only in MBR partitions
+.Pq known on FreeBSD as slices .
+It does not look into BSD
+.Xr disklabel 8
+partitions that are traditionally called partitions.
+If a disklabel partition happens to be placed so that ZFS meta-data can be
+found at the fixed offsets relative to a slice, then
+.Nm
+will recognize the partition as a part of a ZFS pool,
+but this is not guaranteed to happen.
diff --git a/stand/i386/kgzldr/Makefile b/stand/i386/kgzldr/Makefile
new file mode 100644
index 0000000..5a03b83
--- /dev/null
+++ b/stand/i386/kgzldr/Makefile
@@ -0,0 +1,21 @@
+# $FreeBSD$
+
+.include <bsd.init.mk>
+
+PROG= kgzldr.o
+STRIP=
+BINMODE=${LIBMODE}
+BINDIR= ${LIBDIR}
+MAN=
+
+SRCS= start.s boot.c inflate.c lib.c crt.s sio.s
+CFLAGS= -Os
+CFLAGS+=-DKZIP
+NO_SHARED=
+LDFLAGS+=-Wl,-r
+.PATH: ${SYSDIR}/kern
+
+BOOT_COMCONSOLE_PORT?= 0x3f8
+AFLAGS+=--defsym SIO_PRT=${BOOT_COMCONSOLE_PORT}
+
+.include <bsd.prog.mk>
diff --git a/stand/i386/kgzldr/Makefile.depend b/stand/i386/kgzldr/Makefile.depend
new file mode 100644
index 0000000..79506ce
--- /dev/null
+++ b/stand/i386/kgzldr/Makefile.depend
@@ -0,0 +1,12 @@
+# $FreeBSD$
+# Autogenerated - do NOT edit!
+
+DIRDEPS = \
+ include \
+
+
+.include <dirdeps.mk>
+
+.if ${DEP_RELDIR} == ${_DEP_RELDIR}
+# local dependencies - needed for -jN in clean tree
+.endif
diff --git a/stand/i386/kgzldr/boot.c b/stand/i386/kgzldr/boot.c
new file mode 100644
index 0000000..45ed2ee
--- /dev/null
+++ b/stand/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/stand/i386/kgzldr/crt.s b/stand/i386/kgzldr/crt.s
new file mode 100644
index 0000000..cfb479f
--- /dev/null
+++ b/stand/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/stand/i386/kgzldr/kgzldr.h b/stand/i386/kgzldr/kgzldr.h
new file mode 100644
index 0000000..5cd5b44
--- /dev/null
+++ b/stand/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/stand/i386/kgzldr/lib.c b/stand/i386/kgzldr/lib.c
new file mode 100644
index 0000000..538875b
--- /dev/null
+++ b/stand/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/stand/i386/kgzldr/sio.s b/stand/i386/kgzldr/sio.s
new file mode 100644
index 0000000..ff174eb
--- /dev/null
+++ b/stand/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/stand/i386/kgzldr/start.s b/stand/i386/kgzldr/start.s
new file mode 100644
index 0000000..550fa52
--- /dev/null
+++ b/stand/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/stand/i386/libfirewire/Makefile b/stand/i386/libfirewire/Makefile
new file mode 100644
index 0000000..d3f88c4
--- /dev/null
+++ b/stand/i386/libfirewire/Makefile
@@ -0,0 +1,20 @@
+# $FreeBSD$
+
+.include <bsd.init.mk>
+
+LIB= firewire
+INTERNALLIB=
+
+.PATH: ${SYSDIR}/dev/dcons ${SYSDIR}/dev/firewire
+SRCS+= firewire.c fwohci.c dconsole.c
+SRCS+= dcons.c fwcrom.c
+
+CFLAGS+= -D_BOOT
+
+CFLAGS+= -I${LDRSRC}
+CFLAGS+= -I${BTXLIB}
+CFLAGS+= -I${BOOTSRC}/i386/libi386
+
+CFLAGS+= -Wformat -Wall
+
+.include <bsd.lib.mk>
diff --git a/stand/i386/libfirewire/Makefile.depend b/stand/i386/libfirewire/Makefile.depend
new file mode 100644
index 0000000..18be76b
--- /dev/null
+++ b/stand/i386/libfirewire/Makefile.depend
@@ -0,0 +1,13 @@
+# $FreeBSD$
+# Autogenerated - do NOT edit!
+
+DIRDEPS = \
+ include \
+ include/xlocale \
+
+
+.include <dirdeps.mk>
+
+.if ${DEP_RELDIR} == ${_DEP_RELDIR}
+# local dependencies - needed for -jN in clean tree
+.endif
diff --git a/stand/i386/libfirewire/dconsole.c b/stand/i386/libfirewire/dconsole.c
new file mode 100644
index 0000000..1528faf
--- /dev/null
+++ b/stand/i386/libfirewire/dconsole.c
@@ -0,0 +1,127 @@
+/*-
+ * Copyright (c) 2004 Hidetoshi Shimokawa
+ *
+ * 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 <sys/param.h>
+#include <btxv86.h>
+#include <dev/dcons/dcons.h>
+
+void fw_enable(void);
+void fw_poll(void);
+
+static void dconsole_probe(struct console *cp);
+static int dconsole_init(int arg);
+static void dconsole_putchar(int c);
+static int dconsole_getchar(void);
+static int dconsole_ischar(void);
+
+static int dcons_started = 0;
+
+#define DCONS_BUF_SIZE (64*1024)
+static struct dcons_softc sc[DCONS_NPORT];
+uint32_t dcons_paddr;
+
+/* The buffer must be allocated in BSS becase:
+ * - The dcons driver in the kernel is initialized before VM/pmap is
+ * initialized, so that the buffer must be allocate in the region
+ * that is mapped at the very early boot state.
+ * - We expect identiy map only for regions before KERNLOAD
+ * (i386:4MB amd64:1MB).
+ * - It seems that heap in conventional memory(640KB) is not sufficient
+ * and we move it to high address as LOADER_SUPPORT_BZIP2.
+ * - BSS is placed in conventional memory.
+ */
+static char dcons_buffer[DCONS_BUF_SIZE + PAGE_SIZE];
+
+struct console dconsole = {
+ "dcons",
+ "dumb console port",
+ 0,
+ dconsole_probe,
+ dconsole_init,
+ dconsole_putchar,
+ dconsole_getchar,
+ dconsole_ischar
+};
+
+#define DCONSOLE_AS_MULTI_CONSOLE 1
+
+static void
+dconsole_probe(struct console *cp)
+{
+ /* XXX check the BIOS equipment list? */
+ cp->c_flags |= (C_PRESENTIN | C_PRESENTOUT);
+#if DCONSOLE_AS_MULTI_CONSOLE
+ dconsole_init(0);
+ cp->c_flags |= (C_ACTIVEIN | C_ACTIVEOUT);
+#endif
+}
+
+static int
+dconsole_init(int arg)
+{
+ char buf[16], *dbuf;
+ int size;
+
+ if (dcons_started && arg == 0)
+ return 0;
+ dcons_started = 1;
+
+ size = DCONS_BUF_SIZE;
+ dbuf = (char *)round_page((vm_offset_t)&dcons_buffer[0]);
+ dcons_paddr = VTOP(dbuf);
+ sprintf(buf, "0x%08x", dcons_paddr);
+ setenv("dcons.addr", buf, 1);
+
+ dcons_init((struct dcons_buf *)dbuf, size, sc);
+ sprintf(buf, "%d", size);
+ setenv("dcons.size", buf, 1);
+ fw_enable();
+ return(0);
+}
+
+static void
+dconsole_putchar(int c)
+{
+ dcons_putc(&sc[0], c);
+}
+
+static int
+dconsole_getchar(void)
+{
+ fw_poll();
+ return (dcons_checkc(&sc[0]));
+}
+
+static int
+dconsole_ischar(void)
+{
+ fw_poll();
+ return (dcons_ischar(&sc[0]));
+}
diff --git a/stand/i386/libfirewire/firewire.c b/stand/i386/libfirewire/firewire.c
new file mode 100644
index 0000000..4840325
--- /dev/null
+++ b/stand/i386/libfirewire/firewire.c
@@ -0,0 +1,484 @@
+/*-
+ * Copyright (c) 2004 Hidetoshi Shimokawa <simokawa@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$");
+
+/*
+ * FireWire disk device handling.
+ *
+ */
+
+#include <stand.h>
+
+#include <machine/bootinfo.h>
+
+#include <stdarg.h>
+
+#include <bootstrap.h>
+#include <btxv86.h>
+#include <libi386.h>
+#include "fwohci.h"
+#include <dev/dcons/dcons.h>
+
+/* XXX */
+#define BIT4x2(x,y) uint8_t y:4, x:4
+#define BIT16x2(x,y) uint32_t y:16, x:16
+#define _KERNEL
+#include <dev/firewire/iec13213.h>
+
+extern uint32_t dcons_paddr;
+extern struct console dconsole;
+
+struct crom_src_buf {
+ struct crom_src src;
+ struct crom_chunk root;
+ struct crom_chunk vendor;
+ struct crom_chunk hw;
+ /* for dcons */
+ struct crom_chunk unit;
+ struct crom_chunk spec;
+ struct crom_chunk ver;
+};
+
+static int fw_init(void);
+static int fw_strategy(void *devdata, int flag, daddr_t dblk,
+ size_t size, char *buf, size_t *rsize);
+static int fw_open(struct open_file *f, ...);
+static int fw_close(struct open_file *f);
+static int fw_print(int verbose);
+static void fw_cleanup(void);
+
+void fw_enable(void);
+
+struct devsw fwohci = {
+ "FW1394", /* 7 chars at most */
+ DEVT_NET,
+ fw_init,
+ fw_strategy,
+ fw_open,
+ fw_close,
+ noioctl,
+ fw_print,
+ fw_cleanup
+};
+
+static struct fwohci_softc fwinfo[MAX_OHCI];
+static int fw_initialized = 0;
+
+static void
+fw_probe(int index, struct fwohci_softc *sc)
+{
+ int err;
+
+ sc->state = FWOHCI_STATE_INIT;
+ err = biospci_find_devclass(
+ 0x0c0010 /* Serial:FireWire:OHCI */,
+ index /* index */,
+ &sc->locator);
+
+ if (err != 0) {
+ sc->state = FWOHCI_STATE_DEAD;
+ return;
+ }
+
+ biospci_write_config(sc->locator,
+ 0x4 /* command */,
+ 0x6 /* enable bus master and memory mapped I/O */,
+ 1 /* word */);
+
+ biospci_read_config(sc->locator, 0x00 /*devid*/, 2 /*dword*/,
+ &sc->devid);
+ biospci_read_config(sc->locator, 0x10 /*base_addr*/, 2 /*dword*/,
+ &sc->base_addr);
+
+ sc->handle = (uint32_t)PTOV(sc->base_addr);
+ sc->bus_id = OREAD(sc, OHCI_BUS_ID);
+
+ return;
+}
+
+static int
+fw_init(void)
+{
+ int i, avail;
+ struct fwohci_softc *sc;
+
+ if (fw_initialized)
+ return (0);
+
+ avail = 0;
+ for (i = 0; i < MAX_OHCI; i ++) {
+ sc = &fwinfo[i];
+ fw_probe(i, sc);
+ if (sc->state == FWOHCI_STATE_DEAD)
+ break;
+ avail ++;
+ break;
+ }
+ fw_initialized = 1;
+
+ return (0);
+}
+
+
+/*
+ * Print information about OHCI chips
+ */
+static int
+fw_print(int verbose)
+{
+ char line[80];
+ int i, ret = 0;
+ struct fwohci_softc *sc;
+
+ printf("%s devices:", fwohci.dv_name);
+ if ((ret = pager_output("\n")) != 0)
+ return (ret);
+
+ for (i = 0; i < MAX_OHCI; i ++) {
+ sc = &fwinfo[i];
+ if (sc->state == FWOHCI_STATE_DEAD)
+ break;
+ snprintf(line, sizeof(line), "%d: locator=0x%04x devid=0x%08x"
+ " base_addr=0x%08x handle=0x%08x bus_id=0x%08x\n",
+ i, sc->locator, sc->devid,
+ sc->base_addr, sc->handle, sc->bus_id);
+ ret = pager_output(line);
+ if (ret != 0)
+ break;
+ }
+ return (ret);
+}
+
+static int
+fw_open(struct open_file *f, ...)
+{
+#if 0
+ 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);
+#endif
+
+ return (ENXIO);
+}
+
+static int
+fw_close(struct open_file *f)
+{
+ return (0);
+}
+
+static void
+fw_cleanup()
+{
+ struct dcons_buf *db;
+
+ /* invalidate dcons buffer */
+ if (dcons_paddr) {
+ db = (struct dcons_buf *)PTOV(dcons_paddr);
+ db->magic = 0;
+ }
+}
+
+static int
+fw_strategy(void *devdata, int rw, daddr_t dblk, size_t size,
+ char *buf, size_t *rsize)
+{
+ return (EIO);
+}
+
+static void
+fw_init_crom(struct fwohci_softc *sc)
+{
+ struct crom_src *src;
+
+ printf("fw_init_crom\n");
+ sc->crom_src_buf = (struct crom_src_buf *)
+ malloc(sizeof(struct crom_src_buf));
+ if (sc->crom_src_buf == NULL)
+ return;
+
+ src = &sc->crom_src_buf->src;
+ bzero(src, sizeof(struct crom_src));
+
+ /* BUS info sample */
+ src->hdr.info_len = 4;
+
+ src->businfo.bus_name = CSR_BUS_NAME_IEEE1394;
+
+ src->businfo.irmc = 1;
+ src->businfo.cmc = 1;
+ src->businfo.isc = 1;
+ src->businfo.bmc = 1;
+ src->businfo.pmc = 0;
+ src->businfo.cyc_clk_acc = 100;
+ src->businfo.max_rec = sc->maxrec;
+ src->businfo.max_rom = MAXROM_4;
+#define FW_GENERATION_CHANGEABLE 2
+ src->businfo.generation = FW_GENERATION_CHANGEABLE;
+ src->businfo.link_spd = sc->speed;
+
+ src->businfo.eui64.hi = sc->eui.hi;
+ src->businfo.eui64.lo = sc->eui.lo;
+
+ STAILQ_INIT(&src->chunk_list);
+
+ sc->crom_src = src;
+ sc->crom_root = &sc->crom_src_buf->root;
+}
+
+static void
+fw_reset_crom(struct fwohci_softc *sc)
+{
+ struct crom_src_buf *buf;
+ struct crom_src *src;
+ struct crom_chunk *root;
+
+ printf("fw_reset\n");
+ if (sc->crom_src_buf == NULL)
+ fw_init_crom(sc);
+
+ buf = sc->crom_src_buf;
+ src = sc->crom_src;
+ root = sc->crom_root;
+
+ STAILQ_INIT(&src->chunk_list);
+
+ bzero(root, sizeof(struct crom_chunk));
+ crom_add_chunk(src, NULL, root, 0);
+ crom_add_entry(root, CSRKEY_NCAP, 0x0083c0); /* XXX */
+ /* private company_id */
+ crom_add_entry(root, CSRKEY_VENDOR, CSRVAL_VENDOR_PRIVATE);
+#ifdef __DragonFly__
+ crom_add_simple_text(src, root, &buf->vendor, "DragonFly Project");
+#else
+ crom_add_simple_text(src, root, &buf->vendor, "FreeBSD Project");
+#endif
+}
+
+
+#define ADDR_HI(x) (((x) >> 24) & 0xffffff)
+#define ADDR_LO(x) ((x) & 0xffffff)
+
+static void
+dcons_crom(struct fwohci_softc *sc)
+{
+ struct crom_src_buf *buf;
+ struct crom_src *src;
+ struct crom_chunk *root;
+
+ buf = sc->crom_src_buf;
+ src = sc->crom_src;
+ root = sc->crom_root;
+
+ bzero(&buf->unit, sizeof(struct crom_chunk));
+
+ crom_add_chunk(src, root, &buf->unit, CROM_UDIR);
+ crom_add_entry(&buf->unit, CSRKEY_SPEC, CSRVAL_VENDOR_PRIVATE);
+ crom_add_simple_text(src, &buf->unit, &buf->spec, "FreeBSD");
+ crom_add_entry(&buf->unit, CSRKEY_VER, DCONS_CSR_VAL_VER);
+ crom_add_simple_text(src, &buf->unit, &buf->ver, "dcons");
+ crom_add_entry(&buf->unit, DCONS_CSR_KEY_HI, ADDR_HI(dcons_paddr));
+ crom_add_entry(&buf->unit, DCONS_CSR_KEY_LO, ADDR_LO(dcons_paddr));
+}
+
+void
+fw_crom(struct fwohci_softc *sc)
+{
+ struct crom_src *src;
+ void *newrom;
+
+ fw_reset_crom(sc);
+ dcons_crom(sc);
+
+ newrom = malloc(CROMSIZE);
+ src = &sc->crom_src_buf->src;
+ crom_load(src, (uint32_t *)newrom, CROMSIZE);
+ if (bcmp(newrom, sc->config_rom, CROMSIZE) != 0) {
+ /* Bump generation and reload. */
+ src->businfo.generation++;
+
+ /* Handle generation count wraps. */
+ if (src->businfo.generation < 2)
+ src->businfo.generation = 2;
+
+ /* Recalculate CRC to account for generation change. */
+ crom_load(src, (uint32_t *)newrom, CROMSIZE);
+ bcopy(newrom, (void *)sc->config_rom, CROMSIZE);
+ }
+ free(newrom);
+}
+
+static int
+fw_busreset(struct fwohci_softc *sc)
+{
+ int count;
+
+ if (sc->state < FWOHCI_STATE_ENABLED) {
+ printf("fwohci not enabled\n");
+ return(CMD_OK);
+ }
+ fw_crom(sc);
+ fwohci_ibr(sc);
+ count = 0;
+ while (sc->state< FWOHCI_STATE_NORMAL) {
+ fwohci_poll(sc);
+ count ++;
+ if (count > 1000) {
+ printf("give up to wait bus initialize\n");
+ return (-1);
+ }
+ }
+ printf("poll count = %d\n", count);
+ return (0);
+}
+
+void
+fw_enable(void)
+{
+ struct fwohci_softc *sc;
+ int i;
+
+ if (fw_initialized == 0)
+ fw_init();
+
+ for (i = 0; i < MAX_OHCI; i ++) {
+ sc = &fwinfo[i];
+ if (sc->state != FWOHCI_STATE_INIT)
+ break;
+
+ sc->config_rom = (uint32_t *)
+ (((uint32_t)sc->config_rom_buf
+ + (CROMSIZE - 1)) & ~(CROMSIZE - 1));
+#if 0
+ printf("configrom: %08p %08p\n",
+ sc->config_rom_buf, sc->config_rom);
+#endif
+ if (fwohci_init(sc, 0) == 0) {
+ sc->state = FWOHCI_STATE_ENABLED;
+ fw_busreset(sc);
+ } else
+ sc->state = FWOHCI_STATE_DEAD;
+ }
+}
+
+void
+fw_poll(void)
+{
+ struct fwohci_softc *sc;
+ int i;
+
+ if (fw_initialized == 0)
+ return;
+
+ for (i = 0; i < MAX_OHCI; i ++) {
+ sc = &fwinfo[i];
+ if (sc->state < FWOHCI_STATE_ENABLED)
+ break;
+ fwohci_poll(sc);
+ }
+}
+
+#if 0 /* for debug */
+static int
+fw_busreset_cmd(int argc, char *argv[])
+{
+ struct fwohci_softc *sc;
+ int i;
+
+ for (i = 0; i < MAX_OHCI; i ++) {
+ sc = &fwinfo[i];
+ if (sc->state < FWOHCI_STATE_INIT)
+ break;
+ fw_busreset(sc);
+ }
+ return(CMD_OK);
+}
+
+static int
+fw_poll_cmd(int argc, char *argv[])
+{
+ fw_poll();
+ return(CMD_OK);
+}
+
+static int
+fw_enable_cmd(int argc, char *argv[])
+{
+ fw_print(0);
+ fw_enable();
+ return(CMD_OK);
+}
+
+
+static int
+dcons_enable(int argc, char *argv[])
+{
+ dconsole.c_init(0);
+ fw_enable();
+ dconsole.c_flags |= C_ACTIVEIN | C_ACTIVEOUT;
+ return(CMD_OK);
+}
+
+static int
+dcons_read(int argc, char *argv[])
+{
+ char c;
+ while (dconsole.c_ready()) {
+ c = dconsole.c_in();
+ printf("%c", c);
+ }
+ printf("\r\n");
+ return(CMD_OK);
+}
+
+static int
+dcons_write(int argc, char *argv[])
+{
+ int len, i;
+ if (argc < 2)
+ return(CMD_OK);
+
+ len = strlen(argv[1]);
+ for (i = 0; i < len; i ++)
+ dconsole.c_out(argv[1][i]);
+ dconsole.c_out('\r');
+ dconsole.c_out('\n');
+ return(CMD_OK);
+}
+COMMAND_SET(firewire, "firewire", "enable firewire", fw_enable_cmd);
+COMMAND_SET(fwbusreset, "fwbusreset", "firewire busreset", fw_busreset_cmd);
+COMMAND_SET(fwpoll, "fwpoll", "firewire poll", fw_poll_cmd);
+COMMAND_SET(dcons, "dcons", "enable dcons", dcons_enable);
+COMMAND_SET(dread, "dread", "read from dcons", dcons_read);
+COMMAND_SET(dwrite, "dwrite", "write to dcons", dcons_write);
+#endif
diff --git a/stand/i386/libfirewire/fwohci.c b/stand/i386/libfirewire/fwohci.c
new file mode 100644
index 0000000..567abcf
--- /dev/null
+++ b/stand/i386/libfirewire/fwohci.c
@@ -0,0 +1,479 @@
+/*
+ * Copyright (c) 2003 Hidetoshi Shimokawa
+ * Copyright (c) 1998-2002 Katsushi Kobayashi and Hidetoshi Shimokawa
+ * 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 acknowledgement as bellow:
+ *
+ * This product includes software developed by K. Kobayashi and H. Shimokawa
+ *
+ * 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.
+ *
+ * $FreeBSD$
+ *
+ */
+
+#include <stand.h>
+#include <btxv86.h>
+#include <bootstrap.h>
+
+#include "fwohci.h"
+#include "fwohcireg.h"
+#include <dev/firewire/firewire_phy.h>
+
+static uint32_t fwphy_wrdata ( struct fwohci_softc *, uint32_t, uint32_t);
+static uint32_t fwphy_rddata ( struct fwohci_softc *, uint32_t);
+int firewire_debug=0;
+
+#if 0
+#define device_printf(a, x, ...) printf("FW1394: " x, ## __VA_ARGS__)
+#else
+#define device_printf(a, x, ...)
+#endif
+
+#define device_t int
+#define DELAY(x) delay(x)
+
+#define MAX_SPEED 3
+#define MAXREC(x) (2 << (x))
+char *linkspeed[] = {
+ "S100", "S200", "S400", "S800",
+ "S1600", "S3200", "undef", "undef"
+};
+
+#define FW_EUI64_BYTE(eui, x) \
+ ((((x)<4)? \
+ ((eui)->hi >> (8*(3-(x)))): \
+ ((eui)->lo >> (8*(7-(x)))) \
+ ) & 0xff)
+
+/*
+ * Communication with PHY device
+ */
+static uint32_t
+fwphy_wrdata( struct fwohci_softc *sc, uint32_t addr, uint32_t data)
+{
+ uint32_t fun;
+
+ addr &= 0xf;
+ data &= 0xff;
+
+ fun = (PHYDEV_WRCMD | (addr << PHYDEV_REGADDR) | (data << PHYDEV_WRDATA));
+ OWRITE(sc, OHCI_PHYACCESS, fun);
+ DELAY(100);
+
+ return(fwphy_rddata( sc, addr));
+}
+
+static uint32_t
+fwphy_rddata(struct fwohci_softc *sc, u_int addr)
+{
+ uint32_t fun, stat;
+ u_int i, retry = 0;
+
+ addr &= 0xf;
+#define MAX_RETRY 100
+again:
+ OWRITE(sc, FWOHCI_INTSTATCLR, OHCI_INT_REG_FAIL);
+ fun = PHYDEV_RDCMD | (addr << PHYDEV_REGADDR);
+ OWRITE(sc, OHCI_PHYACCESS, fun);
+ for ( i = 0 ; i < MAX_RETRY ; i ++ ){
+ fun = OREAD(sc, OHCI_PHYACCESS);
+ if ((fun & PHYDEV_RDCMD) == 0 && (fun & PHYDEV_RDDONE) != 0)
+ break;
+ DELAY(100);
+ }
+ if(i >= MAX_RETRY) {
+ if (firewire_debug)
+ device_printf(sc->fc.dev, "phy read failed(1).\n");
+ if (++retry < MAX_RETRY) {
+ DELAY(100);
+ goto again;
+ }
+ }
+ /* Make sure that SCLK is started */
+ stat = OREAD(sc, FWOHCI_INTSTAT);
+ if ((stat & OHCI_INT_REG_FAIL) != 0 ||
+ ((fun >> PHYDEV_REGADDR) & 0xf) != addr) {
+ if (firewire_debug)
+ device_printf(sc->fc.dev, "phy read failed(2).\n");
+ if (++retry < MAX_RETRY) {
+ DELAY(100);
+ goto again;
+ }
+ }
+ if (firewire_debug || retry >= MAX_RETRY)
+ device_printf(sc->fc.dev,
+ "fwphy_rddata: 0x%x loop=%d, retry=%d\n", addr, i, retry);
+#undef MAX_RETRY
+ return((fun >> PHYDEV_RDDATA )& 0xff);
+}
+
+
+static int
+fwohci_probe_phy(struct fwohci_softc *sc, device_t dev)
+{
+ uint32_t reg, reg2;
+ int e1394a = 1;
+ int nport, speed;
+/*
+ * probe PHY parameters
+ * 0. to prove PHY version, whether compliance of 1394a.
+ * 1. to probe maximum speed supported by the PHY and
+ * number of port supported by core-logic.
+ * It is not actually available port on your PC .
+ */
+ OWRITE(sc, OHCI_HCCCTL, OHCI_HCC_LPS);
+ DELAY(500);
+
+ reg = fwphy_rddata(sc, FW_PHY_SPD_REG);
+
+ if((reg >> 5) != 7 ){
+ nport = reg & FW_PHY_NP;
+ speed = reg & FW_PHY_SPD >> 6;
+ if (speed > MAX_SPEED) {
+ device_printf(dev, "invalid speed %d (fixed to %d).\n",
+ speed, MAX_SPEED);
+ speed = MAX_SPEED;
+ }
+ device_printf(dev,
+ "Phy 1394 only %s, %d ports.\n",
+ linkspeed[speed], nport);
+ }else{
+ reg2 = fwphy_rddata(sc, FW_PHY_ESPD_REG);
+ nport = reg & FW_PHY_NP;
+ speed = (reg2 & FW_PHY_ESPD) >> 5;
+ if (speed > MAX_SPEED) {
+ device_printf(dev, "invalid speed %d (fixed to %d).\n",
+ speed, MAX_SPEED);
+ speed = MAX_SPEED;
+ }
+ device_printf(dev,
+ "Phy 1394a available %s, %d ports.\n",
+ linkspeed[speed], nport);
+
+ /* check programPhyEnable */
+ reg2 = fwphy_rddata(sc, 5);
+#if 0
+ if (e1394a && (OREAD(sc, OHCI_HCCCTL) & OHCI_HCC_PRPHY)) {
+#else /* XXX force to enable 1394a */
+ if (e1394a) {
+#endif
+ if (firewire_debug)
+ device_printf(dev,
+ "Enable 1394a Enhancements\n");
+ /* enable EAA EMC */
+ reg2 |= 0x03;
+ /* set aPhyEnhanceEnable */
+ OWRITE(sc, OHCI_HCCCTL, OHCI_HCC_PHYEN);
+ OWRITE(sc, OHCI_HCCCTLCLR, OHCI_HCC_PRPHY);
+ } else {
+ /* for safe */
+ reg2 &= ~0x83;
+ }
+ reg2 = fwphy_wrdata(sc, 5, reg2);
+ }
+ sc->speed = speed;
+
+ reg = fwphy_rddata(sc, FW_PHY_SPD_REG);
+ if((reg >> 5) == 7 ){
+ reg = fwphy_rddata(sc, 4);
+ reg |= 1 << 6;
+ fwphy_wrdata(sc, 4, reg);
+ reg = fwphy_rddata(sc, 4);
+ }
+ return 0;
+}
+
+
+void
+fwohci_reset(struct fwohci_softc *sc, device_t dev)
+{
+ int i, max_rec, speed;
+ uint32_t reg, reg2;
+
+ /* Disable interrupts */
+ OWRITE(sc, FWOHCI_INTMASKCLR, ~0);
+
+ /* FLUSH FIFO and reset Transmitter/Receiver */
+ OWRITE(sc, OHCI_HCCCTL, OHCI_HCC_RESET);
+ if (firewire_debug)
+ device_printf(dev, "resetting OHCI...");
+ i = 0;
+ while(OREAD(sc, OHCI_HCCCTL) & OHCI_HCC_RESET) {
+ if (i++ > 100) break;
+ DELAY(1000);
+ }
+ if (firewire_debug)
+ printf("done (loop=%d)\n", i);
+
+ /* Probe phy */
+ fwohci_probe_phy(sc, dev);
+
+ /* Probe link */
+ reg = OREAD(sc, OHCI_BUS_OPT);
+ reg2 = reg | OHCI_BUSFNC;
+ max_rec = (reg & 0x0000f000) >> 12;
+ speed = (reg & 0x00000007);
+ device_printf(dev, "Link %s, max_rec %d bytes.\n",
+ linkspeed[speed], MAXREC(max_rec));
+ /* XXX fix max_rec */
+ sc->maxrec = sc->speed + 8;
+ if (max_rec != sc->maxrec) {
+ reg2 = (reg2 & 0xffff0fff) | (sc->maxrec << 12);
+ device_printf(dev, "max_rec %d -> %d\n",
+ MAXREC(max_rec), MAXREC(sc->maxrec));
+ }
+ if (firewire_debug)
+ device_printf(dev, "BUS_OPT 0x%x -> 0x%x\n", reg, reg2);
+ OWRITE(sc, OHCI_BUS_OPT, reg2);
+
+ /* Initialize registers */
+ OWRITE(sc, OHCI_CROMHDR, sc->config_rom[0]);
+ OWRITE(sc, OHCI_CROMPTR, VTOP(sc->config_rom));
+#if 0
+ OWRITE(sc, OHCI_SID_BUF, sc->sid_dma.bus_addr);
+#endif
+ OWRITE(sc, OHCI_HCCCTLCLR, OHCI_HCC_BIGEND);
+ OWRITE(sc, OHCI_HCCCTL, OHCI_HCC_POSTWR);
+#if 0
+ OWRITE(sc, OHCI_LNKCTL, OHCI_CNTL_SID);
+#endif
+
+ /* Enable link */
+ OWRITE(sc, OHCI_HCCCTL, OHCI_HCC_LINKEN);
+}
+
+int
+fwohci_init(struct fwohci_softc *sc, device_t dev)
+{
+ int i, mver;
+ uint32_t reg;
+ uint8_t ui[8];
+
+/* OHCI version */
+ reg = OREAD(sc, OHCI_VERSION);
+ mver = (reg >> 16) & 0xff;
+ device_printf(dev, "OHCI version %x.%x (ROM=%d)\n",
+ mver, reg & 0xff, (reg>>24) & 1);
+ if (mver < 1 || mver > 9) {
+ device_printf(dev, "invalid OHCI version\n");
+ return (ENXIO);
+ }
+
+/* Available Isochronous DMA channel probe */
+ OWRITE(sc, OHCI_IT_MASK, 0xffffffff);
+ OWRITE(sc, OHCI_IR_MASK, 0xffffffff);
+ reg = OREAD(sc, OHCI_IT_MASK) & OREAD(sc, OHCI_IR_MASK);
+ OWRITE(sc, OHCI_IT_MASKCLR, 0xffffffff);
+ OWRITE(sc, OHCI_IR_MASKCLR, 0xffffffff);
+ for (i = 0; i < 0x20; i++)
+ if ((reg & (1 << i)) == 0)
+ break;
+ device_printf(dev, "No. of Isochronous channels is %d.\n", i);
+ if (i == 0)
+ return (ENXIO);
+
+#if 0
+/* SID receive buffer must align 2^11 */
+#define OHCI_SIDSIZE (1 << 11)
+ sc->sid_buf = fwdma_malloc(&sc->fc, OHCI_SIDSIZE, OHCI_SIDSIZE,
+ &sc->sid_dma, BUS_DMA_WAITOK);
+ if (sc->sid_buf == NULL) {
+ device_printf(dev, "sid_buf alloc failed.");
+ return ENOMEM;
+ }
+#endif
+
+ sc->eui.hi = OREAD(sc, FWOHCIGUID_H);
+ sc->eui.lo = OREAD(sc, FWOHCIGUID_L);
+ for( i = 0 ; i < 8 ; i ++)
+ ui[i] = FW_EUI64_BYTE(&sc->eui,i);
+ device_printf(dev, "EUI64 %02x:%02x:%02x:%02x:%02x:%02x:%02x:%02x\n",
+ ui[0], ui[1], ui[2], ui[3], ui[4], ui[5], ui[6], ui[7]);
+ fwohci_reset(sc, dev);
+
+ return 0;
+}
+
+void
+fwohci_ibr(struct fwohci_softc *sc)
+{
+ uint32_t fun;
+
+ device_printf(sc->dev, "Initiate bus reset\n");
+
+ /*
+ * Make sure our cached values from the config rom are
+ * initialised.
+ */
+ OWRITE(sc, OHCI_CROMHDR, ntohl(sc->config_rom[0]));
+ OWRITE(sc, OHCI_BUS_OPT, ntohl(sc->config_rom[2]));
+
+ /*
+ * Set root hold-off bit so that non cyclemaster capable node
+ * shouldn't became the root node.
+ */
+#if 1
+ fun = fwphy_rddata(sc, FW_PHY_IBR_REG);
+ fun |= FW_PHY_IBR;
+ fun = fwphy_wrdata(sc, FW_PHY_IBR_REG, fun);
+#else /* Short bus reset */
+ fun = fwphy_rddata(sc, FW_PHY_ISBR_REG);
+ fun |= FW_PHY_ISBR;
+ fun = fwphy_wrdata(sc, FW_PHY_ISBR_REG, fun);
+#endif
+}
+
+
+void
+fwohci_sid(struct fwohci_softc *sc)
+{
+ uint32_t node_id;
+ int plen;
+
+ node_id = OREAD(sc, FWOHCI_NODEID);
+ if (!(node_id & OHCI_NODE_VALID)) {
+#if 0
+ printf("Bus reset failure\n");
+#endif
+ return;
+ }
+
+ /* Enable bus reset interrupt */
+ OWRITE(sc, FWOHCI_INTMASK, OHCI_INT_PHY_BUS_R);
+ /* Allow async. request to us */
+ OWRITE(sc, OHCI_AREQHI, 1 << 31);
+ /* XXX insecure ?? */
+ OWRITE(sc, OHCI_PREQHI, 0x7fffffff);
+ OWRITE(sc, OHCI_PREQLO, 0xffffffff);
+ OWRITE(sc, OHCI_PREQUPPER, 0x10000);
+ /* Set ATRetries register */
+ OWRITE(sc, OHCI_ATRETRY, 1<<(13+16) | 0xfff);
+/*
+** Checking whether the node is root or not. If root, turn on
+** cycle master.
+*/
+ plen = OREAD(sc, OHCI_SID_CNT);
+ device_printf(fc->dev, "node_id=0x%08x, gen=%d, ",
+ node_id, (plen >> 16) & 0xff);
+ if (node_id & OHCI_NODE_ROOT) {
+ device_printf(sc->dev, "CYCLEMASTER mode\n");
+ OWRITE(sc, OHCI_LNKCTL,
+ OHCI_CNTL_CYCMTR | OHCI_CNTL_CYCTIMER);
+ } else {
+ device_printf(sc->dev, "non CYCLEMASTER mode\n");
+ OWRITE(sc, OHCI_LNKCTLCLR, OHCI_CNTL_CYCMTR);
+ OWRITE(sc, OHCI_LNKCTL, OHCI_CNTL_CYCTIMER);
+ }
+ if (plen & OHCI_SID_ERR) {
+ device_printf(fc->dev, "SID Error\n");
+ return;
+ }
+ device_printf(sc->dev, "bus reset phase done\n");
+ sc->state = FWOHCI_STATE_NORMAL;
+}
+
+static void
+fwohci_intr_body(struct fwohci_softc *sc, uint32_t stat, int count)
+{
+#undef OHCI_DEBUG
+#ifdef OHCI_DEBUG
+#if 0
+ if(stat & OREAD(sc, FWOHCI_INTMASK))
+#else
+ if (1)
+#endif
+ device_printf(fc->dev, "INTERRUPT < %s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s> 0x%08x, 0x%08x\n",
+ stat & OHCI_INT_EN ? "DMA_EN ":"",
+ stat & OHCI_INT_PHY_REG ? "PHY_REG ":"",
+ stat & OHCI_INT_CYC_LONG ? "CYC_LONG ":"",
+ stat & OHCI_INT_ERR ? "INT_ERR ":"",
+ stat & OHCI_INT_CYC_ERR ? "CYC_ERR ":"",
+ stat & OHCI_INT_CYC_LOST ? "CYC_LOST ":"",
+ stat & OHCI_INT_CYC_64SECOND ? "CYC_64SECOND ":"",
+ stat & OHCI_INT_CYC_START ? "CYC_START ":"",
+ stat & OHCI_INT_PHY_INT ? "PHY_INT ":"",
+ stat & OHCI_INT_PHY_BUS_R ? "BUS_RESET ":"",
+ stat & OHCI_INT_PHY_SID ? "SID ":"",
+ stat & OHCI_INT_LR_ERR ? "DMA_LR_ERR ":"",
+ stat & OHCI_INT_PW_ERR ? "DMA_PW_ERR ":"",
+ stat & OHCI_INT_DMA_IR ? "DMA_IR ":"",
+ stat & OHCI_INT_DMA_IT ? "DMA_IT " :"",
+ stat & OHCI_INT_DMA_PRRS ? "DMA_PRRS " :"",
+ stat & OHCI_INT_DMA_PRRQ ? "DMA_PRRQ " :"",
+ stat & OHCI_INT_DMA_ARRS ? "DMA_ARRS " :"",
+ stat & OHCI_INT_DMA_ARRQ ? "DMA_ARRQ " :"",
+ stat & OHCI_INT_DMA_ATRS ? "DMA_ATRS " :"",
+ stat & OHCI_INT_DMA_ATRQ ? "DMA_ATRQ " :"",
+ stat, OREAD(sc, FWOHCI_INTMASK)
+ );
+#endif
+/* Bus reset */
+ if(stat & OHCI_INT_PHY_BUS_R ){
+ device_printf(fc->dev, "BUS reset\n");
+ if (sc->state == FWOHCI_STATE_BUSRESET)
+ goto busresetout;
+ sc->state = FWOHCI_STATE_BUSRESET;
+ /* Disable bus reset interrupt until sid recv. */
+ OWRITE(sc, FWOHCI_INTMASKCLR, OHCI_INT_PHY_BUS_R);
+
+ OWRITE(sc, FWOHCI_INTMASKCLR, OHCI_INT_CYC_LOST);
+ OWRITE(sc, OHCI_LNKCTLCLR, OHCI_CNTL_CYCSRC);
+
+ OWRITE(sc, OHCI_CROMHDR, ntohl(sc->config_rom[0]));
+ OWRITE(sc, OHCI_BUS_OPT, ntohl(sc->config_rom[2]));
+ } else if (sc->state == FWOHCI_STATE_BUSRESET) {
+ fwohci_sid(sc);
+ }
+busresetout:
+ return;
+}
+
+static uint32_t
+fwochi_check_stat(struct fwohci_softc *sc)
+{
+ uint32_t stat;
+
+ stat = OREAD(sc, FWOHCI_INTSTAT);
+ if (stat == 0xffffffff) {
+ device_printf(sc->fc.dev,
+ "device physically ejected?\n");
+ return(stat);
+ }
+ if (stat)
+ OWRITE(sc, FWOHCI_INTSTATCLR, stat);
+ return(stat);
+}
+
+void
+fwohci_poll(struct fwohci_softc *sc)
+{
+ uint32_t stat;
+
+ stat = fwochi_check_stat(sc);
+ if (stat != 0xffffffff)
+ fwohci_intr_body(sc, stat, 1);
+}
diff --git a/stand/i386/libfirewire/fwohci.h b/stand/i386/libfirewire/fwohci.h
new file mode 100644
index 0000000..4a93220
--- /dev/null
+++ b/stand/i386/libfirewire/fwohci.h
@@ -0,0 +1,162 @@
+/*
+ * Copyright (c) 2007 Hidetoshi Shimokawa
+ * 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 acknowledgement as bellow:
+ *
+ * This product includes software developed by K. Kobayashi and H. Shimokawa
+ *
+ * 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.
+ *
+ * $FreeBSD$
+ *
+ */
+
+#define MAX_OHCI 5
+#define CROMSIZE 0x400
+
+struct fw_eui64 {
+ uint32_t hi, lo;
+};
+
+struct fwohci_softc {
+ uint32_t locator;
+ uint32_t devid;
+ uint32_t base_addr;
+ uint32_t bus_id;
+ uint32_t handle;
+ int32_t state;
+ struct crom_src_buf *crom_src_buf;
+ struct crom_src *crom_src;
+ struct crom_chunk *crom_root;
+ struct fw_eui64 eui;
+ int speed;
+ int maxrec;
+ uint32_t *config_rom;
+ char config_rom_buf[CROMSIZE*2]; /* double size for alignment */
+};
+
+int fwohci_init(struct fwohci_softc *, int);
+void fwohci_ibr(struct fwohci_softc *);
+void fwohci_poll(struct fwohci_softc *);
+
+#define FWOHCI_STATE_DEAD (-1)
+#define FWOHCI_STATE_INIT 0
+#define FWOHCI_STATE_ENABLED 1
+#define FWOHCI_STATE_BUSRESET 2
+#define FWOHCI_STATE_NORMAL 3
+
+#define OREAD(f, o) (*(volatile uint32_t *)((f)->handle + (o)))
+#define OWRITE(f, o, v) (*(volatile uint32_t *)((f)->handle + (o)) = (v))
+
+#define OHCI_VERSION 0x00
+#define OHCI_ATRETRY 0x08
+#define OHCI_CROMHDR 0x18
+#define OHCI_BUS_ID 0x1c
+#define OHCI_BUS_OPT 0x20
+#define OHCI_BUSIRMC (1U << 31)
+#define OHCI_BUSCMC (1 << 30)
+#define OHCI_BUSISC (1 << 29)
+#define OHCI_BUSBMC (1 << 28)
+#define OHCI_BUSPMC (1 << 27)
+#define OHCI_BUSFNC OHCI_BUSIRMC | OHCI_BUSCMC | OHCI_BUSISC |\
+ OHCI_BUSBMC | OHCI_BUSPMC
+
+#define OHCI_EUID_HI 0x24
+#define OHCI_EUID_LO 0x28
+
+#define OHCI_CROMPTR 0x34
+#define OHCI_HCCCTL 0x50
+#define OHCI_HCCCTLCLR 0x54
+#define OHCI_AREQHI 0x100
+#define OHCI_AREQHICLR 0x104
+#define OHCI_AREQLO 0x108
+#define OHCI_AREQLOCLR 0x10c
+#define OHCI_PREQHI 0x110
+#define OHCI_PREQHICLR 0x114
+#define OHCI_PREQLO 0x118
+#define OHCI_PREQLOCLR 0x11c
+#define OHCI_PREQUPPER 0x120
+
+#define OHCI_SID_BUF 0x64
+#define OHCI_SID_CNT 0x68
+#define OHCI_SID_ERR (1U << 31)
+#define OHCI_SID_CNT_MASK 0xffc
+
+#define OHCI_IT_STAT 0x90
+#define OHCI_IT_STATCLR 0x94
+#define OHCI_IT_MASK 0x98
+#define OHCI_IT_MASKCLR 0x9c
+
+#define OHCI_IR_STAT 0xa0
+#define OHCI_IR_STATCLR 0xa4
+#define OHCI_IR_MASK 0xa8
+#define OHCI_IR_MASKCLR 0xac
+
+#define OHCI_LNKCTL 0xe0
+#define OHCI_LNKCTLCLR 0xe4
+
+#define OHCI_PHYACCESS 0xec
+#define OHCI_CYCLETIMER 0xf0
+
+#define OHCI_DMACTL(off) (off)
+#define OHCI_DMACTLCLR(off) (off + 4)
+#define OHCI_DMACMD(off) (off + 0xc)
+#define OHCI_DMAMATCH(off) (off + 0x10)
+
+#define OHCI_ATQOFF 0x180
+#define OHCI_ATQCTL OHCI_ATQOFF
+#define OHCI_ATQCTLCLR (OHCI_ATQOFF + 4)
+#define OHCI_ATQCMD (OHCI_ATQOFF + 0xc)
+#define OHCI_ATQMATCH (OHCI_ATQOFF + 0x10)
+
+#define OHCI_ATSOFF 0x1a0
+#define OHCI_ATSCTL OHCI_ATSOFF
+#define OHCI_ATSCTLCLR (OHCI_ATSOFF + 4)
+#define OHCI_ATSCMD (OHCI_ATSOFF + 0xc)
+#define OHCI_ATSMATCH (OHCI_ATSOFF + 0x10)
+
+#define OHCI_ARQOFF 0x1c0
+#define OHCI_ARQCTL OHCI_ARQOFF
+#define OHCI_ARQCTLCLR (OHCI_ARQOFF + 4)
+#define OHCI_ARQCMD (OHCI_ARQOFF + 0xc)
+#define OHCI_ARQMATCH (OHCI_ARQOFF + 0x10)
+
+#define OHCI_ARSOFF 0x1e0
+#define OHCI_ARSCTL OHCI_ARSOFF
+#define OHCI_ARSCTLCLR (OHCI_ARSOFF + 4)
+#define OHCI_ARSCMD (OHCI_ARSOFF + 0xc)
+#define OHCI_ARSMATCH (OHCI_ARSOFF + 0x10)
+
+#define OHCI_ITOFF(CH) (0x200 + 0x10 * (CH))
+#define OHCI_ITCTL(CH) (OHCI_ITOFF(CH))
+#define OHCI_ITCTLCLR(CH) (OHCI_ITOFF(CH) + 4)
+#define OHCI_ITCMD(CH) (OHCI_ITOFF(CH) + 0xc)
+
+#define OHCI_IROFF(CH) (0x400 + 0x20 * (CH))
+#define OHCI_IRCTL(CH) (OHCI_IROFF(CH))
+#define OHCI_IRCTLCLR(CH) (OHCI_IROFF(CH) + 4)
+#define OHCI_IRCMD(CH) (OHCI_IROFF(CH) + 0xc)
+#define OHCI_IRMATCH(CH) (OHCI_IROFF(CH) + 0x10)
diff --git a/stand/i386/libfirewire/fwohcireg.h b/stand/i386/libfirewire/fwohcireg.h
new file mode 100644
index 0000000..d57870c
--- /dev/null
+++ b/stand/i386/libfirewire/fwohcireg.h
@@ -0,0 +1,369 @@
+/*
+ * Copyright (c) 2003 Hidetoshi Shimokawa
+ * Copyright (c) 1998-2002 Katsushi Kobayashi and Hidetoshi Shimokawa
+ * 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 acknowledgement as bellow:
+ *
+ * This product includes software developed by K. Kobayashi and H. Shimokawa
+ *
+ * 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.
+ *
+ * $FreeBSD$
+ *
+ */
+#define PCI_CBMEM PCIR_BAR(0)
+
+#define FW_VENDORID_NATSEMI 0x100B
+#define FW_VENDORID_NEC 0x1033
+#define FW_VENDORID_SIS 0x1039
+#define FW_VENDORID_TI 0x104c
+#define FW_VENDORID_SONY 0x104d
+#define FW_VENDORID_VIA 0x1106
+#define FW_VENDORID_RICOH 0x1180
+#define FW_VENDORID_APPLE 0x106b
+#define FW_VENDORID_LUCENT 0x11c1
+#define FW_VENDORID_INTEL 0x8086
+#define FW_VENDORID_ADAPTEC 0x9004
+
+#define FW_DEVICE_CS4210 (0x000f << 16)
+#define FW_DEVICE_UPD861 (0x0063 << 16)
+#define FW_DEVICE_UPD871 (0x00ce << 16)
+#define FW_DEVICE_UPD72870 (0x00cd << 16)
+#define FW_DEVICE_UPD72873 (0x00e7 << 16)
+#define FW_DEVICE_UPD72874 (0x00f2 << 16)
+#define FW_DEVICE_TITSB22 (0x8009 << 16)
+#define FW_DEVICE_TITSB23 (0x8019 << 16)
+#define FW_DEVICE_TITSB26 (0x8020 << 16)
+#define FW_DEVICE_TITSB43 (0x8021 << 16)
+#define FW_DEVICE_TITSB43A (0x8023 << 16)
+#define FW_DEVICE_TITSB43AB23 (0x8024 << 16)
+#define FW_DEVICE_TITSB82AA2 (0x8025 << 16)
+#define FW_DEVICE_TITSB43AB21 (0x8026 << 16)
+#define FW_DEVICE_TIPCI4410A (0x8017 << 16)
+#define FW_DEVICE_TIPCI4450 (0x8011 << 16)
+#define FW_DEVICE_TIPCI4451 (0x8027 << 16)
+#define FW_DEVICE_CXD1947 (0x8009 << 16)
+#define FW_DEVICE_CXD3222 (0x8039 << 16)
+#define FW_DEVICE_VT6306 (0x3044 << 16)
+#define FW_DEVICE_R5C551 (0x0551 << 16)
+#define FW_DEVICE_R5C552 (0x0552 << 16)
+#define FW_DEVICE_PANGEA (0x0030 << 16)
+#define FW_DEVICE_UNINORTH (0x0031 << 16)
+#define FW_DEVICE_AIC5800 (0x5800 << 16)
+#define FW_DEVICE_FW322 (0x5811 << 16)
+#define FW_DEVICE_7007 (0x7007 << 16)
+#define FW_DEVICE_82372FB (0x7605 << 16)
+
+#define PCI_INTERFACE_OHCI 0x10
+
+#define FW_OHCI_BASE_REG 0x10
+
+#define OHCI_DMA_ITCH 0x20
+#define OHCI_DMA_IRCH 0x20
+
+#define OHCI_MAX_DMA_CH (0x4 + OHCI_DMA_ITCH + OHCI_DMA_IRCH)
+
+
+typedef uint32_t fwohcireg_t;
+
+/* for PCI */
+#if BYTE_ORDER == BIG_ENDIAN
+#define FWOHCI_DMA_WRITE(x, y) ((x) = htole32(y))
+#define FWOHCI_DMA_READ(x) le32toh(x)
+#define FWOHCI_DMA_SET(x, y) ((x) |= htole32(y))
+#define FWOHCI_DMA_CLEAR(x, y) ((x) &= htole32(~(y)))
+#else
+#define FWOHCI_DMA_WRITE(x, y) ((x) = (y))
+#define FWOHCI_DMA_READ(x) (x)
+#define FWOHCI_DMA_SET(x, y) ((x) |= (y))
+#define FWOHCI_DMA_CLEAR(x, y) ((x) &= ~(y))
+#endif
+
+struct fwohcidb {
+ union {
+ struct {
+ uint32_t cmd;
+ uint32_t addr;
+ uint32_t depend;
+ uint32_t res;
+ } desc;
+ uint32_t immed[4];
+ } db;
+#define OHCI_STATUS_SHIFT 16
+#define OHCI_COUNT_MASK 0xffff
+#define OHCI_OUTPUT_MORE (0 << 28)
+#define OHCI_OUTPUT_LAST (1 << 28)
+#define OHCI_INPUT_MORE (2 << 28)
+#define OHCI_INPUT_LAST (3 << 28)
+#define OHCI_STORE_QUAD (4 << 28)
+#define OHCI_LOAD_QUAD (5 << 28)
+#define OHCI_NOP (6 << 28)
+#define OHCI_STOP (7 << 28)
+#define OHCI_STORE (8 << 28)
+#define OHCI_CMD_MASK (0xf << 28)
+
+#define OHCI_UPDATE (1 << 27)
+
+#define OHCI_KEY_ST0 (0 << 24)
+#define OHCI_KEY_ST1 (1 << 24)
+#define OHCI_KEY_ST2 (2 << 24)
+#define OHCI_KEY_ST3 (3 << 24)
+#define OHCI_KEY_REGS (5 << 24)
+#define OHCI_KEY_SYS (6 << 24)
+#define OHCI_KEY_DEVICE (7 << 24)
+#define OHCI_KEY_MASK (7 << 24)
+
+#define OHCI_INTERRUPT_NEVER (0 << 20)
+#define OHCI_INTERRUPT_TRUE (1 << 20)
+#define OHCI_INTERRUPT_FALSE (2 << 20)
+#define OHCI_INTERRUPT_ALWAYS (3 << 20)
+
+#define OHCI_BRANCH_NEVER (0 << 18)
+#define OHCI_BRANCH_TRUE (1 << 18)
+#define OHCI_BRANCH_FALSE (2 << 18)
+#define OHCI_BRANCH_ALWAYS (3 << 18)
+#define OHCI_BRANCH_MASK (3 << 18)
+
+#define OHCI_WAIT_NEVER (0 << 16)
+#define OHCI_WAIT_TRUE (1 << 16)
+#define OHCI_WAIT_FALSE (2 << 16)
+#define OHCI_WAIT_ALWAYS (3 << 16)
+};
+
+#define OHCI_SPD_S100 0x4
+#define OHCI_SPD_S200 0x1
+#define OHCI_SPD_S400 0x2
+
+
+#define FWOHCIEV_NOSTAT 0
+#define FWOHCIEV_LONGP 2
+#define FWOHCIEV_MISSACK 3
+#define FWOHCIEV_UNDRRUN 4
+#define FWOHCIEV_OVRRUN 5
+#define FWOHCIEV_DESCERR 6
+#define FWOHCIEV_DTRDERR 7
+#define FWOHCIEV_DTWRERR 8
+#define FWOHCIEV_BUSRST 9
+#define FWOHCIEV_TIMEOUT 0xa
+#define FWOHCIEV_TCODERR 0xb
+#define FWOHCIEV_UNKNOWN 0xe
+#define FWOHCIEV_FLUSHED 0xf
+#define FWOHCIEV_ACKCOMPL 0x11
+#define FWOHCIEV_ACKPEND 0x12
+#define FWOHCIEV_ACKBSX 0x14
+#define FWOHCIEV_ACKBSA 0x15
+#define FWOHCIEV_ACKBSB 0x16
+#define FWOHCIEV_ACKTARD 0x1b
+#define FWOHCIEV_ACKDERR 0x1d
+#define FWOHCIEV_ACKTERR 0x1e
+
+#define FWOHCIEV_MASK 0x1f
+
+struct ohci_dma{
+ fwohcireg_t cntl;
+
+#define OHCI_CNTL_CYCMATCH_S (0x1 << 31)
+
+#define OHCI_CNTL_BUFFIL (0x1 << 31)
+#define OHCI_CNTL_ISOHDR (0x1 << 30)
+#define OHCI_CNTL_CYCMATCH_R (0x1 << 29)
+#define OHCI_CNTL_MULTICH (0x1 << 28)
+
+#define OHCI_CNTL_DMA_RUN (0x1 << 15)
+#define OHCI_CNTL_DMA_WAKE (0x1 << 12)
+#define OHCI_CNTL_DMA_DEAD (0x1 << 11)
+#define OHCI_CNTL_DMA_ACTIVE (0x1 << 10)
+#define OHCI_CNTL_DMA_BT (0x1 << 8)
+#define OHCI_CNTL_DMA_BAD (0x1 << 7)
+#define OHCI_CNTL_DMA_STAT (0xff)
+
+ fwohcireg_t cntl_clr;
+ fwohcireg_t dummy0;
+ fwohcireg_t cmd;
+ fwohcireg_t match;
+ fwohcireg_t dummy1;
+ fwohcireg_t dummy2;
+ fwohcireg_t dummy3;
+};
+
+struct ohci_itdma{
+ fwohcireg_t cntl;
+ fwohcireg_t cntl_clr;
+ fwohcireg_t dummy0;
+ fwohcireg_t cmd;
+};
+
+struct ohci_registers {
+ fwohcireg_t ver; /* Version No. 0x0 */
+ fwohcireg_t guid; /* GUID_ROM No. 0x4 */
+ fwohcireg_t retry; /* AT retries 0x8 */
+#define FWOHCI_RETRY 0x8
+ fwohcireg_t csr_data; /* CSR data 0xc */
+ fwohcireg_t csr_cmp; /* CSR compare 0x10 */
+ fwohcireg_t csr_cntl; /* CSR compare 0x14 */
+ fwohcireg_t rom_hdr; /* config ROM ptr. 0x18 */
+ fwohcireg_t bus_id; /* BUS_ID 0x1c */
+ fwohcireg_t bus_opt; /* BUS option 0x20 */
+#define FWOHCIGUID_H 0x24
+#define FWOHCIGUID_L 0x28
+ fwohcireg_t guid_hi; /* GUID hi 0x24 */
+ fwohcireg_t guid_lo; /* GUID lo 0x28 */
+ fwohcireg_t dummy0[2]; /* dummy 0x2c-0x30 */
+ fwohcireg_t config_rom; /* config ROM map 0x34 */
+ fwohcireg_t post_wr_lo; /* post write addr lo 0x38 */
+ fwohcireg_t post_wr_hi; /* post write addr hi 0x3c */
+ fwohcireg_t vendor; /* vendor ID 0x40 */
+ fwohcireg_t dummy1[3]; /* dummy 0x44-0x4c */
+ fwohcireg_t hcc_cntl_set; /* HCC control set 0x50 */
+ fwohcireg_t hcc_cntl_clr; /* HCC control clr 0x54 */
+#define OHCI_HCC_BIBIV (1U << 31) /* BIBimage Valid */
+#define OHCI_HCC_BIGEND (1 << 30) /* noByteSwapData */
+#define OHCI_HCC_PRPHY (1 << 23) /* programPhyEnable */
+#define OHCI_HCC_PHYEN (1 << 22) /* aPhyEnhanceEnable */
+#define OHCI_HCC_LPS (1 << 19) /* LPS */
+#define OHCI_HCC_POSTWR (1 << 18) /* postedWriteEnable */
+#define OHCI_HCC_LINKEN (1 << 17) /* linkEnable */
+#define OHCI_HCC_RESET (1 << 16) /* softReset */
+ fwohcireg_t dummy2[2]; /* dummy 0x58-0x5c */
+ fwohcireg_t dummy3[1]; /* dummy 0x60 */
+ fwohcireg_t sid_buf; /* self id buffer 0x64 */
+ fwohcireg_t sid_cnt; /* self id count 0x68 */
+ fwohcireg_t dummy4[1]; /* dummy 0x6c */
+ fwohcireg_t ir_mask_hi_set; /* ir mask hi set 0x70 */
+ fwohcireg_t ir_mask_hi_clr; /* ir mask hi set 0x74 */
+ fwohcireg_t ir_mask_lo_set; /* ir mask hi set 0x78 */
+ fwohcireg_t ir_mask_lo_clr; /* ir mask hi set 0x7c */
+#define FWOHCI_INTSTAT 0x80
+#define FWOHCI_INTSTATCLR 0x84
+#define FWOHCI_INTMASK 0x88
+#define FWOHCI_INTMASKCLR 0x8c
+ fwohcireg_t int_stat; /* 0x80 */
+ fwohcireg_t int_clear; /* 0x84 */
+ fwohcireg_t int_mask; /* 0x88 */
+ fwohcireg_t int_mask_clear; /* 0x8c */
+ fwohcireg_t it_int_stat; /* 0x90 */
+ fwohcireg_t it_int_clear; /* 0x94 */
+ fwohcireg_t it_int_mask; /* 0x98 */
+ fwohcireg_t it_mask_clear; /* 0x9c */
+ fwohcireg_t ir_int_stat; /* 0xa0 */
+ fwohcireg_t ir_int_clear; /* 0xa4 */
+ fwohcireg_t ir_int_mask; /* 0xa8 */
+ fwohcireg_t ir_mask_clear; /* 0xac */
+ fwohcireg_t dummy5[11]; /* dummy 0xb0-d8 */
+ fwohcireg_t fairness; /* fairness control 0xdc */
+ fwohcireg_t link_cntl; /* Chip control 0xe0*/
+ fwohcireg_t link_cntl_clr; /* Chip control clear 0xe4*/
+#define FWOHCI_NODEID 0xe8
+ fwohcireg_t node; /* Node ID 0xe8 */
+#define OHCI_NODE_VALID (1U << 31)
+#define OHCI_NODE_ROOT (1 << 30)
+
+#define OHCI_ASYSRCBUS 1
+
+ fwohcireg_t phy_access; /* PHY cntl 0xec */
+#define PHYDEV_RDDONE (1<<31)
+#define PHYDEV_RDCMD (1<<15)
+#define PHYDEV_WRCMD (1<<14)
+#define PHYDEV_REGADDR 8
+#define PHYDEV_WRDATA 0
+#define PHYDEV_RDADDR 24
+#define PHYDEV_RDDATA 16
+
+ fwohcireg_t cycle_timer; /* Cycle Timer 0xf0 */
+ fwohcireg_t dummy6[3]; /* dummy 0xf4-fc */
+ fwohcireg_t areq_hi; /* Async req. filter hi 0x100 */
+ fwohcireg_t areq_hi_clr; /* Async req. filter hi 0x104 */
+ fwohcireg_t areq_lo; /* Async req. filter lo 0x108 */
+ fwohcireg_t areq_lo_clr; /* Async req. filter lo 0x10c */
+ fwohcireg_t preq_hi; /* Async req. filter hi 0x110 */
+ fwohcireg_t preq_hi_clr; /* Async req. filter hi 0x114 */
+ fwohcireg_t preq_lo; /* Async req. filter lo 0x118 */
+ fwohcireg_t preq_lo_clr; /* Async req. filter lo 0x11c */
+
+ fwohcireg_t pys_upper; /* Physical Upper bound 0x120 */
+
+ fwohcireg_t dummy7[23]; /* dummy 0x124-0x17c */
+
+ /* 0x180, 0x184, 0x188, 0x18c */
+ /* 0x190, 0x194, 0x198, 0x19c */
+ /* 0x1a0, 0x1a4, 0x1a8, 0x1ac */
+ /* 0x1b0, 0x1b4, 0x1b8, 0x1bc */
+ /* 0x1c0, 0x1c4, 0x1c8, 0x1cc */
+ /* 0x1d0, 0x1d4, 0x1d8, 0x1dc */
+ /* 0x1e0, 0x1e4, 0x1e8, 0x1ec */
+ /* 0x1f0, 0x1f4, 0x1f8, 0x1fc */
+ struct ohci_dma dma_ch[0x4];
+
+ /* 0x200, 0x204, 0x208, 0x20c */
+ /* 0x210, 0x204, 0x208, 0x20c */
+ struct ohci_itdma dma_itch[0x20];
+
+ /* 0x400, 0x404, 0x408, 0x40c */
+ /* 0x410, 0x404, 0x408, 0x40c */
+ struct ohci_dma dma_irch[0x20];
+};
+
+#define OHCI_CNTL_CYCSRC (0x1 << 22)
+#define OHCI_CNTL_CYCMTR (0x1 << 21)
+#define OHCI_CNTL_CYCTIMER (0x1 << 20)
+#define OHCI_CNTL_PHYPKT (0x1 << 10)
+#define OHCI_CNTL_SID (0x1 << 9)
+
+#define OHCI_INT_DMA_ATRQ (0x1 << 0)
+#define OHCI_INT_DMA_ATRS (0x1 << 1)
+#define OHCI_INT_DMA_ARRQ (0x1 << 2)
+#define OHCI_INT_DMA_ARRS (0x1 << 3)
+#define OHCI_INT_DMA_PRRQ (0x1 << 4)
+#define OHCI_INT_DMA_PRRS (0x1 << 5)
+#define OHCI_INT_DMA_IT (0x1 << 6)
+#define OHCI_INT_DMA_IR (0x1 << 7)
+#define OHCI_INT_PW_ERR (0x1 << 8)
+#define OHCI_INT_LR_ERR (0x1 << 9)
+
+#define OHCI_INT_PHY_SID (0x1 << 16)
+#define OHCI_INT_PHY_BUS_R (0x1 << 17)
+
+#define OHCI_INT_REG_FAIL (0x1 << 18)
+
+#define OHCI_INT_PHY_INT (0x1 << 19)
+#define OHCI_INT_CYC_START (0x1 << 20)
+#define OHCI_INT_CYC_64SECOND (0x1 << 21)
+#define OHCI_INT_CYC_LOST (0x1 << 22)
+#define OHCI_INT_CYC_ERR (0x1 << 23)
+
+#define OHCI_INT_ERR (0x1 << 24)
+#define OHCI_INT_CYC_LONG (0x1 << 25)
+#define OHCI_INT_PHY_REG (0x1 << 26)
+
+#define OHCI_INT_EN (0x1 << 31)
+
+#define IP_CHANNELS 0x0234
+#define FWOHCI_MAXREC 2048
+
+#define OHCI_ISORA 0x02
+#define OHCI_ISORB 0x04
+
+#define FWOHCITCODE_PHY 0xe
diff --git a/stand/i386/libi386/Makefile b/stand/i386/libi386/Makefile
new file mode 100644
index 0000000..8d65513
--- /dev/null
+++ b/stand/i386/libi386/Makefile
@@ -0,0 +1,61 @@
+# $FreeBSD$
+
+HAVE_GPT= yes
+HAVE_GELI= yes
+
+.include <bsd.init.mk>
+
+LIB= i386
+INTERNALLIB=
+
+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 multiboot.c multiboot_tramp.S relocater_tramp.S \
+ i386_copy.c i386_module.c nullconsole.c pxe.c pxetramp.s \
+ smbios.c time.c vidconsole.c amd64_tramp.S spinconsole.c
+.PATH: ${ZFSSRC}
+SRCS+= devicename_stubs.c
+
+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
+
+.if !defined(BOOT_HIDE_SERIAL_NUMBERS)
+# Export serial numbers, UUID, and asset tag from loader.
+CFLAGS+= -DSMBIOS_SERIAL_NUMBERS
+.if defined(BOOT_LITTLE_ENDIAN_UUID)
+# Use little-endian UUID format as defined in SMBIOS 2.6.
+CFLAGS+= -DSMBIOS_LITTLE_ENDIAN_UUID
+.elif defined(BOOT_NETWORK_ENDIAN_UUID)
+# Use network-endian UUID format for backward compatibility.
+CFLAGS+= -DSMBIOS_NETWORK_ENDIAN_UUID
+.endif
+.endif
+
+# Include simple terminal emulation (cons25-compatible)
+CFLAGS+= -DTERM_EMU
+
+# XXX: make alloca() useable
+CFLAGS+= -Dalloca=__builtin_alloca
+
+CFLAGS+= -I${BOOTSRC}/ficl -I${BOOTSRC}/ficl/i386 \
+ -I${LDRSRC} -I${BOOTSRC}/i386/common \
+ -I${BTXLIB} \
+ -I${SYSDIR}/contrib/dev/acpica/include
+
+# Handle FreeBSD specific %b and %D printf format specifiers
+CFLAGS+= ${FORMAT_EXTENSIONS}
+
+.include <bsd.lib.mk>
+
+# XXX: clang integrated-as doesn't grok .codeNN directives yet
+CFLAGS.amd64_tramp.S= ${CLANG_NO_IAS}
+CFLAGS.multiboot_tramp.S= ${CLANG_NO_IAS}
diff --git a/stand/i386/libi386/Makefile.depend b/stand/i386/libi386/Makefile.depend
new file mode 100644
index 0000000..df20c96
--- /dev/null
+++ b/stand/i386/libi386/Makefile.depend
@@ -0,0 +1,14 @@
+# $FreeBSD$
+# Autogenerated - do NOT edit!
+
+DIRDEPS = \
+ include \
+ include/xlocale \
+ lib/libmd \
+
+
+.include <dirdeps.mk>
+
+.if ${DEP_RELDIR} == ${_DEP_RELDIR}
+# local dependencies - needed for -jN in clean tree
+.endif
diff --git a/stand/i386/libi386/amd64_tramp.S b/stand/i386/libi386/amd64_tramp.S
new file mode 100644
index 0000000..6359b90
--- /dev/null
+++ b/stand/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, %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 compatibility 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/stand/i386/libi386/biosacpi.c b/stand/i386/libi386/biosacpi.c
new file mode 100644
index 0000000..8167fca
--- /dev/null
+++ b/stand/i386/libi386/biosacpi.c
@@ -0,0 +1,144 @@
+/*-
+ * 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 <btxv86.h>
+#include "libi386.h"
+
+#include "platform/acfreebsd.h"
+#include "acconfig.h"
+#define ACPI_SYSTEM_XFACE
+#include "actypes.h"
+#include "actbl.h"
+
+/*
+ * Detect ACPI and export information about the ACPI BIOS into the
+ * environment.
+ */
+
+static ACPI_TABLE_RSDP *biosacpi_find_rsdp(void);
+static ACPI_TABLE_RSDP *biosacpi_search_rsdp(char *base, int length);
+
+#define RSDP_CHECKSUM_LENGTH 20
+
+void
+biosacpi_detect(void)
+{
+ ACPI_TABLE_RSDP *rsdp;
+ char buf[24];
+ int revision;
+
+ /* locate and validate the RSDP */
+ if ((rsdp = biosacpi_find_rsdp()) == NULL)
+ return;
+
+ /*
+ * Report the RSDP to the kernel. While this can be found with
+ * a BIOS boot, the RSDP may be elsewhere when booted from UEFI.
+ * The old code used the 'hints' method to communite this to
+ * the kernel. However, while convenient, the 'hints' method
+ * is fragile and does not work when static hints are compiled
+ * into the kernel. Instead, move to setting different tunables
+ * that start with acpi. The old 'hints' can be removed before
+ * we branch for FreeBSD 12.
+ */
+ sprintf(buf, "0x%08x", VTOP(rsdp));
+ setenv("hint.acpi.0.rsdp", buf, 1);
+ setenv("acpi.rsdp", buf, 1);
+ revision = rsdp->Revision;
+ if (revision == 0)
+ revision = 1;
+ sprintf(buf, "%d", revision);
+ setenv("hint.acpi.0.revision", buf, 1);
+ setenv("acpi.revision", buf, 1);
+ strncpy(buf, rsdp->OemId, sizeof(rsdp->OemId));
+ buf[sizeof(rsdp->OemId)] = '\0';
+ setenv("hint.acpi.0.oem", buf, 1);
+ setenv("acpi.oem", buf, 1);
+ sprintf(buf, "0x%08x", rsdp->RsdtPhysicalAddress);
+ setenv("hint.acpi.0.rsdt", buf, 1);
+ setenv("acpi.rsdt", buf, 1);
+ if (revision >= 2) {
+ /* XXX extended checksum? */
+ sprintf(buf, "0x%016llx", rsdp->XsdtPhysicalAddress);
+ setenv("hint.acpi.0.xsdt", buf, 1);
+ setenv("acpi.xsdt", buf, 1);
+ sprintf(buf, "%d", rsdp->Length);
+ setenv("hint.acpi.0.xsdt_length", buf, 1);
+ setenv("acpi.xsdt_length", buf, 1);
+ }
+}
+
+/*
+ * Find the RSDP in low memory. See section 5.2.2 of the ACPI spec.
+ */
+static ACPI_TABLE_RSDP *
+biosacpi_find_rsdp(void)
+{
+ ACPI_TABLE_RSDP *rsdp;
+ uint16_t *addr;
+
+ /* EBDA is the 1 KB addressed by the 16 bit pointer at 0x40E. */
+ addr = (uint16_t *)PTOV(0x40E);
+ if ((rsdp = biosacpi_search_rsdp((char *)(*addr << 4), 0x400)) != NULL)
+ return (rsdp);
+
+ /* Check the upper memory BIOS space, 0xe0000 - 0xfffff. */
+ if ((rsdp = biosacpi_search_rsdp((char *)0xe0000, 0x20000)) != NULL)
+ return (rsdp);
+
+ return (NULL);
+}
+
+static ACPI_TABLE_RSDP *
+biosacpi_search_rsdp(char *base, int length)
+{
+ ACPI_TABLE_RSDP *rsdp;
+ u_int8_t *cp, sum;
+ int ofs, idx;
+
+ /* search on 16-byte boundaries */
+ for (ofs = 0; ofs < length; ofs += 16) {
+ rsdp = (ACPI_TABLE_RSDP *)PTOV(base + ofs);
+
+ /* compare signature, validate checksum */
+ if (!strncmp(rsdp->Signature, ACPI_SIG_RSDP, strlen(ACPI_SIG_RSDP))) {
+ cp = (u_int8_t *)rsdp;
+ sum = 0;
+ for (idx = 0; idx < RSDP_CHECKSUM_LENGTH; idx++)
+ sum += *(cp + idx);
+ if (sum != 0)
+ continue;
+ return(rsdp);
+ }
+ }
+ return(NULL);
+}
diff --git a/stand/i386/libi386/bioscd.c b/stand/i386/libi386/bioscd.c
new file mode 100644
index 0000000..2e8fc3b
--- /dev/null
+++ b/stand/i386/libi386/bioscd.c
@@ -0,0 +1,452 @@
+/*-
+ * 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 <stdarg.h>
+
+#include <bootstrap.h>
+#include <btxv86.h>
+#include <edd.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;
+ int bc_open; /* reference counter */
+ void *bc_bcache; /* buffer cache data */
+} bcinfo [MAXBCDEV];
+static int nbcinfo = 0;
+
+#define BC(dev) (bcinfo[(dev)->d_unit])
+
+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 int 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++;
+ bcache_add_dev(nbcinfo); /* register cd device in bcache */
+ return(0);
+}
+
+/*
+ * Print information about disks
+ */
+static int
+bc_print(int verbose)
+{
+ char line[80];
+ int i, ret = 0;
+
+ if (nbcinfo == 0)
+ return (0);
+
+ printf("%s devices:", bioscd.dv_name);
+ if ((ret = pager_output("\n")) != 0)
+ return (ret);
+
+ for (i = 0; i < nbcinfo; i++) {
+ snprintf(line, sizeof(line), " cd%d: Device 0x%x\n", i,
+ bcinfo[i].bc_sp.sp_devicespec);
+ if ((ret = pager_output(line)) != 0)
+ break;
+ }
+ return (ret);
+}
+
+/*
+ * Attempt to open the disk described by (dev) for use by (f).
+ */
+static int
+bc_open(struct open_file *f, ...)
+{
+ va_list ap;
+ struct i386_devdesc *dev;
+
+ va_start(ap, f);
+ dev = va_arg(ap, struct i386_devdesc *);
+ va_end(ap);
+ if (dev->d_unit >= nbcinfo) {
+ DEBUG("attempt to open nonexistent disk");
+ return(ENXIO);
+ }
+
+ BC(dev).bc_open++;
+ if (BC(dev).bc_bcache == NULL)
+ BC(dev).bc_bcache = bcache_allocate();
+ return(0);
+}
+
+static int
+bc_close(struct open_file *f)
+{
+ struct i386_devdesc *dev;
+
+ dev = (struct i386_devdesc *)f->f_devdata;
+ BC(dev).bc_open--;
+ if (BC(dev).bc_open == 0) {
+ bcache_free(BC(dev).bc_bcache);
+ BC(dev).bc_bcache = NULL;
+ }
+ return(0);
+}
+
+static int
+bc_strategy(void *devdata, int rw, daddr_t dblk, size_t size,
+ char *buf, size_t *rsize)
+{
+ struct bcache_devdata bcd;
+ struct i386_devdesc *dev;
+
+ dev = (struct i386_devdesc *)devdata;
+ bcd.dv_strategy = bc_realstrategy;
+ bcd.dv_devdata = devdata;
+ bcd.dv_cache = BC(dev).bc_bcache;
+
+ return (bcache_strategy(&bcd, rw, dblk, size, buf, rsize));
+}
+
+static int
+bc_realstrategy(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_MASK) != F_READ)
+ return(EROFS);
+ dev = (struct i386_devdesc *)devdata;
+ unit = dev->d_unit;
+ blks = size / BIOSCD_SECSIZE;
+ if (dblk % (BIOSCD_SECSIZE / DEV_BSIZE) != 0)
+ return (EINVAL);
+ dblk /= (BIOSCD_SECSIZE / DEV_BSIZE);
+ DEBUG("read %d from %lld to %p", blks, dblk, buf);
+
+ if (rsize)
+ *rsize = 0;
+ if ((blks = bc_read(unit, dblk, blks, buf)) < 0) {
+ DEBUG("read error");
+ return (EIO);
+ } else {
+ if (size / BIOSCD_SECSIZE > blks) {
+ if (rsize)
+ *rsize = blks * BIOSCD_SECSIZE;
+ return (0);
+ }
+ }
+#ifdef BD_SUPPORT_FRAGS
+ DEBUG("frag read %d from %lld+%d to %p",
+ fragsize, dblk, blks, buf + (blks * BIOSCD_SECSIZE));
+ if (fragsize && bc_read(unit, dblk + blks, 1, fragbuf) != 1) {
+ if (blks) {
+ if (rsize)
+ *rsize = blks * BIOSCD_SECSIZE;
+ return (0);
+ }
+ DEBUG("frag read error");
+ return(EIO);
+ }
+ bcopy(fragbuf, buf + (blks * BIOSCD_SECSIZE), fragsize);
+#endif
+ if (rsize)
+ *rsize = size;
+ return (0);
+}
+
+/* return negative value for an error, otherwise blocks read */
+static int
+bc_read(int unit, daddr_t dblk, int blks, caddr_t dest)
+{
+ u_int maxfer, resid, result, retry, x;
+ caddr_t bbuf, p, xp;
+ static struct edd_packet packet;
+ 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);
+
+ /* Decide whether we have to bounce */
+ if (VTOP(dest) >> 20 != 0) {
+ /*
+ * The destination buffer is above first 1MB of
+ * physical memory so we have to arrange a suitable
+ * bounce buffer.
+ */
+ x = V86_IO_BUFFER_SIZE / BIOSCD_SECSIZE;
+ x = min(x, (unsigned)blks);
+ bbuf = PTOV(V86_IO_BUFFER);
+ maxfer = x;
+ } else {
+ bbuf = NULL;
+ maxfer = 0;
+ }
+
+ biosdev = bc_unit2bios(unit);
+ resid = blks;
+ p = dest;
+
+ while (resid > 0) {
+ if (bbuf)
+ xp = bbuf;
+ else
+ xp = p;
+ x = resid;
+ if (maxfer > 0)
+ x = min(x, maxfer);
+
+ /*
+ * 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.len = sizeof(struct edd_packet);
+ packet.count = x;
+ packet.off = VTOPOFF(xp);
+ packet.seg = VTOPSEG(xp);
+ packet.lba = dblk;
+ v86.ctl = V86_FLAGS;
+ v86.addr = 0x13;
+ v86.eax = 0x4200;
+ v86.edx = biosdev;
+ v86.ds = VTOPSEG(&packet);
+ v86.esi = VTOPOFF(&packet);
+ v86int();
+ result = V86_CY(v86.efl);
+ if (result == 0)
+ break;
+ /* fall back to 1 sector read */
+ x = 1;
+ }
+
+#ifdef DISK_DEBUG
+ error = (v86.eax >> 8) & 0xff;
+#endif
+ DEBUG("%d sectors from %lld to %p (0x%x) %s", x, dblk, p,
+ VTOP(p), result ? "failed" : "ok");
+ DEBUG("unit %d status 0x%x", unit, error);
+
+ /* still an error? break off */
+ if (result != 0)
+ break;
+
+ if (bbuf != NULL)
+ bcopy(bbuf, p, x * BIOSCD_SECSIZE);
+ p += (x * BIOSCD_SECSIZE);
+ dblk += x;
+ resid -= x;
+ }
+
+/* hexdump(dest, (blks * BIOSCD_SECSIZE)); */
+
+ if (blks - resid == 0)
+ return (-1); /* read failed */
+
+ return (blks - resid);
+}
+
+/*
+ * 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_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, unit, 0);
+ DEBUG("dev is 0x%x\n", rootdev);
+ return(rootdev);
+}
diff --git a/stand/i386/libi386/biosdisk.c b/stand/i386/libi386/biosdisk.c
new file mode 100644
index 0000000..a2147e2
--- /dev/null
+++ b/stand/i386/libi386/biosdisk.c
@@ -0,0 +1,1013 @@
+/*-
+ * Copyright (c) 1998 Michael Smith <msmith@freebsd.org>
+ * Copyright (c) 2012 Andrey V. Elsukov <ae@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 <sys/disk.h>
+#include <sys/limits.h>
+#include <stand.h>
+#include <machine/bootinfo.h>
+#include <stdarg.h>
+
+#include <bootstrap.h>
+#include <btxv86.h>
+#include <edd.h>
+#include "disk.h"
+#include "libi386.h"
+
+#ifdef LOADER_GELI_SUPPORT
+#include "cons.h"
+#include "drv.h"
+#include "gpt.h"
+#include "part.h"
+#include <uuid.h>
+struct pentry {
+ struct ptable_entry part;
+ uint64_t flags;
+ union {
+ uint8_t bsd;
+ uint8_t mbr;
+ uuid_t gpt;
+ uint16_t vtoc8;
+ } type;
+ STAILQ_ENTRY(pentry) entry;
+};
+struct ptable {
+ enum ptable_type type;
+ uint16_t sectorsize;
+ uint64_t sectors;
+
+ STAILQ_HEAD(, pentry) entries;
+};
+
+#include "geliboot.c"
+#endif /* LOADER_GELI_SUPPORT */
+
+CTASSERT(sizeof(struct i386_devdesc) >= sizeof(struct disk_devdesc));
+
+#define BIOS_NUMDRIVES 0x475
+#define BIOSDISK_SECSIZE 512
+#define BUFSIZE (1 * BIOSDISK_SECSIZE)
+
+#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
+
+/*
+ * List of BIOS devices, translation from disk unit number to
+ * BIOS unit number.
+ */
+static struct bdinfo
+{
+ int bd_unit; /* BIOS unit number */
+ int bd_cyl; /* BIOS geometry */
+ int bd_hds;
+ int bd_sec;
+ int bd_flags;
+#define BD_MODEINT13 0x0000
+#define BD_MODEEDD1 0x0001
+#define BD_MODEEDD3 0x0002
+#define BD_MODEMASK 0x0003
+#define BD_FLOPPY 0x0004
+ int bd_type; /* BIOS 'drive type' (floppy only) */
+ uint16_t bd_sectorsize; /* Sector size */
+ uint64_t bd_sectors; /* Disk size */
+ int bd_open; /* reference counter */
+ void *bd_bcache; /* buffer cache data */
+} bdinfo [MAXBDDEV];
+static int nbdinfo = 0;
+
+#define BD(dev) (bdinfo[(dev)->d_unit])
+
+static int bd_read(struct disk_devdesc *dev, daddr_t dblk, int blks,
+ caddr_t dest);
+static int bd_write(struct disk_devdesc *dev, daddr_t dblk, int blks,
+ caddr_t dest);
+static int bd_int13probe(struct bdinfo *bd);
+
+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 int bd_ioctl(struct open_file *f, u_long cmd, void *data);
+static int bd_print(int verbose);
+
+#ifdef LOADER_GELI_SUPPORT
+enum isgeli {
+ ISGELI_UNKNOWN,
+ ISGELI_NO,
+ ISGELI_YES
+};
+static enum isgeli geli_status[MAXBDDEV][MAXTBLENTS];
+
+int bios_read(void *vdev __unused, struct dsk *priv, off_t off, char *buf,
+ size_t bytes);
+#endif /* LOADER_GELI_SUPPORT */
+
+struct devsw biosdisk = {
+ "disk",
+ DEVT_DISK,
+ bd_init,
+ bd_strategy,
+ bd_open,
+ bd_close,
+ bd_ioctl,
+ bd_print,
+ NULL
+};
+
+/*
+ * 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;
+
+#ifdef LOADER_GELI_SUPPORT
+ geli_init();
+#endif
+ /* sequence 0, 0x80 */
+ for (base = 0; base <= 0x80; base += 0x80) {
+ for (unit = base; (nbdinfo < MAXBDDEV); unit++) {
+#ifndef VIRTUALBOX
+ /*
+ * Check the BIOS equipment list for number
+ * of fixed disks.
+ */
+ if(base == 0x80 &&
+ (nfd >= *(unsigned char *)PTOV(BIOS_NUMDRIVES)))
+ break;
+#endif
+ bdinfo[nbdinfo].bd_open = 0;
+ bdinfo[nbdinfo].bd_bcache = NULL;
+ 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++;
+ }
+ }
+ bcache_add_dev(nbdinfo);
+ return(0);
+}
+
+/*
+ * Try to detect a device supported by the legacy int13 BIOS
+ */
+static int
+bd_int13probe(struct bdinfo *bd)
+{
+ struct edd_params params;
+ int ret = 1; /* assume success */
+
+ v86.ctl = V86_FLAGS;
+ v86.addr = 0x13;
+ v86.eax = 0x800;
+ v86.edx = bd->bd_unit;
+ v86int();
+
+ /* Don't error out if we get bad sector number, try EDD as well */
+ if (V86_CY(v86.efl) || /* carry set */
+ (v86.edx & 0xff) <= (unsigned)(bd->bd_unit & 0x7f)) /* unit # bad */
+ return (0); /* skip device */
+
+ if ((v86.ecx & 0x3f) == 0) /* absurd sector number */
+ ret = 0; /* set error */
+
+ /* Convert max cyl # -> # of cylinders */
+ bd->bd_cyl = ((v86.ecx & 0xc0) << 2) + ((v86.ecx & 0xff00) >> 8) + 1;
+ /* Convert max head # -> # of heads */
+ bd->bd_hds = ((v86.edx & 0xff00) >> 8) + 1;
+ bd->bd_sec = v86.ecx & 0x3f;
+ bd->bd_type = v86.ebx & 0xff;
+ bd->bd_flags |= BD_MODEINT13;
+
+ /* Calculate sectors count from the geometry */
+ bd->bd_sectors = bd->bd_cyl * bd->bd_hds * bd->bd_sec;
+ bd->bd_sectorsize = BIOSDISK_SECSIZE;
+ DEBUG("unit 0x%x geometry %d/%d/%d", bd->bd_unit, bd->bd_cyl,
+ bd->bd_hds, bd->bd_sec);
+
+ /* Determine if we can use EDD with this device. */
+ v86.ctl = V86_FLAGS;
+ v86.addr = 0x13;
+ v86.eax = 0x4100;
+ v86.edx = bd->bd_unit;
+ v86.ebx = 0x55aa;
+ v86int();
+ if (V86_CY(v86.efl) || /* carry set */
+ (v86.ebx & 0xffff) != 0xaa55 || /* signature */
+ (v86.ecx & EDD_INTERFACE_FIXED_DISK) == 0)
+ return (ret); /* return code from int13 AH=08 */
+
+ /* EDD supported */
+ bd->bd_flags |= BD_MODEEDD1;
+ if ((v86.eax & 0xff00) >= 0x3000)
+ bd->bd_flags |= BD_MODEEDD3;
+ /* Get disk params */
+ params.len = sizeof(struct edd_params);
+ v86.ctl = V86_FLAGS;
+ v86.addr = 0x13;
+ v86.eax = 0x4800;
+ v86.edx = bd->bd_unit;
+ v86.ds = VTOPSEG(&params);
+ v86.esi = VTOPOFF(&params);
+ v86int();
+ if (!V86_CY(v86.efl)) {
+ uint64_t total;
+
+ /*
+ * Sector size must be a multiple of 512 bytes.
+ * An alternate test would be to check power of 2,
+ * powerof2(params.sector_size).
+ */
+ if (params.sector_size % BIOSDISK_SECSIZE)
+ bd->bd_sectorsize = BIOSDISK_SECSIZE;
+ else
+ bd->bd_sectorsize = params.sector_size;
+
+ total = bd->bd_sectorsize * params.sectors;
+ if (params.sectors != 0) {
+ /* Only update if we did not overflow. */
+ if (total > params.sectors)
+ bd->bd_sectors = params.sectors;
+ }
+
+ total = (uint64_t)params.cylinders *
+ params.heads * params.sectors_per_track;
+ if (bd->bd_sectors < total)
+ bd->bd_sectors = total;
+
+ ret = 1;
+ }
+ DEBUG("unit 0x%x flags %x, sectors %llu, sectorsize %u",
+ bd->bd_unit, bd->bd_flags, bd->bd_sectors, bd->bd_sectorsize);
+ return (ret);
+}
+
+/*
+ * Print information about disks
+ */
+static int
+bd_print(int verbose)
+{
+ static char line[80];
+ struct disk_devdesc dev;
+ int i, ret = 0;
+
+ if (nbdinfo == 0)
+ return (0);
+
+ printf("%s devices:", biosdisk.dv_name);
+ if ((ret = pager_output("\n")) != 0)
+ return (ret);
+
+ for (i = 0; i < nbdinfo; i++) {
+ snprintf(line, sizeof(line),
+ " disk%d: BIOS drive %c (%ju X %u):\n", i,
+ (bdinfo[i].bd_unit < 0x80) ? ('A' + bdinfo[i].bd_unit):
+ ('C' + bdinfo[i].bd_unit - 0x80),
+ (uintmax_t)bdinfo[i].bd_sectors,
+ bdinfo[i].bd_sectorsize);
+ if ((ret = pager_output(line)) != 0)
+ break;
+ dev.d_dev = &biosdisk;
+ dev.d_unit = i;
+ dev.d_slice = -1;
+ dev.d_partition = -1;
+ if (disk_open(&dev,
+ bdinfo[i].bd_sectorsize * bdinfo[i].bd_sectors,
+ bdinfo[i].bd_sectorsize) == 0) {
+ snprintf(line, sizeof(line), " disk%d", i);
+ ret = disk_print(&dev, line, verbose);
+ disk_close(&dev);
+ if (ret != 0)
+ return (ret);
+ }
+ }
+ return (ret);
+}
+
+/*
+ * 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, ...)
+{
+ struct disk_devdesc *dev, rdev;
+ struct disk_devdesc disk;
+ int err, g_err;
+ va_list ap;
+ uint64_t size;
+
+ va_start(ap, f);
+ dev = va_arg(ap, struct disk_devdesc *);
+ va_end(ap);
+
+ if (dev->d_unit < 0 || dev->d_unit >= nbdinfo)
+ return (EIO);
+ BD(dev).bd_open++;
+ if (BD(dev).bd_bcache == NULL)
+ BD(dev).bd_bcache = bcache_allocate();
+
+ /*
+ * Read disk size from partition.
+ * This is needed to work around buggy BIOS systems returning
+ * wrong (truncated) disk media size.
+ * During bd_probe() we tested if the mulitplication of bd_sectors
+ * would overflow so it should be safe to perform here.
+ */
+ disk.d_dev = dev->d_dev;
+ disk.d_type = dev->d_type;
+ disk.d_unit = dev->d_unit;
+ disk.d_opendata = NULL;
+ disk.d_slice = -1;
+ disk.d_partition = -1;
+ disk.d_offset = 0;
+ if (disk_open(&disk, BD(dev).bd_sectors * BD(dev).bd_sectorsize,
+ BD(dev).bd_sectorsize) == 0) {
+
+ if (disk_ioctl(&disk, DIOCGMEDIASIZE, &size) == 0) {
+ size /= BD(dev).bd_sectorsize;
+ if (size > BD(dev).bd_sectors)
+ BD(dev).bd_sectors = size;
+ }
+ disk_close(&disk);
+ }
+
+ err = disk_open(dev, BD(dev).bd_sectors * BD(dev).bd_sectorsize,
+ BD(dev).bd_sectorsize);
+
+#ifdef LOADER_GELI_SUPPORT
+ static char gelipw[GELI_PW_MAXLEN];
+ char *passphrase;
+
+ if (err)
+ return (err);
+
+ /* if we already know there is no GELI, skip the rest */
+ if (geli_status[dev->d_unit][dev->d_slice] != ISGELI_UNKNOWN)
+ return (err);
+
+ struct dsk dskp;
+ struct ptable *table = NULL;
+ struct ptable_entry part;
+ struct pentry *entry;
+ int geli_part = 0;
+
+ dskp.drive = bd_unit2bios(dev->d_unit);
+ dskp.type = dev->d_type;
+ dskp.unit = dev->d_unit;
+ dskp.slice = dev->d_slice;
+ dskp.part = dev->d_partition;
+ dskp.start = dev->d_offset;
+
+ memcpy(&rdev, dev, sizeof(rdev));
+ /* to read the GPT table, we need to read the first sector */
+ rdev.d_offset = 0;
+ /* We need the LBA of the end of the partition */
+ table = ptable_open(&rdev, BD(dev).bd_sectors,
+ BD(dev).bd_sectorsize, ptblread);
+ if (table == NULL) {
+ DEBUG("Can't read partition table");
+ /* soft failure, return the exit status of disk_open */
+ return (err);
+ }
+
+ if (table->type == PTABLE_GPT)
+ dskp.part = 255;
+
+ STAILQ_FOREACH(entry, &table->entries, entry) {
+ dskp.slice = entry->part.index;
+ dskp.start = entry->part.start;
+ if (is_geli(&dskp) == 0) {
+ geli_status[dev->d_unit][dskp.slice] = ISGELI_YES;
+ return (0);
+ }
+ if (geli_taste(bios_read, &dskp,
+ entry->part.end - entry->part.start) == 0) {
+ if (geli_havekey(&dskp) == 0) {
+ geli_status[dev->d_unit][dskp.slice] = ISGELI_YES;
+ geli_part++;
+ continue;
+ }
+ if ((passphrase = getenv("kern.geom.eli.passphrase"))
+ != NULL) {
+ /* Use the cached passphrase */
+ bcopy(passphrase, &gelipw, GELI_PW_MAXLEN);
+ }
+ if (geli_passphrase(&gelipw, dskp.unit, 'p',
+ (dskp.slice > 0 ? dskp.slice : dskp.part),
+ &dskp) == 0) {
+ setenv("kern.geom.eli.passphrase", &gelipw, 1);
+ bzero(gelipw, sizeof(gelipw));
+ geli_status[dev->d_unit][dskp.slice] = ISGELI_YES;
+ geli_part++;
+ continue;
+ }
+ } else
+ geli_status[dev->d_unit][dskp.slice] = ISGELI_NO;
+ }
+
+ /* none of the partitions on this disk have GELI */
+ if (geli_part == 0) {
+ /* found no GELI */
+ geli_status[dev->d_unit][dev->d_slice] = ISGELI_NO;
+ }
+#endif /* LOADER_GELI_SUPPORT */
+
+ return (err);
+}
+
+static int
+bd_close(struct open_file *f)
+{
+ struct disk_devdesc *dev;
+
+ dev = (struct disk_devdesc *)f->f_devdata;
+ BD(dev).bd_open--;
+ if (BD(dev).bd_open == 0) {
+ bcache_free(BD(dev).bd_bcache);
+ BD(dev).bd_bcache = NULL;
+ }
+ return (disk_close(dev));
+}
+
+static int
+bd_ioctl(struct open_file *f, u_long cmd, void *data)
+{
+ struct disk_devdesc *dev;
+ int rc;
+
+ dev = (struct disk_devdesc *)f->f_devdata;
+
+ rc = disk_ioctl(dev, cmd, data);
+ if (rc != ENOTTY)
+ return (rc);
+
+ switch (cmd) {
+ case DIOCGSECTORSIZE:
+ *(u_int *)data = BD(dev).bd_sectorsize;
+ break;
+ case DIOCGMEDIASIZE:
+ *(uint64_t *)data = BD(dev).bd_sectors * BD(dev).bd_sectorsize;
+ break;
+ default:
+ return (ENOTTY);
+ }
+ return (0);
+}
+
+static int
+bd_strategy(void *devdata, int rw, daddr_t dblk, size_t size,
+ char *buf, size_t *rsize)
+{
+ struct bcache_devdata bcd;
+ struct disk_devdesc *dev;
+
+ dev = (struct disk_devdesc *)devdata;
+ bcd.dv_strategy = bd_realstrategy;
+ bcd.dv_devdata = devdata;
+ bcd.dv_cache = BD(dev).bd_bcache;
+ return (bcache_strategy(&bcd, rw, dblk + dev->d_offset,
+ size, buf, rsize));
+}
+
+static int
+bd_realstrategy(void *devdata, int rw, daddr_t dblk, size_t size,
+ char *buf, size_t *rsize)
+{
+ struct disk_devdesc *dev = (struct disk_devdesc *)devdata;
+ uint64_t disk_blocks;
+ int blks, rc;
+#ifdef BD_SUPPORT_FRAGS /* XXX: sector size */
+ char fragbuf[BIOSDISK_SECSIZE];
+ size_t fragsize;
+
+ fragsize = size % BIOSDISK_SECSIZE;
+#else
+ if (size % BD(dev).bd_sectorsize)
+ panic("bd_strategy: %d bytes I/O not multiple of block size", size);
+#endif
+
+ DEBUG("open_disk %p", dev);
+
+ /*
+ * Check the value of the size argument. We do have quite small
+ * heap (64MB), but we do not know good upper limit, so we check against
+ * INT_MAX here. This will also protect us against possible overflows
+ * while translating block count to bytes.
+ */
+ if (size > INT_MAX) {
+ DEBUG("too large read: %zu bytes", size);
+ return (EIO);
+ }
+
+ blks = size / BD(dev).bd_sectorsize;
+ if (dblk > dblk + blks)
+ return (EIO);
+
+ if (rsize)
+ *rsize = 0;
+
+ /* Get disk blocks, this value is either for whole disk or for partition */
+ if (disk_ioctl(dev, DIOCGMEDIASIZE, &disk_blocks)) {
+ /* DIOCGMEDIASIZE does return bytes. */
+ disk_blocks /= BD(dev).bd_sectorsize;
+ } else {
+ /* We should not get here. Just try to survive. */
+ disk_blocks = BD(dev).bd_sectors - dev->d_offset;
+ }
+
+ /* Validate source block address. */
+ if (dblk < dev->d_offset || dblk >= dev->d_offset + disk_blocks)
+ return (EIO);
+
+ /*
+ * Truncate if we are crossing disk or partition end.
+ */
+ if (dblk + blks >= dev->d_offset + disk_blocks) {
+ blks = dev->d_offset + disk_blocks - dblk;
+ size = blks * BD(dev).bd_sectorsize;
+ DEBUG("short read %d", blks);
+ }
+
+ switch (rw & F_MASK) {
+ case F_READ:
+ DEBUG("read %d from %lld to %p", blks, dblk, buf);
+
+ if (blks && (rc = bd_read(dev, dblk, blks, buf))) {
+ /* Filter out floppy controller errors */
+ if (BD(dev).bd_flags != BD_FLOPPY || rc != 0x20) {
+ printf("read %d from %lld to %p, error: 0x%x", blks, dblk,
+ buf, rc);
+ }
+ return (EIO);
+ }
+#ifdef BD_SUPPORT_FRAGS /* XXX: sector size */
+ 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
+ break;
+ case F_WRITE :
+ DEBUG("write %d from %d to %p", blks, dblk, buf);
+
+ if (blks && bd_write(dev, dblk, blks, buf)) {
+ DEBUG("write error");
+ return (EIO);
+ }
+#ifdef BD_SUPPORT_FRAGS
+ if(fragsize) {
+ DEBUG("Attempted to write a frag");
+ return (EIO);
+ }
+#endif
+ break;
+ default:
+ /* DO NOTHING */
+ return (EROFS);
+ }
+
+ if (rsize)
+ *rsize = size;
+ return (0);
+}
+
+static int
+bd_edd_io(struct disk_devdesc *dev, daddr_t dblk, int blks, caddr_t dest,
+ int write)
+{
+ static struct edd_packet packet;
+
+ packet.len = sizeof(struct edd_packet);
+ packet.count = blks;
+ packet.off = VTOPOFF(dest);
+ packet.seg = VTOPSEG(dest);
+ packet.lba = dblk;
+ v86.ctl = V86_FLAGS;
+ v86.addr = 0x13;
+ if (write)
+ /* Should we Write with verify ?? 0x4302 ? */
+ v86.eax = 0x4300;
+ else
+ v86.eax = 0x4200;
+ v86.edx = BD(dev).bd_unit;
+ v86.ds = VTOPSEG(&packet);
+ v86.esi = VTOPOFF(&packet);
+ v86int();
+ if (V86_CY(v86.efl))
+ return (v86.eax >> 8);
+ return (0);
+}
+
+static int
+bd_chs_io(struct disk_devdesc *dev, daddr_t dblk, int blks, caddr_t dest,
+ int write)
+{
+ u_int x, bpc, cyl, hd, sec;
+
+ bpc = BD(dev).bd_sec * BD(dev).bd_hds; /* blocks per cylinder */
+ x = dblk;
+ cyl = x / bpc; /* block # / blocks per cylinder */
+ x %= bpc; /* block offset into cylinder */
+ hd = x / BD(dev).bd_sec; /* offset / blocks per track */
+ sec = x % BD(dev).bd_sec; /* offset into track */
+
+ /* correct sector number for 1-based BIOS numbering */
+ sec++;
+
+ if (cyl > 1023)
+ /* CHS doesn't support cylinders > 1023. */
+ return (1);
+
+ v86.ctl = V86_FLAGS;
+ v86.addr = 0x13;
+ if (write)
+ v86.eax = 0x300 | blks;
+ else
+ v86.eax = 0x200 | blks;
+ v86.ecx = ((cyl & 0xff) << 8) | ((cyl & 0x300) >> 2) | sec;
+ v86.edx = (hd << 8) | BD(dev).bd_unit;
+ v86.es = VTOPSEG(dest);
+ v86.ebx = VTOPOFF(dest);
+ v86int();
+ if (V86_CY(v86.efl))
+ return (v86.eax >> 8);
+ return (0);
+}
+
+static int
+bd_io(struct disk_devdesc *dev, daddr_t dblk, int blks, caddr_t dest, int write)
+{
+ u_int x, sec, result, resid, retry, maxfer;
+ caddr_t p, xp, bbuf;
+
+ /* Just in case some idiot actually tries to read/write -1 blocks... */
+ if (blks < 0)
+ return (-1);
+
+ resid = blks;
+ p = dest;
+
+ /* Decide whether we have to bounce */
+ if (VTOP(dest) >> 20 != 0 || (BD(dev).bd_unit < 0x80 &&
+ (VTOP(dest) >> 16) != (VTOP(dest +
+ blks * BD(dev).bd_sectorsize) >> 16))) {
+
+ /*
+ * There is a 64k physical boundary somewhere in the
+ * destination buffer, or the destination buffer is above
+ * first 1MB of physical memory 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 = V86_IO_BUFFER_SIZE / BD(dev).bd_sectorsize;
+ x = min(x, (unsigned)blks);
+ bbuf = PTOV(V86_IO_BUFFER);
+ maxfer = x; /* limit transfers to bounce region size */
+ } else {
+ bbuf = NULL;
+ maxfer = 0;
+ }
+
+ while (resid > 0) {
+ /*
+ * Play it safe and don't cross track boundaries.
+ * (XXX this is probably unnecessary)
+ */
+ sec = dblk % BD(dev).bd_sec; /* offset into track */
+ x = min(BD(dev).bd_sec - sec, resid);
+ if (maxfer > 0)
+ x = min(x, maxfer); /* fit bounce buffer */
+
+ /* where do we transfer to? */
+ xp = bbuf == NULL ? p : bbuf;
+
+ /*
+ * Put your Data In, Put your Data out,
+ * Put your Data In, and shake it all about
+ */
+ if (write && bbuf != NULL)
+ bcopy(p, bbuf, x * BD(dev).bd_sectorsize);
+
+ /*
+ * 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 = BD(dev).bd_unit;
+ v86int();
+ }
+
+ if (BD(dev).bd_flags & BD_MODEEDD1)
+ result = bd_edd_io(dev, dblk, x, xp, write);
+ else
+ result = bd_chs_io(dev, dblk, x, xp, write);
+ if (result == 0)
+ break;
+ }
+
+ if (write)
+ DEBUG("Write %d sector(s) from %p (0x%x) to %lld %s", x,
+ p, VTOP(p), dblk, result ? "failed" : "ok");
+ else
+ DEBUG("Read %d sector(s) from %lld to %p (0x%x) %s", x,
+ dblk, p, VTOP(p), result ? "failed" : "ok");
+ if (result) {
+ return (result);
+ }
+ if (!write && bbuf != NULL)
+ bcopy(bbuf, p, x * BD(dev).bd_sectorsize);
+ p += (x * BD(dev).bd_sectorsize);
+ dblk += x;
+ resid -= x;
+ }
+
+/* hexdump(dest, (blks * BD(dev).bd_sectorsize)); */
+ return(0);
+}
+
+static int
+bd_read(struct disk_devdesc *dev, daddr_t dblk, int blks, caddr_t dest)
+{
+#ifdef LOADER_GELI_SUPPORT
+ struct dsk dskp;
+ off_t p_off, diff;
+ daddr_t alignlba;
+ int err, n, alignblks;
+ char *tmpbuf;
+
+ /* if we already know there is no GELI, skip the rest */
+ if (geli_status[dev->d_unit][dev->d_slice] != ISGELI_YES)
+ return (bd_io(dev, dblk, blks, dest, 0));
+
+ if (geli_status[dev->d_unit][dev->d_slice] == ISGELI_YES) {
+ /*
+ * Align reads to DEV_GELIBOOT_BSIZE bytes because partial
+ * sectors cannot be decrypted. Round the requested LBA down to
+ * nearest multiple of DEV_GELIBOOT_BSIZE bytes.
+ */
+ alignlba = rounddown2(dblk * BD(dev).bd_sectorsize,
+ DEV_GELIBOOT_BSIZE) / BD(dev).bd_sectorsize;
+ /*
+ * Round number of blocks to read up to nearest multiple of
+ * DEV_GELIBOOT_BSIZE
+ */
+ diff = (dblk - alignlba) * BD(dev).bd_sectorsize;
+ alignblks = roundup2(blks * BD(dev).bd_sectorsize + diff,
+ DEV_GELIBOOT_BSIZE) / BD(dev).bd_sectorsize;
+
+ /*
+ * If the read is rounded up to a larger size, use a temporary
+ * buffer here because the buffer provided by the caller may be
+ * too small.
+ */
+ if (diff == 0) {
+ tmpbuf = dest;
+ } else {
+ tmpbuf = malloc(alignblks * BD(dev).bd_sectorsize);
+ if (tmpbuf == NULL) {
+ return (-1);
+ }
+ }
+
+ err = bd_io(dev, alignlba, alignblks, tmpbuf, 0);
+ if (err)
+ return (err);
+
+ dskp.drive = bd_unit2bios(dev->d_unit);
+ dskp.type = dev->d_type;
+ dskp.unit = dev->d_unit;
+ dskp.slice = dev->d_slice;
+ dskp.part = dev->d_partition;
+ dskp.start = dev->d_offset;
+
+ /* GELI needs the offset relative to the partition start */
+ p_off = alignlba - dskp.start;
+
+ err = geli_read(&dskp, p_off * BD(dev).bd_sectorsize, tmpbuf,
+ alignblks * BD(dev).bd_sectorsize);
+ if (err)
+ return (err);
+
+ if (tmpbuf != dest) {
+ bcopy(tmpbuf + diff, dest, blks * BD(dev).bd_sectorsize);
+ free(tmpbuf);
+ }
+ return (0);
+ }
+#endif /* LOADER_GELI_SUPPORT */
+
+ return (bd_io(dev, dblk, blks, dest, 0));
+}
+
+static int
+bd_write(struct disk_devdesc *dev, daddr_t dblk, int blks, caddr_t dest)
+{
+
+ return (bd_io(dev, dblk, blks, dest, 1));
+}
+
+/*
+ * 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_CY(v86.efl))
+ 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 *d)
+{
+ struct disk_devdesc *dev;
+ int biosdev;
+ int major;
+ int rootdev;
+ char *nip, *cp;
+ int i, unit;
+
+ dev = (struct disk_devdesc *)d;
+ biosdev = bd_unit2bios(dev->d_unit);
+ DEBUG("unit %d BIOS device %d", dev->d_unit, biosdev);
+ if (biosdev == -1) /* not a BIOS device */
+ return(-1);
+ if (disk_open(dev, BD(dev).bd_sectors * BD(dev).bd_sectorsize,
+ BD(dev).bd_sectorsize) != 0) /* oops, not a viable device */
+ return (-1);
+ else
+ disk_close(dev);
+
+ if (biosdev < 0x80) {
+ /* floppy (or emulated floppy) or ATAPI device */
+ if (bdinfo[dev->d_unit].bd_type == DT_ATAPI) {
+ /* is an ATAPI disk */
+ major = WFDMAJOR;
+ } else {
+ /* is a floppy disk */
+ major = FDMAJOR;
+ }
+ } else {
+ /* assume an IDE disk */
+ major = WDMAJOR;
+ }
+ /* default root disk unit number */
+ unit = biosdev & 0x7f;
+
+ /* 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_slice + 1, unit, dev->d_partition);
+ DEBUG("dev is 0x%x\n", rootdev);
+ return(rootdev);
+}
+
+#ifdef LOADER_GELI_SUPPORT
+int
+bios_read(void *vdev __unused, struct dsk *priv, off_t off, char *buf, size_t bytes)
+{
+ struct disk_devdesc dev;
+
+ dev.d_dev = &biosdisk;
+ dev.d_type = priv->type;
+ dev.d_unit = priv->unit;
+ dev.d_slice = priv->slice;
+ dev.d_partition = priv->part;
+ dev.d_offset = priv->start;
+
+ off = off / BD(&dev).bd_sectorsize;
+ /* GELI gives us the offset relative to the partition start */
+ off += dev.d_offset;
+ bytes = bytes / BD(&dev).bd_sectorsize;
+
+ return (bd_io(&dev, off, bytes, buf, 0));
+}
+#endif /* LOADER_GELI_SUPPORT */
diff --git a/stand/i386/libi386/biosmem.c b/stand/i386/libi386/biosmem.c
new file mode 100644
index 0000000..7105757
--- /dev/null
+++ b/stand/i386/libi386/biosmem.c
@@ -0,0 +1,258 @@
+/*-
+ * 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 <machine/pc/bios.h>
+#include "bootstrap.h"
+#include "libi386.h"
+#include "btxv86.h"
+#include "smbios.h"
+
+vm_offset_t memtop, memtop_copyin, high_heap_base;
+uint32_t bios_basemem, bios_extmem, high_heap_size;
+
+static struct bios_smap_xattr smap;
+
+/*
+ * Used to track which method was used to set BIOS memory
+ * regions.
+ */
+static uint8_t b_bios_probed;
+#define B_BASEMEM_E820 0x1
+#define B_BASEMEM_12 0x2
+#define B_EXTMEM_E820 0x4
+#define B_EXTMEM_E801 0x8
+#define B_EXTMEM_8800 0x10
+
+/*
+ * The minimum amount of memory to reserve in bios_extmem for the heap.
+ */
+#define HEAP_MIN (64 * 1024 * 1024)
+
+/*
+ * Products in this list need quirks to detect
+ * memory correctly. You need both maker and product as
+ * reported by smbios.
+ */
+/* e820 might not return useful extended memory */
+#define BQ_DISTRUST_E820_EXTMEM 0x1
+struct bios_getmem_quirks {
+ const char *bios_vendor;
+ const char *maker;
+ const char *product;
+ int quirk;
+};
+
+static struct bios_getmem_quirks quirks[] = {
+ {"coreboot", "Acer", "Peppy", BQ_DISTRUST_E820_EXTMEM},
+ {NULL, NULL, NULL, 0}
+};
+
+static int
+bios_getquirks(void)
+{
+ int i;
+
+ for (i = 0; quirks[i].quirk != 0; ++i) {
+ if (smbios_match(quirks[i].bios_vendor, quirks[i].maker,
+ quirks[i].product))
+ return (quirks[i].quirk);
+ }
+
+ return (0);
+}
+
+void
+bios_getmem(void)
+{
+ uint64_t size;
+
+ /* 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 bios_smap_xattr);
+ v86.edx = SMAP_SIG;
+ v86.es = VTOPSEG(&smap);
+ v86.edi = VTOPOFF(&smap);
+ v86int();
+ if ((V86_CY(v86.efl)) || (v86.eax != SMAP_SIG))
+ break;
+ /* look for a low-memory segment that's large enough */
+ if ((smap.type == SMAP_TYPE_MEMORY) && (smap.base == 0) &&
+ (smap.length >= (512 * 1024))) {
+ bios_basemem = smap.length;
+ b_bios_probed |= B_BASEMEM_E820;
+ }
+
+ /* look for the first segment in 'extended' memory */
+ if ((smap.type == SMAP_TYPE_MEMORY) &&
+ (smap.base == 0x100000) &&
+ !(bios_getquirks() & BQ_DISTRUST_E820_EXTMEM)) {
+ bios_extmem = smap.length;
+ b_bios_probed |= B_EXTMEM_E820;
+ }
+
+ /*
+ * Look for the highest segment in 'extended' memory beyond
+ * 1MB but below 4GB.
+ */
+ if ((smap.type == SMAP_TYPE_MEMORY) &&
+ (smap.base > 0x100000) &&
+ (smap.base < 0x100000000ull)) {
+ size = smap.length;
+
+ /*
+ * If this segment crosses the 4GB boundary,
+ * truncate it.
+ */
+ if (smap.base + size > 0x100000000ull)
+ size = 0x100000000ull - smap.base;
+
+ /*
+ * To make maximum space for the kernel and the modules,
+ * set heap to use highest HEAP_MIN bytes below 4GB.
+ */
+ if (high_heap_base < smap.base && size >= HEAP_MIN) {
+ high_heap_base = smap.base + size - HEAP_MIN;
+ high_heap_size = HEAP_MIN;
+ }
+ }
+ } 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;
+ b_bios_probed |= B_BASEMEM_12;
+ }
+
+ /*
+ * 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_CY(v86.efl))) {
+ /*
+ * Clear high_heap; it may end up overlapping
+ * with the segment we're determining here.
+ * Let the default "steal stuff from top of
+ * bios_extmem" code below pick up on it.
+ */
+ high_heap_size = 0;
+ high_heap_base = 0;
+
+ /*
+ * %cx is the number of 1KiB blocks between 1..16MiB.
+ * It can only be up to 0x3c00; if it's smaller then
+ * there's a PC AT memory hole so we can't treat
+ * it as contiguous.
+ */
+ bios_extmem = (v86.ecx & 0xffff) * 1024;
+ if (bios_extmem == (1024 * 0x3c00))
+ bios_extmem += (v86.edx & 0xffff) * 64 * 1024;
+
+ /* truncate bios_extmem */
+ if (bios_extmem > 0x3ff00000)
+ bios_extmem = 0x3ff00000;
+
+ b_bios_probed |= B_EXTMEM_E801;
+ }
+ }
+ if (bios_extmem == 0) {
+ v86.ctl = 0;
+ v86.addr = 0x15; /* int 0x15 function 0x88 */
+ v86.eax = 0x8800;
+ v86int();
+ bios_extmem = (v86.eax & 0xffff) * 1024;
+ b_bios_probed |= B_EXTMEM_8800;
+ }
+
+ /* Set memtop to actual top of memory */
+ if (high_heap_size != 0) {
+ memtop = memtop_copyin = high_heap_base;
+ } else {
+ memtop = memtop_copyin = 0x100000 + bios_extmem;
+ }
+
+ /*
+ * If we have extended memory and did not find a suitable heap
+ * region in the SMAP, use the last HEAP_MIN of 'extended' memory as a
+ * high heap candidate.
+ */
+ if (bios_extmem >= HEAP_MIN && high_heap_size < HEAP_MIN) {
+ high_heap_size = HEAP_MIN;
+ high_heap_base = memtop - HEAP_MIN;
+ memtop = memtop_copyin = high_heap_base;
+ }
+}
+
+static int
+command_biosmem(int argc, char *argv[])
+{
+ int bq = bios_getquirks();
+
+ printf("bios_basemem: 0x%llx\n", (unsigned long long)bios_basemem);
+ printf("bios_extmem: 0x%llx\n", (unsigned long long)bios_extmem);
+ printf("memtop: 0x%llx\n", (unsigned long long)memtop);
+ printf("high_heap_base: 0x%llx\n", (unsigned long long)high_heap_base);
+ printf("high_heap_size: 0x%llx\n", (unsigned long long)high_heap_size);
+ printf("bios_quirks: 0x%02x", bq);
+ if (bq & BQ_DISTRUST_E820_EXTMEM)
+ printf(" BQ_DISTRUST_E820_EXTMEM");
+ printf("\n");
+ printf("b_bios_probed: 0x%02x", (int)b_bios_probed);
+ if (b_bios_probed & B_BASEMEM_E820)
+ printf(" B_BASEMEM_E820");
+ if (b_bios_probed & B_BASEMEM_12)
+ printf(" B_BASEMEM_12");
+ if (b_bios_probed & B_EXTMEM_E820)
+ printf(" B_EXTMEM_E820");
+ if (b_bios_probed & B_EXTMEM_E801)
+ printf(" B_EXTMEM_E801");
+ if (b_bios_probed & B_EXTMEM_8800)
+ printf(" B_EXTMEM_8800");
+ printf("\n");
+
+ return (CMD_OK);
+}
+
+COMMAND_SET(biosmem, "biosmem", "show BIOS memory setup", command_biosmem);
diff --git a/stand/i386/libi386/biospci.c b/stand/i386/libi386/biospci.c
new file mode 100644
index 0000000..098e30c
--- /dev/null
+++ b/stand/i386/libi386/biospci.c
@@ -0,0 +1,588 @@
+/*-
+ * Copyright (c) 1998 Michael Smith <msmith@freebsd.org>
+ * Copyright (c) 2016 Netflix, 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$");
+
+/*
+ * PnP enumerator using the PCI BIOS.
+ */
+
+#include <stand.h>
+#include <machine/stdarg.h>
+#include <bootstrap.h>
+#include <isapnp.h>
+#include <btxv86.h>
+#include "libi386.h"
+#ifdef BOOT_FORTH
+#include "ficl.h"
+#endif
+
+/*
+ * 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}
+};
+
+static struct pci_progif progif_firewire[] = {
+ {0x10, "OHCI"},
+ {-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_firewire},
+ {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);
+
+struct pnphandler biospcihandler =
+{
+ "PCI BIOS",
+ biospci_enumerate
+};
+static int biospci_version;
+
+#define PCI_BIOS_PRESENT 0xb101
+#define FIND_PCI_DEVICE 0xb102
+#define FIND_PCI_CLASS_CODE 0xb103
+#define GENERATE_SPECIAL_CYCLE 0xb106
+#define READ_CONFIG_BYTE 0xb108
+#define READ_CONFIG_WORD 0xb109
+#define READ_CONFIG_DWORD 0xb10a
+#define WRITE_CONFIG_BYTE 0xb10b
+#define WRITE_CONFIG_WORD 0xb10c
+#define WRITE_CONFIG_DWORD 0xb10d
+#define GET_IRQ_ROUTING_OPTIONS 0xb10e
+#define SET_PCI_IRQ 0xb10f
+
+#define PCI_INT 0x1a
+
+#define PCI_SIGNATURE 0x20494350 /* AKA "PCI " */
+
+void
+biospci_detect(void)
+{
+ uint16_t version, hwcap, maxbus;
+ char buf[24];
+
+ /* Find the PCI BIOS */
+ v86.ctl = V86_FLAGS;
+ v86.addr = PCI_INT;
+ v86.eax = PCI_BIOS_PRESENT;
+ v86.edi = 0x0;
+ v86int();
+
+ /* Check for OK response */
+ if (V86_CY(v86.efl) || ((v86.eax & 0xff00) != 0) ||
+ (v86.edx != PCI_SIGNATURE))
+ return;
+
+ version = v86.ebx & 0xffff;
+ hwcap = v86.eax & 0xff;
+ maxbus = v86.ecx & 0xff;
+#if 0
+ printf("PCI BIOS %d.%d%s%s maxbus %d\n",
+ bcd2bin((version >> 8) & 0xf), bcd2bin(version & 0xf),
+ (hwcap & 1) ? " config1" : "", (hwcap & 2) ? " config2" : "",
+ maxbus);
+#endif
+ sprintf(buf, "%d", bcd2bin((version >> 8) & 0xf));
+ setenv("pcibios.major", buf, 1);
+ sprintf(buf, "%d", bcd2bin(version & 0xf));
+ setenv("pcibios.minor", buf, 1);
+ sprintf(buf, "%d", !!(hwcap & 1));
+ setenv("pcibios.config1", buf, 1);
+ sprintf(buf, "%d", !!(hwcap & 2));
+ setenv("pcibios.config2", buf, 1);
+ sprintf(buf, "%d", maxbus);
+ setenv("pcibios.maxbus", buf, 1);
+ biospci_version = bcd2bin((version >> 8) & 0xf) * 10 + bcd2bin(version & 0xf);
+}
+
+static void
+biospci_enumerate(void)
+{
+ int device_index, err;
+ uint32_t locator, devid;
+ struct pci_class *pc;
+ struct pci_subclass *psc;
+ struct pci_progif *ppi;
+
+ /* 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 */
+ err = biospci_find_devclass((pc->pc_class << 16)
+ + (psc->ps_subclass << 8) + ppi->pi_code,
+ device_index, &locator);
+ if (err != 0)
+ break;
+
+ /* Read the device identifier from the nominated device */
+ err = biospci_read_config(locator, 0, 2, &devid);
+ if (err != 0)
+ break;
+
+ /* We have the device ID, create a PnP object and save everything */
+ 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);
+}
+
+int
+biospci_find_devclass(uint32_t class, int index, uint32_t *locator)
+{
+ v86.ctl = V86_FLAGS;
+ v86.addr = PCI_INT;
+ v86.eax = FIND_PCI_CLASS_CODE;
+ v86.ecx = class;
+ v86.esi = index;
+ v86int();
+
+ /* error */
+ if (V86_CY(v86.efl) || (v86.eax & 0xff00))
+ return (-1);
+
+ *locator = v86.ebx;
+ return (0);
+}
+
+static int
+biospci_find_device(uint32_t devid, int index, uint32_t *locator)
+{
+ v86.ctl = V86_FLAGS;
+ v86.addr = PCI_INT;
+ v86.eax = FIND_PCI_DEVICE;
+ v86.edx = devid & 0xffff; /* EDX - Vendor ID */
+ v86.ecx = (devid >> 16) & 0xffff; /* ECX - Device ID */
+ v86.esi = index;
+ v86int();
+
+ /* error */
+ if (V86_CY(v86.efl) || (v86.eax & 0xff00))
+ return (-1);
+
+ *locator = v86.ebx;
+ return (0);
+}
+/*
+ * Configuration space access methods.
+ * width = 0(byte), 1(word) or 2(dword).
+ */
+int
+biospci_write_config(uint32_t locator, int offset, int width, uint32_t val)
+{
+ v86.ctl = V86_FLAGS;
+ v86.addr = PCI_INT;
+ v86.eax = WRITE_CONFIG_BYTE + width;
+ v86.ebx = locator;
+ v86.edi = offset;
+ v86.ecx = val;
+ v86int();
+
+ /* error */
+ if (V86_CY(v86.efl) || (v86.eax & 0xff00))
+ return (-1);
+
+ return(0);
+}
+
+int
+biospci_read_config(uint32_t locator, int offset, int width, uint32_t *val)
+{
+ v86.ctl = V86_FLAGS;
+ v86.addr = PCI_INT;
+ v86.eax = READ_CONFIG_BYTE + width;
+ v86.ebx = locator;
+ v86.edi = offset;
+ v86int();
+
+ /* error */
+ if (V86_CY(v86.efl) || (v86.eax & 0xff00))
+ return (-1);
+
+ *val = v86.ecx;
+ return (0);
+}
+
+uint32_t
+biospci_locator(int8_t bus, uint8_t device, uint8_t function)
+{
+
+ return ((bus << 8) | ((device & 0x1f) << 3) | (function & 0x7));
+}
+
+/*
+ * Counts the number of instances of devid we have in the system, as least as
+ * far as the PCI BIOS is able to tell.
+ */
+static int
+biospci_count_device_type(uint32_t devid)
+{
+ int i;
+
+ for (i = 0; 1; i++) {
+ v86.ctl = V86_FLAGS;
+ v86.addr = PCI_INT;
+ v86.eax = FIND_PCI_DEVICE;
+ v86.edx = devid & 0xffff; /* EDX - Vendor ID */
+ v86.ecx = (devid >> 16) & 0xffff; /* ECX - Device ID */
+ v86.esi = i;
+ v86int();
+ if (V86_CY(v86.efl) || (v86.eax & 0xff00))
+ break;
+
+ }
+ return i;
+}
+
+#ifdef BOOT_FORTH
+/*
+ * pcibios-device-count (devid -- count)
+ *
+ * Returns the PCI BIOS' count of how many devices matching devid are in the system.
+ * devid is the 32-bit vendor + device.
+ */
+static void
+ficlPciBiosCountDevices(FICL_VM *pVM)
+{
+ uint32_t devid;
+ int i;
+
+ devid = stackPopINT(pVM->pStack);
+
+ i = biospci_count_device_type(devid);
+
+ stackPushINT(pVM->pStack, i);
+}
+
+/*
+ * pcibios-write-config (locator offset width value -- )
+ *
+ * Writes the specified config register.
+ * Locator is bus << 8 | device << 3 | fuction
+ * offset is the pci config register
+ * width is 0 for byte, 1 for word, 2 for dword
+ * value is the value to write
+ */
+static void
+ficlPciBiosWriteConfig(FICL_VM *pVM)
+{
+ uint32_t value, width, offset, locator;
+
+ value = stackPopINT(pVM->pStack);
+ width = stackPopINT(pVM->pStack);
+ offset = stackPopINT(pVM->pStack);
+ locator = stackPopINT(pVM->pStack);
+
+ biospci_write_config(locator, offset, width, value);
+}
+
+/*
+ * pcibios-read-config (locator offset width -- value)
+ *
+ * Reads the specified config register.
+ * Locator is bus << 8 | device << 3 | fuction
+ * offset is the pci config register
+ * width is 0 for byte, 1 for word, 2 for dword
+ * value is the value to read from the register
+ */
+static void
+ficlPciBiosReadConfig(FICL_VM *pVM)
+{
+ uint32_t value, width, offset, locator;
+
+ width = stackPopINT(pVM->pStack);
+ offset = stackPopINT(pVM->pStack);
+ locator = stackPopINT(pVM->pStack);
+
+ biospci_read_config(locator, offset, width, &value);
+
+ stackPushINT(pVM->pStack, value);
+}
+
+/*
+ * pcibios-find-devclass (class index -- locator)
+ *
+ * Finds the index'th instance of class in the pci tree.
+ * must be an exact match.
+ * class is the class to search for.
+ * index 0..N (set to 0, increment until error)
+ *
+ * Locator is bus << 8 | device << 3 | fuction (or -1 on error)
+ */
+static void
+ficlPciBiosFindDevclass(FICL_VM *pVM)
+{
+ uint32_t index, class, locator;
+
+ index = stackPopINT(pVM->pStack);
+ class = stackPopINT(pVM->pStack);
+
+ if (biospci_find_devclass(class, index, &locator))
+ locator = 0xffffffff;
+
+ stackPushINT(pVM->pStack, locator);
+}
+
+/*
+ * pcibios-find-device(devid index -- locator)
+ *
+ * Finds the index'th instance of devid in the pci tree.
+ * must be an exact match.
+ * class is the class to search for.
+ * index 0..N (set to 0, increment until error)
+ *
+ * Locator is bus << 8 | device << 3 | fuction (or -1 on error)
+ */
+static void
+ficlPciBiosFindDevice(FICL_VM *pVM)
+{
+ uint32_t index, devid, locator;
+
+ index = stackPopINT(pVM->pStack);
+ devid = stackPopINT(pVM->pStack);
+
+ if (biospci_find_device(devid, index, &locator))
+ locator = 0xffffffff;
+
+ stackPushINT(pVM->pStack, locator);
+}
+
+/*
+ * pcibios-find-device(bus device function -- locator)
+ *
+ * converts bus, device, function to locator.
+ *
+ * Locator is bus << 8 | device << 3 | fuction
+ */
+static void
+ficlPciBiosLocator(FICL_VM *pVM)
+{
+ uint32_t bus, device, function, locator;
+
+ function = stackPopINT(pVM->pStack);
+ device = stackPopINT(pVM->pStack);
+ bus = stackPopINT(pVM->pStack);
+
+ locator = biospci_locator(bus, device, function);
+
+ stackPushINT(pVM->pStack, locator);
+}
+
+/*
+ * Glue function to add the appropriate forth words to access pci bios
+ * functionality.
+ */
+static void ficlCompilePciBios(FICL_SYSTEM *pSys)
+{
+ FICL_DICT *dp = pSys->dp;
+ assert (dp);
+
+ dictAppendWord(dp, "pcibios-device-count", ficlPciBiosCountDevices, FW_DEFAULT);
+ dictAppendWord(dp, "pcibios-read-config", ficlPciBiosReadConfig, FW_DEFAULT);
+ dictAppendWord(dp, "pcibios-write-config", ficlPciBiosWriteConfig, FW_DEFAULT);
+ dictAppendWord(dp, "pcibios-find-devclass", ficlPciBiosFindDevclass, FW_DEFAULT);
+ dictAppendWord(dp, "pcibios-find-device", ficlPciBiosFindDevice, FW_DEFAULT);
+ dictAppendWord(dp, "pcibios-locator", ficlPciBiosLocator, FW_DEFAULT);
+
+ ficlSetEnv(pSys, "pcibios-version", biospci_version);
+}
+
+FICL_COMPILE_SET(ficlCompilePciBios);
+#endif
diff --git a/stand/i386/libi386/biospnp.c b/stand/i386/libi386/biospnp.c
new file mode 100644
index 0000000..30e55fc
--- /dev/null
+++ b/stand/i386/libi386/biospnp.c
@@ -0,0 +1,292 @@
+/*-
+ * 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];
+ u_int8_t pnp_version;
+ u_int8_t pnp_length;
+ u_int16_t pnp_BIOScontrol;
+ u_int8_t pnp_checksum;
+ u_int32_t pnp_eventflag;
+ u_int16_t pnp_rmip;
+ u_int16_t pnp_rmcs;
+ u_int16_t pnp_pmip;
+ u_int32_t pnp_pmcs;
+ u_int8_t pnp_OEMdev[4];
+ u_int16_t pnp_rmds;
+ u_int32_t pnp_pmds;
+} __packed;
+
+struct pnp_devNode
+{
+ u_int16_t dn_size;
+ u_int8_t dn_handle;
+ u_int8_t dn_id[4];
+ u_int8_t dn_type[3];
+ u_int16_t dn_attrib;
+ u_int8_t dn_data[1];
+} __packed;
+
+struct pnp_isaConfiguration
+{
+ u_int8_t ic_revision;
+ u_int8_t ic_nCSN;
+ u_int16_t ic_rdport;
+ 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 *)alloca(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;
+ }
+ }
+ va_end(ap);
+
+ /* 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/stand/i386/libi386/biossmap.c b/stand/i386/libi386/biossmap.c
new file mode 100644
index 0000000..e95ea64
--- /dev/null
+++ b/stand/i386/libi386/biossmap.c
@@ -0,0 +1,159 @@
+/*-
+ * 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 <sys/queue.h>
+#include <sys/stddef.h>
+#include <machine/metadata.h>
+#include <machine/pc/bios.h>
+#include "bootstrap.h"
+#include "libi386.h"
+#include "btxv86.h"
+
+struct smap_buf {
+ struct bios_smap smap;
+ uint32_t xattr; /* Extended attribute from ACPI 3.0 */
+ STAILQ_ENTRY(smap_buf) bufs;
+};
+
+#define SMAP_BUFSIZE offsetof(struct smap_buf, bufs)
+
+static struct bios_smap *smapbase;
+static uint32_t *smapattr;
+static u_int smaplen;
+
+void
+bios_getsmap(void)
+{
+ struct smap_buf buf;
+ STAILQ_HEAD(smap_head, smap_buf) head =
+ STAILQ_HEAD_INITIALIZER(head);
+ struct smap_buf *cur, *next;
+ u_int n, x;
+
+ STAILQ_INIT(&head);
+ n = 0;
+ x = 0;
+ v86.ebx = 0;
+ do {
+ v86.ctl = V86_FLAGS;
+ v86.addr = 0x15;
+ v86.eax = 0xe820; /* int 0x15 function 0xe820 */
+ v86.ecx = SMAP_BUFSIZE;
+ v86.edx = SMAP_SIG;
+ v86.es = VTOPSEG(&buf);
+ v86.edi = VTOPOFF(&buf);
+ v86int();
+ if (V86_CY(v86.efl) || v86.eax != SMAP_SIG ||
+ v86.ecx < sizeof(buf.smap) || v86.ecx > SMAP_BUFSIZE)
+ break;
+
+ next = malloc(sizeof(*next));
+ if (next == NULL)
+ break;
+ next->smap = buf.smap;
+ if (v86.ecx == SMAP_BUFSIZE) {
+ next->xattr = buf.xattr;
+ x++;
+ }
+ STAILQ_INSERT_TAIL(&head, next, bufs);
+ n++;
+ } while (v86.ebx != 0);
+ smaplen = n;
+
+ if (smaplen > 0) {
+ smapbase = malloc(smaplen * sizeof(*smapbase));
+ if (smapbase != NULL) {
+ n = 0;
+ STAILQ_FOREACH(cur, &head, bufs)
+ smapbase[n++] = cur->smap;
+ }
+ if (smaplen == x) {
+ smapattr = malloc(smaplen * sizeof(*smapattr));
+ if (smapattr != NULL) {
+ n = 0;
+ STAILQ_FOREACH(cur, &head, bufs)
+ smapattr[n++] = cur->xattr &
+ SMAP_XATTR_MASK;
+ }
+ } else
+ smapattr = NULL;
+ cur = STAILQ_FIRST(&head);
+ while (cur != NULL) {
+ next = STAILQ_NEXT(cur, bufs);
+ free(cur);
+ cur = next;
+ }
+ }
+}
+
+void
+bios_addsmapdata(struct preloaded_file *kfp)
+{
+ size_t size;
+
+ if (smapbase == NULL || smaplen == 0)
+ return;
+ size = smaplen * sizeof(*smapbase);
+ file_addmetadata(kfp, MODINFOMD_SMAP, size, smapbase);
+ if (smapattr != NULL) {
+ size = smaplen * sizeof(*smapattr);
+ file_addmetadata(kfp, MODINFOMD_SMAP_XATTR, size, smapattr);
+ }
+}
+
+COMMAND_SET(smap, "smap", "show BIOS SMAP", command_smap);
+
+static int
+command_smap(int argc, char *argv[])
+{
+ u_int i;
+
+ if (smapbase == NULL || smaplen == 0)
+ return (CMD_ERROR);
+ if (smapattr != NULL)
+ for (i = 0; i < smaplen; i++)
+ printf("SMAP type=%02x base=%016llx len=%016llx attr=%02x\n",
+ (unsigned int)smapbase[i].type,
+ (unsigned long long)smapbase[i].base,
+ (unsigned long long)smapbase[i].length,
+ (unsigned int)smapattr[i]);
+ else
+ for (i = 0; i < smaplen; i++)
+ printf("SMAP type=%02x base=%016llx len=%016llx\n",
+ (unsigned int)smapbase[i].type,
+ (unsigned long long)smapbase[i].base,
+ (unsigned long long)smapbase[i].length);
+ return (CMD_OK);
+}
diff --git a/stand/i386/libi386/bootinfo.c b/stand/i386/libi386/bootinfo.c
new file mode 100644
index 0000000..c33cec7
--- /dev/null
+++ b/stand/i386/libi386/bootinfo.c
@@ -0,0 +1,170 @@
+/*-
+ * 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 <sys/boot.h>
+#include "bootstrap.h"
+#include "libi386.h"
+#include "btxv86.h"
+
+int
+bi_getboothowto(char *kargs)
+{
+ char *cp;
+ char *curpos, *next, *string;
+ int howto;
+ int active;
+ int i;
+ int vidconsole;
+
+ /* 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_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;
+
+ /* Enable selected consoles */
+ string = next = strdup(getenv("console"));
+ vidconsole = 0;
+ while (next != NULL) {
+ curpos = strsep(&next, " ,");
+ if (*curpos == '\0')
+ continue;
+ if (!strcmp(curpos, "vidconsole"))
+ vidconsole = 1;
+ else if (!strcmp(curpos, "comconsole"))
+ howto |= RB_SERIAL;
+ else if (!strcmp(curpos, "nullconsole"))
+ howto |= RB_MUTE;
+ }
+
+ if (vidconsole && (howto & RB_SERIAL))
+ howto |= RB_MULTIPLE;
+
+ /*
+ * XXX: Note that until the kernel is ready to respect multiple consoles
+ * for the boot messages, the first named console is the primary console
+ */
+ if (!strcmp(string, "vidconsole"))
+ howto &= ~RB_SERIAL;
+
+ free(string);
+
+ return(howto);
+}
+
+void
+bi_setboothowto(int howto)
+{
+ int i;
+
+ for (i = 0; howto_names[i].ev != NULL; i++)
+ if (howto & howto_names[i].mask)
+ setenv(howto_names[i].ev, "YES", 1);
+}
+
+/*
+ * 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/stand/i386/libi386/bootinfo32.c b/stand/i386/libi386/bootinfo32.c
new file mode 100644
index 0000000..494688f
--- /dev/null
+++ b/stand/i386/libi386/bootinfo32.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$");
+
+#include <stand.h>
+#include <sys/param.h>
+#include <sys/reboot.h>
+#include <sys/linker.h>
+#include <machine/bootinfo.h>
+#include <machine/metadata.h>
+#include "bootstrap.h"
+#include "libi386.h"
+#include "btxv86.h"
+
+#ifdef LOADER_GELI_SUPPORT
+#include "geliboot.h"
+
+static const size_t keybuf_size = sizeof(struct keybuf) +
+ (GELI_MAX_KEYS * sizeof(struct keybuf_ent));
+#endif
+
+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;
+#ifdef LOADER_GELI_SUPPORT
+ char buf[keybuf_size];
+ struct keybuf *keybuf = (struct keybuf *)buf;
+#endif
+
+ 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_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_unit);
+ bootdevnr = bd_getdev(rootdev);
+ break;
+
+ case DEVT_NET:
+ case DEVT_ZFS:
+ 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);
+#ifdef LOADER_GELI_SUPPORT
+ geli_fill_keybuf(keybuf);
+ file_addmetadata(kfp, MODINFOMD_KEYBUF, keybuf_size, buf);
+ bzero(buf, sizeof(buf));
+#endif
+
+ /* 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/stand/i386/libi386/bootinfo64.c b/stand/i386/libi386/bootinfo64.c
new file mode 100644
index 0000000..02fcf72
--- /dev/null
+++ b/stand/i386/libi386/bootinfo64.c
@@ -0,0 +1,280 @@
+/*-
+ * 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 <machine/cpufunc.h>
+#include <machine/metadata.h>
+#include <machine/psl.h>
+#include <machine/specialreg.h>
+#include "bootstrap.h"
+#include "libi386.h"
+#include "btxv86.h"
+
+#ifdef LOADER_GELI_SUPPORT
+#include "geliboot.h"
+
+static const size_t keybuf_size = sizeof(struct keybuf) +
+ (GELI_MAX_KEYS * sizeof(struct keybuf_ent));
+#endif
+
+/*
+ * 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);
+}
+
+/*
+ * Check to see if this CPU supports long mode.
+ */
+static int
+bi_checkcpu(void)
+{
+ char *cpu_vendor;
+ int vendor[3];
+ int eflags;
+ unsigned int regs[4];
+
+ /* Check for presence of "cpuid". */
+ eflags = read_eflags();
+ write_eflags(eflags ^ PSL_ID);
+ if (!((eflags ^ read_eflags()) & PSL_ID))
+ return (0);
+
+ /* Fetch the vendor string. */
+ do_cpuid(0, regs);
+ vendor[0] = regs[1];
+ vendor[1] = regs[3];
+ vendor[2] = regs[2];
+ cpu_vendor = (char *)vendor;
+
+ /* Check for vendors that support AMD features. */
+ if (strncmp(cpu_vendor, INTEL_VENDOR_ID, 12) != 0 &&
+ strncmp(cpu_vendor, AMD_VENDOR_ID, 12) != 0 &&
+ strncmp(cpu_vendor, CENTAUR_VENDOR_ID, 12) != 0)
+ return (0);
+
+ /* Has to support AMD features. */
+ do_cpuid(0x80000000, regs);
+ if (!(regs[0] >= 0x80000001))
+ return (0);
+
+ /* Check for long mode. */
+ do_cpuid(0x80000001, regs);
+ return (regs[3] & AMDID_LM);
+}
+
+/*
+ * Load the information expected by an amd64 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 addr, vm_offset_t *modulep,
+ vm_offset_t *kernendp, int add_smap)
+{
+ struct preloaded_file *xp, *kfp;
+ struct i386_devdesc *rootdev;
+ struct file_metadata *md;
+ u_int64_t kernend;
+ u_int64_t envp;
+ u_int64_t module;
+ vm_offset_t size;
+ char *rootdevname;
+ int howto;
+#ifdef LOADER_GELI_SUPPORT
+ char buf[keybuf_size];
+ struct keybuf *keybuf = (struct keybuf *)buf;
+#endif
+
+ if (!bi_checkcpu()) {
+ printf("CPU doesn't support long mode\n");
+ return (EINVAL);
+ }
+
+ 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));
+
+ if (addr == 0) {
+ /* find the last module in the chain */
+ 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);
+
+ /* place the metadata before anything */
+ module = *modulep = addr;
+
+ 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);
+ file_addmetadata(kfp, MODINFOMD_MODULEP, sizeof module, &module);
+ if (add_smap != 0)
+ bios_addsmapdata(kfp);
+
+#ifdef LOADER_GELI_SUPPORT
+ geli_fill_keybuf(keybuf);
+ file_addmetadata(kfp, MODINFOMD_KEYBUF, keybuf_size, buf);
+ bzero(buf, sizeof(buf));
+#endif
+
+ size = bi_copymodules64(0);
+
+ /* copy our environment */
+ envp = roundup(addr + size, PAGE_SIZE);
+ addr = bi_copyenv(envp);
+
+ /* set kernend */
+ kernend = roundup(addr, PAGE_SIZE);
+ *kernendp = kernend;
+
+ /* patch MODINFOMD_KERNEND */
+ md = file_findmetadata(kfp, MODINFOMD_KERNEND);
+ bcopy(&kernend, md->md_data, sizeof kernend);
+
+ /* patch MODINFOMD_ENVP */
+ md = file_findmetadata(kfp, MODINFOMD_ENVP);
+ bcopy(&envp, md->md_data, sizeof envp);
+
+ /* copy module list and metadata */
+ (void)bi_copymodules64(*modulep);
+
+ return(0);
+}
diff --git a/stand/i386/libi386/comconsole.c b/stand/i386/libi386/comconsole.c
new file mode 100644
index 0000000..f98a1f7
--- /dev/null
+++ b/stand/i386/libi386/comconsole.c
@@ -0,0 +1,385 @@
+/*-
+ * 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 <dev/pci/pcireg.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 */
+#define COMC_DIV2BPS(x) (115200 / (x)) /* DLAB divisor to speed */
+
+#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_getspeed(void);
+static int comc_ischar(void);
+static int comc_parseint(const char *string);
+static uint32_t comc_parse_pcidev(const char *string);
+static int comc_pcidev_set(struct env_var *ev, int flags,
+ const void *value);
+static int comc_pcidev_handle(uint32_t locator);
+static int comc_port_set(struct env_var *ev, int flags,
+ const void *value);
+static void comc_setup(int speed, int port);
+static int comc_speed_set(struct env_var *ev, int flags,
+ const void *value);
+
+static int comc_curspeed;
+static int comc_port = COMPORT;
+static uint32_t comc_locator;
+
+struct console comconsole = {
+ "comconsole",
+ "serial port",
+ 0,
+ comc_probe,
+ comc_init,
+ comc_putchar,
+ comc_getchar,
+ comc_ischar
+};
+
+static void
+comc_probe(struct console *cp)
+{
+ char intbuf[16];
+ char *cons, *env;
+ int speed, port;
+ uint32_t locator;
+
+ if (comc_curspeed == 0) {
+ comc_curspeed = COMSPEED;
+ /*
+ * Assume that the speed was set by an earlier boot loader if
+ * comconsole is already the preferred console.
+ */
+ cons = getenv("console");
+ if ((cons != NULL && strcmp(cons, comconsole.c_name) == 0) ||
+ getenv("boot_multicons") != NULL) {
+ comc_curspeed = comc_getspeed();
+ }
+
+ env = getenv("comconsole_speed");
+ if (env != NULL) {
+ speed = comc_parseint(env);
+ if (speed > 0)
+ comc_curspeed = speed;
+ }
+
+ sprintf(intbuf, "%d", comc_curspeed);
+ unsetenv("comconsole_speed");
+ env_setenv("comconsole_speed", EV_VOLATILE, intbuf, comc_speed_set,
+ env_nounset);
+
+ env = getenv("comconsole_port");
+ if (env != NULL) {
+ port = comc_parseint(env);
+ if (port > 0)
+ comc_port = port;
+ }
+
+ sprintf(intbuf, "%d", comc_port);
+ unsetenv("comconsole_port");
+ env_setenv("comconsole_port", EV_VOLATILE, intbuf, comc_port_set,
+ env_nounset);
+
+ env = getenv("comconsole_pcidev");
+ if (env != NULL) {
+ locator = comc_parse_pcidev(env);
+ if (locator != 0)
+ comc_pcidev_handle(locator);
+ }
+
+ unsetenv("comconsole_pcidev");
+ env_setenv("comconsole_pcidev", EV_VOLATILE, env, comc_pcidev_set,
+ env_nounset);
+ }
+ comc_setup(comc_curspeed, comc_port);
+}
+
+static int
+comc_init(int arg)
+{
+
+ comc_setup(comc_curspeed, comc_port);
+
+ if ((comconsole.c_flags & (C_PRESENTIN | C_PRESENTOUT)) ==
+ (C_PRESENTIN | C_PRESENTOUT))
+ return (CMD_OK);
+ return (CMD_ERROR);
+}
+
+static void
+comc_putchar(int c)
+{
+ int wait;
+
+ for (wait = COMC_TXWAIT; wait > 0; wait--)
+ if (inb(comc_port + com_lsr) & LSR_TXRDY) {
+ outb(comc_port + com_data, (u_char)c);
+ break;
+ }
+}
+
+static int
+comc_getchar(void)
+{
+ return (comc_ischar() ? inb(comc_port + com_data) : -1);
+}
+
+static int
+comc_ischar(void)
+{
+ return (inb(comc_port + com_lsr) & LSR_RXRDY);
+}
+
+static int
+comc_speed_set(struct env_var *ev, int flags, const void *value)
+{
+ int speed;
+
+ if (value == NULL || (speed = comc_parseint(value)) <= 0) {
+ printf("Invalid speed\n");
+ return (CMD_ERROR);
+ }
+
+ if (comc_curspeed != speed)
+ comc_setup(speed, comc_port);
+
+ env_setenv(ev->ev_name, flags | EV_NOHOOK, value, NULL, NULL);
+
+ return (CMD_OK);
+}
+
+static int
+comc_port_set(struct env_var *ev, int flags, const void *value)
+{
+ int port;
+
+ if (value == NULL || (port = comc_parseint(value)) <= 0) {
+ printf("Invalid port\n");
+ return (CMD_ERROR);
+ }
+
+ if (comc_port != port)
+ comc_setup(comc_curspeed, port);
+
+ env_setenv(ev->ev_name, flags | EV_NOHOOK, value, NULL, NULL);
+
+ return (CMD_OK);
+}
+
+/*
+ * Input: bus:dev:func[:bar]. If bar is not specified, it is 0x10.
+ * Output: bar[24:16] bus[15:8] dev[7:3] func[2:0]
+ */
+static uint32_t
+comc_parse_pcidev(const char *string)
+{
+#ifdef NO_PCI
+ return (0);
+#else
+ char *p, *p1;
+ uint8_t bus, dev, func, bar;
+ uint32_t locator;
+ int pres;
+
+ pres = strtol(string, &p, 0);
+ if (p == string || *p != ':' || pres < 0 )
+ return (0);
+ bus = pres;
+ p1 = ++p;
+
+ pres = strtol(p1, &p, 0);
+ if (p == string || *p != ':' || pres < 0 )
+ return (0);
+ dev = pres;
+ p1 = ++p;
+
+ pres = strtol(p1, &p, 0);
+ if (p == string || (*p != ':' && *p != '\0') || pres < 0 )
+ return (0);
+ func = pres;
+
+ if (*p == ':') {
+ p1 = ++p;
+ pres = strtol(p1, &p, 0);
+ if (p == string || *p != '\0' || pres <= 0 )
+ return (0);
+ bar = pres;
+ } else
+ bar = 0x10;
+
+ locator = (bar << 16) | biospci_locator(bus, dev, func);
+ return (locator);
+#endif
+}
+
+static int
+comc_pcidev_handle(uint32_t locator)
+{
+#ifdef NO_PCI
+ return (CMD_ERROR);
+#else
+ char intbuf[64];
+ uint32_t port;
+
+ if (biospci_read_config(locator & 0xffff,
+ (locator & 0xff0000) >> 16, 2, &port) == -1) {
+ printf("Cannot read bar at 0x%x\n", locator);
+ return (CMD_ERROR);
+ }
+
+ /*
+ * biospci_read_config() sets port == 0xffffffff if the pcidev
+ * isn't found on the bus. Check for 0xffffffff and return to not
+ * panic in BTX.
+ */
+ if (port == 0xffffffff) {
+ printf("Cannot find specified pcidev\n");
+ return (CMD_ERROR);
+ }
+ if (!PCI_BAR_IO(port)) {
+ printf("Memory bar at 0x%x\n", locator);
+ return (CMD_ERROR);
+ }
+ port &= PCIM_BAR_IO_BASE;
+
+ sprintf(intbuf, "%d", port);
+ unsetenv("comconsole_port");
+ env_setenv("comconsole_port", EV_VOLATILE, intbuf,
+ comc_port_set, env_nounset);
+
+ comc_setup(comc_curspeed, port);
+ comc_locator = locator;
+
+ return (CMD_OK);
+#endif
+}
+
+static int
+comc_pcidev_set(struct env_var *ev, int flags, const void *value)
+{
+ uint32_t locator;
+ int error;
+
+ if (value == NULL || (locator = comc_parse_pcidev(value)) <= 0) {
+ printf("Invalid pcidev\n");
+ return (CMD_ERROR);
+ }
+ if ((comconsole.c_flags & (C_ACTIVEIN | C_ACTIVEOUT)) != 0 &&
+ comc_locator != locator) {
+ error = comc_pcidev_handle(locator);
+ if (error != CMD_OK)
+ return (error);
+ }
+ env_setenv(ev->ev_name, flags | EV_NOHOOK, value, NULL, NULL);
+ return (CMD_OK);
+}
+
+static void
+comc_setup(int speed, int port)
+{
+ static int TRY_COUNT = 1000000;
+ char intbuf[64];
+ int tries;
+
+ unsetenv("hw.uart.console");
+ comc_curspeed = speed;
+ comc_port = port;
+ if ((comconsole.c_flags & (C_ACTIVEIN | C_ACTIVEOUT)) == 0)
+ return;
+
+ outb(comc_port + com_cfcr, CFCR_DLAB | COMC_FMT);
+ outb(comc_port + com_dlbl, COMC_BPS(speed) & 0xff);
+ outb(comc_port + com_dlbh, COMC_BPS(speed) >> 8);
+ outb(comc_port + com_cfcr, COMC_FMT);
+ outb(comc_port + com_mcr, MCR_RTS | MCR_DTR);
+
+ tries = 0;
+ do
+ inb(comc_port + com_data);
+ while (inb(comc_port + com_lsr) & LSR_RXRDY && ++tries < TRY_COUNT);
+
+ if (tries < TRY_COUNT) {
+ comconsole.c_flags |= (C_PRESENTIN | C_PRESENTOUT);
+ sprintf(intbuf, "io:%d,br:%d", comc_port, comc_curspeed);
+ env_setenv("hw.uart.console", EV_VOLATILE, intbuf, NULL, NULL);
+ } else
+ comconsole.c_flags &= ~(C_PRESENTIN | C_PRESENTOUT);
+}
+
+static int
+comc_parseint(const char *speedstr)
+{
+ char *p;
+ int speed;
+
+ speed = strtol(speedstr, &p, 0);
+ if (p == speedstr || *p != '\0' || speed <= 0)
+ return (-1);
+
+ return (speed);
+}
+
+static int
+comc_getspeed(void)
+{
+ u_int divisor;
+ u_char dlbh;
+ u_char dlbl;
+ u_char cfcr;
+
+ cfcr = inb(comc_port + com_cfcr);
+ outb(comc_port + com_cfcr, CFCR_DLAB | cfcr);
+
+ dlbl = inb(comc_port + com_dlbl);
+ dlbh = inb(comc_port + com_dlbh);
+
+ outb(comc_port + com_cfcr, cfcr);
+
+ divisor = dlbh << 8 | dlbl;
+
+ /* XXX there should be more sanity checking. */
+ if (divisor == 0)
+ return (COMSPEED);
+ return (COMC_DIV2BPS(divisor));
+}
diff --git a/stand/i386/libi386/devicename.c b/stand/i386/libi386/devicename.c
new file mode 100644
index 0000000..c7705d7
--- /dev/null
+++ b/stand/i386/libi386/devicename.c
@@ -0,0 +1,206 @@
+/*-
+ * 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 "bootstrap.h"
+#include "disk.h"
+#include "libi386.h"
+#include "../zfs/libzfs.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, 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:
+ err = disk_parsedev((struct disk_devdesc *)idev, np, path);
+ if (err != 0)
+ goto fail;
+ 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;
+ }
+ } else {
+ cp = (char *)np;
+ }
+ if (*cp && (*cp != ':')) {
+ err = EINVAL;
+ goto fail;
+ }
+
+ idev->d_unit = unit;
+ if (path != NULL)
+ *path = (*cp == 0) ? cp : cp + 1;
+ break;
+ case DEVT_ZFS:
+ err = zfs_parsedev((struct zfs_devdesc *)idev, np, path);
+ if (err != 0)
+ goto fail;
+ 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? */
+
+ switch(dev->d_type) {
+ case DEVT_NONE:
+ strcpy(buf, "(no device)");
+ break;
+
+ case DEVT_CD:
+ case DEVT_NET:
+ sprintf(buf, "%s%d:", dev->d_dev->dv_name, dev->d_unit);
+ break;
+
+ case DEVT_DISK:
+ return (disk_fmtdev(vdev));
+
+ case DEVT_ZFS:
+ return(zfs_fmtdev(vdev));
+ }
+ return(buf);
+}
+
+
+/*
+ * Set currdev to suit the value being supplied in (value)
+ */
+int
+i386_setcurrdev(struct env_var *ev, int flags, const 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/stand/i386/libi386/elf32_freebsd.c b/stand/i386/libi386/elf32_freebsd.c
new file mode 100644
index 0000000..641e273
--- /dev/null
+++ b/stand/i386/libi386/elf32_freebsd.c
@@ -0,0 +1,84 @@
+/*-
+ * 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);
+static int elf32_obj_exec(struct preloaded_file *amp);
+
+struct file_format i386_elf = { elf32_loadfile, elf32_exec };
+struct file_format i386_elf_obj = { elf32_obj_loadfile, elf32_obj_exec };
+
+/*
+ * There is an ELF kernel and one or more ELF 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);
+ 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");
+}
+
+static int
+elf32_obj_exec(struct preloaded_file *fp)
+{
+ return (EFTYPE);
+}
diff --git a/stand/i386/libi386/elf64_freebsd.c b/stand/i386/libi386/elf64_freebsd.c
new file mode 100644
index 0000000..338a745
--- /dev/null
+++ b/stand/i386/libi386/elf64_freebsd.c
@@ -0,0 +1,126 @@
+/*-
+ * 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);
+static int elf64_obj_exec(struct preloaded_file *amp);
+
+struct file_format amd64_elf = { elf64_loadfile, elf64_exec };
+struct file_format amd64_elf_obj = { elf64_obj_loadfile, elf64_obj_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 void amd64_tramp();
+
+/*
+ * There is an ELF kernel and one or more ELF 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);
+ ehdr = (Elf_Ehdr *)&(md->md_data);
+
+ err = bi_load64(fp->f_args, 0, &modulep, &kernend, 1);
+ 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");
+}
+
+static int
+elf64_obj_exec(struct preloaded_file *fp)
+{
+ return (EFTYPE);
+}
diff --git a/stand/i386/libi386/i386_copy.c b/stand/i386/libi386/i386_copy.c
new file mode 100644
index 0000000..3c05241
--- /dev/null
+++ b/stand/i386/libi386/i386_copy.c
@@ -0,0 +1,75 @@
+/*-
+ * 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"
+
+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)
+{
+
+ if (dest + len >= memtop_copyin) {
+ errno = EFBIG;
+ return(-1);
+ }
+
+ return (read(fd, PTOV(dest), len));
+}
diff --git a/stand/i386/libi386/i386_module.c b/stand/i386/libi386/i386_module.c
new file mode 100644
index 0000000..78ab61b
--- /dev/null
+++ b/stand/i386/libi386/i386_module.c
@@ -0,0 +1,44 @@
+/*-
+ * 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.
+ *
+ */
+
+/*
+ * Use voodoo to load modules required by current hardware.
+ */
+int
+i386_autoload(void)
+{
+
+ /* XXX use PnP to locate stuff here */
+ return(0);
+}
diff --git a/stand/i386/libi386/libi386.h b/stand/i386/libi386/libi386.h
new file mode 100644
index 0000000..e65a060
--- /dev/null
+++ b/stand/i386/libi386/libi386.h
@@ -0,0 +1,156 @@
+/*-
+ * 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 and also with struct zfs_devdesc for zfs
+ * support.
+ */
+struct i386_devdesc
+{
+ struct devsw *d_dev;
+ int d_type;
+ int d_unit;
+ union
+ {
+ struct
+ {
+ void *data;
+ int slice;
+ int partition;
+ off_t offset;
+ } biosdisk;
+ struct
+ {
+ void *data;
+ } bioscd;
+ struct
+ {
+ void *data;
+ uint64_t pool_guid;
+ uint64_t root_guid;
+ } zfs;
+ } d_kind;
+};
+
+/*
+ * relocater trampoline support.
+ */
+struct relocate_data {
+ uint32_t src;
+ uint32_t dest;
+ uint32_t size;
+};
+
+extern void relocater(void);
+
+/*
+ * The relocater_data[] is fixed size array allocated in relocater_tramp.S
+ */
+extern struct relocate_data relocater_data[];
+extern uint32_t relocater_size;
+
+extern uint16_t relocator_ip;
+extern uint16_t relocator_cs;
+extern uint16_t relocator_ds;
+extern uint16_t relocator_es;
+extern uint16_t relocator_fs;
+extern uint16_t relocator_gs;
+extern uint16_t relocator_ss;
+extern uint16_t relocator_sp;
+extern uint32_t relocator_esi;
+extern uint32_t relocator_eax;
+extern uint32_t relocator_ebx;
+extern uint32_t relocator_edx;
+extern uint32_t relocator_ebp;
+extern uint16_t relocator_a20_enabled;
+
+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, const void *value);
+
+extern struct devdesc currdev; /* our current device */
+
+#define MAXDEV 31 /* maximum number of distinct devices */
+#define MAXBDDEV MAXDEV
+
+/* 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 */
+uint32_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 uint32_t bios_basemem; /* base memory in bytes */
+extern uint32_t bios_extmem; /* extended memory in bytes */
+extern vm_offset_t memtop; /* last address of physical memory + 1 */
+extern vm_offset_t memtop_copyin; /* memtop less heap size for the cases */
+ /* when heap is at the top of */
+ /* extended memory; for other cases */
+ /* just the same as memtop */
+extern uint32_t high_heap_size; /* extended memory region available */
+extern vm_offset_t high_heap_base; /* for use as the heap */
+
+void biospci_detect(void);
+int biospci_find_devclass(uint32_t class, int index, uint32_t *locator);
+int biospci_read_config(uint32_t locator, int offset, int width, uint32_t *val);
+uint32_t biospci_locator(int8_t bus, uint8_t device, uint8_t function);
+int biospci_write_config(uint32_t locator, int offset, int width, uint32_t val);
+
+void biosacpi_detect(void);
+
+int i386_autoload(void);
+
+int bi_getboothowto(char *kargs);
+void bi_setboothowto(int howto);
+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 addr, vm_offset_t *modulep,
+ vm_offset_t *kernend, int add_smap);
+
+void pxe_enable(void *pxeinfo);
diff --git a/stand/i386/libi386/multiboot.c b/stand/i386/libi386/multiboot.c
new file mode 100644
index 0000000..9aac640
--- /dev/null
+++ b/stand/i386/libi386/multiboot.c
@@ -0,0 +1,467 @@
+/*-
+ * Copyright (c) 2014 Roger Pau Monné <royger@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.
+ */
+
+/*
+ * This multiboot implementation only implements a subset of the full
+ * multiboot specification in order to be able to boot Xen and a
+ * FreeBSD Dom0. Trying to use it to boot other multiboot compliant
+ * kernels will most surely fail.
+ *
+ * The full multiboot specification can be found here:
+ * http://www.gnu.org/software/grub/manual/multiboot/multiboot.html
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/param.h>
+#include <sys/exec.h>
+#include <sys/linker.h>
+#include <sys/module.h>
+#include <sys/stdint.h>
+#define _MACHINE_ELF_WANT_32BIT
+#include <machine/elf.h>
+#include <string.h>
+#include <stand.h>
+
+#include "bootstrap.h"
+#include "multiboot.h"
+#include "../i386/libi386/libi386.h"
+#include "../i386/btx/lib/btxv86.h"
+
+#define MULTIBOOT_SUPPORTED_FLAGS \
+ (MULTIBOOT_PAGE_ALIGN|MULTIBOOT_MEMORY_INFO)
+#define NUM_MODULES 2
+#define METADATA_FIXED_SIZE (PAGE_SIZE*4)
+#define METADATA_MODULE_SIZE PAGE_SIZE
+
+#define METADATA_RESV_SIZE(mod_num) \
+ roundup(METADATA_FIXED_SIZE + METADATA_MODULE_SIZE * mod_num, PAGE_SIZE)
+
+extern int elf32_loadfile_raw(char *filename, u_int64_t dest,
+ struct preloaded_file **result, int multiboot);
+extern int elf64_load_modmetadata(struct preloaded_file *fp, u_int64_t dest);
+extern int elf64_obj_loadfile(char *filename, u_int64_t dest,
+ struct preloaded_file **result);
+
+static int multiboot_loadfile(char *, u_int64_t, struct preloaded_file **);
+static int multiboot_exec(struct preloaded_file *);
+
+static int multiboot_obj_loadfile(char *, u_int64_t, struct preloaded_file **);
+static int multiboot_obj_exec(struct preloaded_file *fp);
+
+struct file_format multiboot = { multiboot_loadfile, multiboot_exec };
+struct file_format multiboot_obj =
+ { multiboot_obj_loadfile, multiboot_obj_exec };
+
+extern void multiboot_tramp();
+
+static const char mbl_name[] = "FreeBSD Loader";
+
+static int
+num_modules(struct preloaded_file *kfp)
+{
+ struct kernel_module *kmp;
+ int mod_num = 0;
+
+ for (kmp = kfp->f_modules; kmp != NULL; kmp = kmp->m_next)
+ mod_num++;
+
+ return (mod_num);
+}
+
+static vm_offset_t
+max_addr(void)
+{
+ struct preloaded_file *fp;
+ vm_offset_t addr = 0;
+
+ for (fp = file_findfile(NULL, NULL); fp != NULL; fp = fp->f_next) {
+ if (addr < (fp->f_addr + fp->f_size))
+ addr = fp->f_addr + fp->f_size;
+ }
+
+ return (addr);
+}
+
+static int
+multiboot_loadfile(char *filename, u_int64_t dest,
+ struct preloaded_file **result)
+{
+ uint32_t *magic;
+ int i, error;
+ caddr_t header_search;
+ ssize_t search_size;
+ int fd;
+ struct multiboot_header *header;
+ char *cmdline;
+
+ /*
+ * Read MULTIBOOT_SEARCH size in order to search for the
+ * multiboot magic header.
+ */
+ if (filename == NULL)
+ return (EFTYPE);
+ if ((fd = open(filename, O_RDONLY)) == -1)
+ return (errno);
+ header_search = malloc(MULTIBOOT_SEARCH);
+ if (header_search == NULL) {
+ close(fd);
+ return (ENOMEM);
+ }
+ search_size = read(fd, header_search, MULTIBOOT_SEARCH);
+ magic = (uint32_t *)header_search;
+
+ header = NULL;
+ for (i = 0; i < (search_size / sizeof(uint32_t)); i++) {
+ if (magic[i] == MULTIBOOT_HEADER_MAGIC) {
+ header = (struct multiboot_header *)&magic[i];
+ break;
+ }
+ }
+
+ if (header == NULL) {
+ error = EFTYPE;
+ goto out;
+ }
+
+ /* Valid multiboot header has been found, validate checksum */
+ if (header->magic + header->flags + header->checksum != 0) {
+ printf(
+ "Multiboot checksum failed, magic: 0x%x flags: 0x%x checksum: 0x%x\n",
+ header->magic, header->flags, header->checksum);
+ error = EFTYPE;
+ goto out;
+ }
+
+ if ((header->flags & ~MULTIBOOT_SUPPORTED_FLAGS) != 0) {
+ printf("Unsupported multiboot flags found: 0x%x\n",
+ header->flags);
+ error = EFTYPE;
+ goto out;
+ }
+
+ error = elf32_loadfile_raw(filename, dest, result, 1);
+ if (error != 0) {
+ printf(
+ "elf32_loadfile_raw failed: %d unable to load multiboot kernel\n",
+ error);
+ goto out;
+ }
+
+ /*
+ * f_addr is already aligned to PAGE_SIZE, make sure
+ * f_size it's also aligned so when the modules are loaded
+ * they are aligned to PAGE_SIZE.
+ */
+ (*result)->f_size = roundup((*result)->f_size, PAGE_SIZE);
+
+out:
+ free(header_search);
+ close(fd);
+ return (error);
+}
+
+static int
+multiboot_exec(struct preloaded_file *fp)
+{
+ vm_offset_t module_start, last_addr, metadata_size;
+ vm_offset_t modulep, kernend, entry;
+ struct file_metadata *md;
+ Elf_Ehdr *ehdr;
+ struct multiboot_info *mb_info = NULL;
+ struct multiboot_mod_list *mb_mod = NULL;
+ char *cmdline = NULL;
+ size_t len;
+ int error, mod_num;
+
+ /*
+ * Don't pass the memory size found by the bootloader, the memory
+ * available to Dom0 will be lower than that.
+ */
+ unsetenv("smbios.memory.enabled");
+
+ /* Allocate the multiboot struct and fill the basic details. */
+ mb_info = malloc(sizeof(struct multiboot_info));
+ if (mb_info == NULL) {
+ error = ENOMEM;
+ goto error;
+ }
+ bzero(mb_info, sizeof(struct multiboot_info));
+ mb_info->flags = MULTIBOOT_INFO_MEMORY|MULTIBOOT_INFO_BOOT_LOADER_NAME;
+ mb_info->mem_lower = bios_basemem / 1024;
+ mb_info->mem_upper = bios_extmem / 1024;
+ mb_info->boot_loader_name = VTOP(mbl_name);
+
+ /* Set the Xen command line. */
+ if (fp->f_args == NULL) {
+ /* Add the Xen command line if it is set. */
+ cmdline = getenv("xen_cmdline");
+ if (cmdline != NULL) {
+ fp->f_args = strdup(cmdline);
+ if (fp->f_args == NULL) {
+ error = ENOMEM;
+ goto error;
+ }
+ }
+ }
+ if (fp->f_args != NULL) {
+ len = strlen(fp->f_name) + 1 + strlen(fp->f_args) + 1;
+ cmdline = malloc(len);
+ if (cmdline == NULL) {
+ error = ENOMEM;
+ goto error;
+ }
+ snprintf(cmdline, len, "%s %s", fp->f_name, fp->f_args);
+ mb_info->cmdline = VTOP(cmdline);
+ mb_info->flags |= MULTIBOOT_INFO_CMDLINE;
+ }
+
+ /* Find the entry point of the Xen kernel and save it for later */
+ if ((md = file_findmetadata(fp, MODINFOMD_ELFHDR)) == NULL) {
+ printf("Unable to find %s entry point\n", fp->f_name);
+ error = EINVAL;
+ goto error;
+ }
+ ehdr = (Elf_Ehdr *)&(md->md_data);
+ entry = ehdr->e_entry & 0xffffff;
+
+ /*
+ * Prepare the multiboot module list, Xen assumes the first
+ * module is the Dom0 kernel, and the second one is the initramfs.
+ * This is not optimal for FreeBSD, that doesn't have a initramfs
+ * but instead loads modules dynamically and creates the metadata
+ * info on-the-fly.
+ *
+ * As expected, the first multiboot module is going to be the
+ * FreeBSD kernel loaded as a raw file. The second module is going
+ * to contain the metadata info and the loaded modules.
+ *
+ * On native FreeBSD loads all the modules and then places the
+ * metadata info at the end, but this is painful when running on Xen,
+ * because it relocates the second multiboot module wherever it
+ * likes. In order to workaround this limitation the metadata
+ * information is placed at the start of the second module and
+ * the original modulep value is saved together with the other
+ * metadata, so we can relocate everything.
+ *
+ * Native layout:
+ * fp->f_addr + fp->f_size
+ * +---------+----------------+------------+
+ * | | | |
+ * | Kernel | Modules | Metadata |
+ * | | | |
+ * +---------+----------------+------------+
+ * fp->f_addr modulep kernend
+ *
+ * Xen layout:
+ *
+ * Initial:
+ * fp->f_addr + fp->f_size
+ * +---------+----------+----------------+------------+
+ * | | | | |
+ * | Kernel | Reserved | Modules | Metadata |
+ * | | | | dry run |
+ * +---------+----------+----------------+------------+
+ * fp->f_addr
+ *
+ * After metadata polacement (ie: final):
+ * fp->f_addr + fp->f_size
+ * +-----------+---------+----------+----------------+
+ * | | | | |
+ * | Kernel | Free | Metadata | Modules |
+ * | | | | |
+ * +-----------+---------+----------+----------------+
+ * fp->f_addr modulep kernend
+ * \__________/ \__________________________/
+ * Multiboot module 0 Multiboot module 1
+ */
+
+ fp = file_findfile(NULL, "elf kernel");
+ if (fp == NULL) {
+ printf("No FreeBSD kernel provided, aborting\n");
+ error = EINVAL;
+ goto error;
+ }
+
+ if (fp->f_metadata != NULL) {
+ printf("FreeBSD kernel already contains metadata, aborting\n");
+ error = EINVAL;
+ goto error;
+ }
+
+
+ mb_mod = malloc(sizeof(struct multiboot_mod_list) * NUM_MODULES);
+ if (mb_mod == NULL) {
+ error = ENOMEM;
+ goto error;
+ }
+
+ bzero(mb_mod, sizeof(struct multiboot_mod_list) * NUM_MODULES);
+
+ /*
+ * Calculate how much memory is needed for the metatdata. We did
+ * an approximation of the maximum size when loading the kernel,
+ * but now we know the exact size, so we can release some of this
+ * preallocated memory if not needed.
+ */
+ last_addr = roundup(max_addr(), PAGE_SIZE);
+ mod_num = num_modules(fp);
+
+ /*
+ * Place the metadata after the last used address in order to
+ * calculate it's size, this will not be used.
+ */
+ error = bi_load64(fp->f_args, last_addr, &modulep, &kernend, 0);
+ if (error != 0) {
+ printf("bi_load64 failed: %d\n", error);
+ goto error;
+ }
+ metadata_size = roundup(kernend - last_addr, PAGE_SIZE);
+
+ /* Check that the size is not greater than what we have reserved */
+ if (metadata_size > METADATA_RESV_SIZE(mod_num)) {
+ printf("Required memory for metadata is greater than reserved "
+ "space, please increase METADATA_FIXED_SIZE and "
+ "METADATA_MODULE_SIZE and rebuild the loader\n");
+ error = ENOMEM;
+ goto error;
+ }
+
+ /* Clean the metadata added to the kernel in the bi_load64 dry run */
+ file_removemetadata(fp);
+
+ /*
+ * This is the position where the second multiboot module
+ * will be placed.
+ */
+ module_start = fp->f_addr + fp->f_size - metadata_size;
+
+ error = bi_load64(fp->f_args, module_start, &modulep, &kernend, 0);
+ if (error != 0) {
+ printf("bi_load64 failed: %d\n", error);
+ goto error;
+ }
+
+ mb_mod[0].mod_start = fp->f_addr;
+ mb_mod[0].mod_end = fp->f_addr + fp->f_size;
+ mb_mod[0].mod_end -= METADATA_RESV_SIZE(mod_num);
+
+ mb_mod[1].mod_start = module_start;
+ mb_mod[1].mod_end = last_addr;
+
+ mb_info->mods_count = NUM_MODULES;
+ mb_info->mods_addr = VTOP(mb_mod);
+ mb_info->flags |= MULTIBOOT_INFO_MODS;
+
+ dev_cleanup();
+ __exec((void *)VTOP(multiboot_tramp), (void *)entry,
+ (void *)VTOP(mb_info));
+
+ panic("exec returned");
+
+error:
+ if (mb_mod)
+ free(mb_mod);
+ if (mb_info)
+ free(mb_info);
+ if (cmdline)
+ free(cmdline);
+ return (error);
+}
+
+static int
+multiboot_obj_loadfile(char *filename, u_int64_t dest,
+ struct preloaded_file **result)
+{
+ struct preloaded_file *mfp, *kfp, *rfp;
+ struct kernel_module *kmp;
+ int error, mod_num;
+
+ /* See if there's a multiboot kernel loaded */
+ mfp = file_findfile(NULL, "elf multiboot kernel");
+ if (mfp == NULL)
+ return (EFTYPE);
+
+ /*
+ * We have a multiboot kernel loaded, see if there's a FreeBSD
+ * kernel loaded also.
+ */
+ kfp = file_findfile(NULL, "elf kernel");
+ if (kfp == NULL) {
+ /*
+ * No kernel loaded, this must be it. The kernel has to
+ * be loaded as a raw file, it will be processed by
+ * Xen and correctly loaded as an ELF file.
+ */
+ rfp = file_loadraw(filename, "elf kernel", 0);
+ if (rfp == NULL) {
+ printf(
+ "Unable to load %s as a multiboot payload kernel\n",
+ filename);
+ return (EINVAL);
+ }
+
+ /* Load kernel metadata... */
+ setenv("kernelname", filename, 1);
+ error = elf64_load_modmetadata(rfp, rfp->f_addr + rfp->f_size);
+ if (error) {
+ printf("Unable to load kernel %s metadata error: %d\n",
+ rfp->f_name, error);
+ return (EINVAL);
+ }
+
+ /*
+ * Save space at the end of the kernel in order to place
+ * the metadata information. We do an approximation of the
+ * max metadata size, this is not optimal but it's probably
+ * the best we can do at this point. Once all modules are
+ * loaded and the size of the metadata is known this
+ * space will be recovered if not used.
+ */
+ mod_num = num_modules(rfp);
+ rfp->f_size = roundup(rfp->f_size, PAGE_SIZE);
+ rfp->f_size += METADATA_RESV_SIZE(mod_num);
+ *result = rfp;
+ } else {
+ /* The rest should be loaded as regular modules */
+ error = elf64_obj_loadfile(filename, dest, result);
+ if (error != 0) {
+ printf("Unable to load %s as an object file, error: %d",
+ filename, error);
+ return (error);
+ }
+ }
+
+ return (0);
+}
+
+static int
+multiboot_obj_exec(struct preloaded_file *fp)
+{
+
+ return (EFTYPE);
+}
diff --git a/stand/i386/libi386/multiboot.h b/stand/i386/libi386/multiboot.h
new file mode 100644
index 0000000..819fa2e
--- /dev/null
+++ b/stand/i386/libi386/multiboot.h
@@ -0,0 +1,225 @@
+/* multiboot.h - Multiboot header file. */
+/* Copyright (C) 1999,2003,2007,2008,2009 Free Software Foundation, Inc.
+*
+* Permission is hereby granted, free of charge, to any person obtaining a copy
+* of this software and associated documentation files (the "Software"), to
+* deal in the Software without restriction, including without limitation the
+* rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+* sell copies of the Software, and to permit persons to whom the Software is
+* furnished to do so, subject to the following conditions:
+*
+* The above copyright notice and this permission notice shall be included in
+* all copies or substantial portions of the Software.
+*
+* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL ANY
+* DEVELOPER OR DISTRIBUTOR BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
+* WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR
+* IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+*
+* $FreeBSD$
+*/
+
+#ifndef MULTIBOOT_HEADER
+#define MULTIBOOT_HEADER 1
+
+/* How many bytes from the start of the file we search for the header. */
+#define MULTIBOOT_SEARCH 8192
+
+/* The magic field should contain this. */
+#define MULTIBOOT_HEADER_MAGIC 0x1BADB002
+
+/* This should be in %eax. */
+#define MULTIBOOT_BOOTLOADER_MAGIC 0x2BADB002
+
+/* The bits in the required part of flags field we don't support. */
+#define MULTIBOOT_UNSUPPORTED 0x0000fffc
+
+/* Alignment of multiboot modules. */
+#define MULTIBOOT_MOD_ALIGN 0x00001000
+
+/* Alignment of the multiboot info structure. */
+#define MULTIBOOT_INFO_ALIGN 0x00000004
+
+/* Flags set in the 'flags' member of the multiboot header. */
+
+/* Align all boot modules on i386 page (4KB) boundaries. */
+#define MULTIBOOT_PAGE_ALIGN 0x00000001
+
+/* Must pass memory information to OS. */
+#define MULTIBOOT_MEMORY_INFO 0x00000002
+
+/* Must pass video information to OS. */
+#define MULTIBOOT_VIDEO_MODE 0x00000004
+
+/* This flag indicates the use of the address fields in the header. */
+#define MULTIBOOT_AOUT_KLUDGE 0x00010000
+
+/* Flags to be set in the 'flags' member of the multiboot info structure. */
+
+/* is there basic lower/upper memory information? */
+#define MULTIBOOT_INFO_MEMORY 0x00000001
+/* is there a boot device set? */
+#define MULTIBOOT_INFO_BOOTDEV 0x00000002
+/* is the command-line defined? */
+#define MULTIBOOT_INFO_CMDLINE 0x00000004
+/* are there modules to do something with? */
+#define MULTIBOOT_INFO_MODS 0x00000008
+
+/* These next two are mutually exclusive */
+
+/* is there a symbol table loaded? */
+#define MULTIBOOT_INFO_AOUT_SYMS 0x00000010
+/* is there an ELF section header table? */
+#define MULTIBOOT_INFO_ELF_SHDR 0X00000020
+
+/* is there a full memory map? */
+#define MULTIBOOT_INFO_MEM_MAP 0x00000040
+
+/* Is there drive info? */
+#define MULTIBOOT_INFO_DRIVE_INFO 0x00000080
+
+/* Is there a config table? */
+#define MULTIBOOT_INFO_CONFIG_TABLE 0x00000100
+
+/* Is there a boot loader name? */
+#define MULTIBOOT_INFO_BOOT_LOADER_NAME 0x00000200
+
+/* Is there a APM table? */
+#define MULTIBOOT_INFO_APM_TABLE 0x00000400
+
+/* Is there video information? */
+#define MULTIBOOT_INFO_VIDEO_INFO 0x00000800
+
+#ifndef ASM_FILE
+
+typedef unsigned short multiboot_uint16_t;
+typedef unsigned int multiboot_uint32_t;
+typedef unsigned long long multiboot_uint64_t;
+
+struct multiboot_header
+{
+/* Must be MULTIBOOT_MAGIC - see above. */
+ multiboot_uint32_t magic;
+
+/* Feature flags. */
+ multiboot_uint32_t flags;
+
+/* The above fields plus this one must equal 0 mod 2^32. */
+ multiboot_uint32_t checksum;
+
+/* These are only valid if MULTIBOOT_AOUT_KLUDGE is set. */
+ multiboot_uint32_t header_addr;
+ multiboot_uint32_t load_addr;
+ multiboot_uint32_t load_end_addr;
+ multiboot_uint32_t bss_end_addr;
+ multiboot_uint32_t entry_addr;
+
+/* These are only valid if MULTIBOOT_VIDEO_MODE is set. */
+ multiboot_uint32_t mode_type;
+ multiboot_uint32_t width;
+ multiboot_uint32_t height;
+ multiboot_uint32_t depth;
+};
+
+/* The symbol table for a.out. */
+struct multiboot_aout_symbol_table
+{
+ multiboot_uint32_t tabsize;
+ multiboot_uint32_t strsize;
+ multiboot_uint32_t addr;
+ multiboot_uint32_t reserved;
+};
+typedef struct multiboot_aout_symbol_table multiboot_aout_symbol_table_t;
+
+/* The section header table for ELF. */
+struct multiboot_elf_section_header_table
+{
+ multiboot_uint32_t num;
+ multiboot_uint32_t size;
+ multiboot_uint32_t addr;
+ multiboot_uint32_t shndx;
+};
+typedef struct multiboot_elf_section_header_table multiboot_elf_section_header_table_t;
+
+struct multiboot_info
+{
+/* Multiboot info version number */
+ multiboot_uint32_t flags;
+
+/* Available memory from BIOS */
+ multiboot_uint32_t mem_lower;
+ multiboot_uint32_t mem_upper;
+
+/* "root" partition */
+ multiboot_uint32_t boot_device;
+
+/* Kernel command line */
+ multiboot_uint32_t cmdline;
+
+/* Boot-Module list */
+ multiboot_uint32_t mods_count;
+ multiboot_uint32_t mods_addr;
+
+ union
+ {
+ multiboot_aout_symbol_table_t aout_sym;
+ multiboot_elf_section_header_table_t elf_sec;
+ } u;
+
+/* Memory Mapping buffer */
+ multiboot_uint32_t mmap_length;
+ multiboot_uint32_t mmap_addr;
+
+/* Drive Info buffer */
+ multiboot_uint32_t drives_length;
+ multiboot_uint32_t drives_addr;
+
+/* ROM configuration table */
+ multiboot_uint32_t config_table;
+
+/* Boot Loader Name */
+ multiboot_uint32_t boot_loader_name;
+
+/* APM table */
+ multiboot_uint32_t apm_table;
+
+/* Video */
+ multiboot_uint32_t vbe_control_info;
+ multiboot_uint32_t vbe_mode_info;
+ multiboot_uint16_t vbe_mode;
+ multiboot_uint16_t vbe_interface_seg;
+ multiboot_uint16_t vbe_interface_off;
+ multiboot_uint16_t vbe_interface_len;
+};
+typedef struct multiboot_info multiboot_info_t;
+
+struct multiboot_mmap_entry
+{
+ multiboot_uint32_t size;
+ multiboot_uint64_t addr;
+ multiboot_uint64_t len;
+#define MULTIBOOT_MEMORY_AVAILABLE 1
+#define MULTIBOOT_MEMORY_RESERVED 2
+ multiboot_uint32_t type;
+} __attribute__((packed));
+typedef struct multiboot_mmap_entry multiboot_memory_map_t;
+
+struct multiboot_mod_list
+{
+/* the memory used goes from bytes 'mod_start' to 'mod_end-1' inclusive */
+ multiboot_uint32_t mod_start;
+ multiboot_uint32_t mod_end;
+
+/* Module command line */
+ multiboot_uint32_t cmdline;
+
+/* padding to take it to 16 bytes (must be zero) */
+ multiboot_uint32_t pad;
+};
+typedef struct multiboot_mod_list multiboot_module_t;
+
+#endif /* ! ASM_FILE */
+
+#endif /* ! MULTIBOOT_HEADER */
diff --git a/stand/i386/libi386/multiboot_tramp.S b/stand/i386/libi386/multiboot_tramp.S
new file mode 100644
index 0000000..0bd6043
--- /dev/null
+++ b/stand/i386/libi386/multiboot_tramp.S
@@ -0,0 +1,51 @@
+/*-
+ * Copyright (c) 2014 Roger Pau Monné <royger@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$
+ */
+
+#define ASM_FILE
+#include "multiboot.h"
+
+/*
+ * The multiboot specification requires the executable to be launched
+ * with %cs set to a flat read/execute segment with offset 0 and limit
+ * 0xFFFFFFFF, and the rest of the segment registers (%ds, %es, %fs,
+ * %gs, %ss) to flat read/write segments with the same offset and limit.
+ * This is already done by the BTX code before calling multiboot_tramp,
+ * so there is no need to do anything here.
+ */
+
+ .globl multiboot_tramp
+multiboot_tramp:
+ /* Be sure that interrupts are disabled. */
+ cli
+
+ movl $MULTIBOOT_BOOTLOADER_MAGIC, %eax
+ /* Get the entry point and address of the multiboot_info parameter. */
+ movl 8(%esp), %ebx
+ movl 4(%esp), %ecx
+
+ call *%ecx
diff --git a/stand/i386/libi386/nullconsole.c b/stand/i386/libi386/nullconsole.c
new file mode 100644
index 0000000..ebb1e7e
--- /dev/null
+++ b/stand/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/stand/i386/libi386/pread.c b/stand/i386/libi386/pread.c
new file mode 100644
index 0000000..870e254
--- /dev/null
+++ b/stand/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/stand/i386/libi386/pxe.c b/stand/i386/libi386/pxe.c
new file mode 100644
index 0000000..7cb4833
--- /dev/null
+++ b/stand/i386/libi386/pxe.c
@@ -0,0 +1,540 @@
+/*-
+ * 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 <stddef.h>
+#include <string.h>
+#include <stdarg.h>
+#include <sys/param.h>
+
+#include <net/ethernet.h>
+#include <netinet/in_systm.h>
+#include <netinet/in.h>
+#include <netinet/ip.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
+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 */
+
+#ifdef PXE_DEBUG
+static int pxe_debug = 0;
+#endif
+
+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_print(int verbose);
+static void pxe_cleanup(void);
+
+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 ssize_t pxe_netif_get(struct iodesc *, void **, time_t);
+static ssize_t 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[nitems(pxe_ifs)];
+
+struct netif_driver pxenetif = {
+ .netif_bname = "pxenet",
+ .netif_match = pxe_netif_match,
+ .netif_probe = pxe_netif_probe,
+ .netif_init = pxe_netif_init,
+ .netif_get = pxe_netif_get,
+ .netif_put = pxe_netif_put,
+ .netif_end = pxe_netif_end,
+ .netif_ifs = pxe_ifs,
+ .netif_nifs = nitems(pxe_ifs)
+};
+
+struct netif_driver *netif_drivers[] = {
+ &pxenetif,
+ NULL
+};
+
+struct devsw pxedisk = {
+ .dv_name = "net",
+ .dv_type = DEVT_NET,
+ .dv_init = pxe_init,
+ .dv_strategy = NULL, /* Will be set in pxe_init */
+ .dv_open = NULL, /* Will be set in pxe_init */
+ .dv_close = NULL, /* Will be set in pxe_init */
+ .dv_ioctl = noioctl,
+ .dv_print = pxe_print,
+ .dv_cleanup = 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;
+ extern struct devsw netdev;
+
+ 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;
+ }
+ }
+
+ pxedisk.dv_open = netdev.dv_open;
+ pxedisk.dv_close = netdev.dv_close;
+ pxedisk.dv_strategy = netdev.dv_strategy;
+
+ 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);
+ }
+ free(bootp_response);
+ if ((bootp_response = malloc(gci_p->BufferSize)) != NULL) {
+ bootp_response_size = gci_p->BufferSize;
+ bcopy(PTOV((gci_p->Buffer.segment << 4) + gci_p->Buffer.offset),
+ bootp_response, bootp_response_size);
+ }
+ return (1);
+}
+
+static int
+pxe_print(int verbose)
+{
+ if (pxe_call == NULL)
+ return (0);
+
+ printf("%s devices:", pxedisk.dv_name);
+ if (pager_output("\n") != 0)
+ return (1);
+ printf(" %s0:", pxedisk.dv_name);
+ if (verbose) {
+ printf(" %s:%s", inet_ntoa(rootip), rootpath);
+ }
+ return (pager_output("\n"));
+}
+
+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;
+}
+
+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;
+}
+
+
+static int
+pxe_netif_match(struct netif *nif, void *machdep_hint)
+{
+ return (1);
+}
+
+static int
+pxe_netif_probe(struct netif *nif, void *machdep_hint)
+{
+ if (pxe_call == NULL)
+ return (-1);
+
+ return (0);
+}
+
+static void
+pxe_netif_end(struct netif *nif)
+{
+ t_PXENV_UNDI_CLOSE *undi_close_p;
+
+ undi_close_p = (t_PXENV_UNDI_CLOSE *)scratch_buffer;
+ bzero(undi_close_p, sizeof(*undi_close_p));
+ pxe_call(PXENV_UNDI_CLOSE);
+ if (undi_close_p->Status != 0)
+ printf("undi close failed: %x\n", undi_close_p->Status);
+}
+
+static void
+pxe_netif_init(struct iodesc *desc, void *machdep_hint)
+{
+ t_PXENV_UNDI_GET_INFORMATION *undi_info_p;
+ t_PXENV_UNDI_OPEN *undi_open_p;
+ uint8_t *mac;
+ int i, len;
+
+ undi_info_p = (t_PXENV_UNDI_GET_INFORMATION *)scratch_buffer;
+ bzero(undi_info_p, sizeof(*undi_info_p));
+ pxe_call(PXENV_UNDI_GET_INFORMATION);
+ if (undi_info_p->Status != 0) {
+ printf("undi get info failed: %x\n", undi_info_p->Status);
+ return;
+ }
+
+ /* Make sure the CurrentNodeAddress is valid. */
+ for (i = 0; i < undi_info_p->HwAddrLen; ++i) {
+ if (undi_info_p->CurrentNodeAddress[i] != 0)
+ break;
+ }
+ if (i < undi_info_p->HwAddrLen) {
+ for (i = 0; i < undi_info_p->HwAddrLen; ++i) {
+ if (undi_info_p->CurrentNodeAddress[i] != 0xff)
+ break;
+ }
+ }
+ if (i < undi_info_p->HwAddrLen)
+ mac = undi_info_p->CurrentNodeAddress;
+ else
+ mac = undi_info_p->PermNodeAddress;
+
+ len = min(sizeof (desc->myea), undi_info_p->HwAddrLen);
+ for (i = 0; i < len; ++i)
+ desc->myea[i] = mac[i];
+
+ if (bootp_response != NULL)
+ desc->xid = bootp_response->bp_xid;
+ else
+ desc->xid = 0;
+
+ undi_open_p = (t_PXENV_UNDI_OPEN *)scratch_buffer;
+ bzero(undi_open_p, sizeof(*undi_open_p));
+ undi_open_p->PktFilter = FLTR_DIRECTED | FLTR_BRDCST;
+ pxe_call(PXENV_UNDI_OPEN);
+ if (undi_open_p->Status != 0)
+ printf("undi open failed: %x\n", undi_open_p->Status);
+}
+
+static int
+pxe_netif_receive(void **pkt)
+{
+ t_PXENV_UNDI_ISR *isr = (t_PXENV_UNDI_ISR *)scratch_buffer;
+ char *buf, *ptr, *frame;
+ size_t size, rsize;
+
+ bzero(isr, sizeof(*isr));
+ isr->FuncFlag = PXENV_UNDI_ISR_IN_START;
+ pxe_call(PXENV_UNDI_ISR);
+ if (isr->Status != 0)
+ return (-1);
+
+ bzero(isr, sizeof(*isr));
+ isr->FuncFlag = PXENV_UNDI_ISR_IN_PROCESS;
+ pxe_call(PXENV_UNDI_ISR);
+ if (isr->Status != 0)
+ return (-1);
+
+ while (isr->FuncFlag == PXENV_UNDI_ISR_OUT_TRANSMIT) {
+ /*
+ * Wait till transmit is done.
+ */
+ bzero(isr, sizeof(*isr));
+ isr->FuncFlag = PXENV_UNDI_ISR_IN_GET_NEXT;
+ pxe_call(PXENV_UNDI_ISR);
+ if (isr->Status != 0 ||
+ isr->FuncFlag == PXENV_UNDI_ISR_OUT_DONE)
+ return (-1);
+ }
+
+ while (isr->FuncFlag != PXENV_UNDI_ISR_OUT_RECEIVE) {
+ if (isr->Status != 0 ||
+ isr->FuncFlag == PXENV_UNDI_ISR_OUT_DONE) {
+ return (-1);
+ }
+ bzero(isr, sizeof(*isr));
+ isr->FuncFlag = PXENV_UNDI_ISR_IN_GET_NEXT;
+ pxe_call(PXENV_UNDI_ISR);
+ }
+
+ size = isr->FrameLength;
+ buf = malloc(size + ETHER_ALIGN);
+ if (buf == NULL)
+ return (-1);
+ ptr = buf + ETHER_ALIGN;
+ rsize = 0;
+
+ while (rsize < size) {
+ frame = (char *)((uintptr_t)isr->Frame.segment << 4);
+ frame += isr->Frame.offset;
+ bcopy(PTOV(frame), ptr, isr->BufferLength);
+ ptr += isr->BufferLength;
+ rsize += isr->BufferLength;
+
+ bzero(isr, sizeof(*isr));
+ isr->FuncFlag = PXENV_UNDI_ISR_IN_GET_NEXT;
+ pxe_call(PXENV_UNDI_ISR);
+ if (isr->Status != 0) {
+ free(buf);
+ return (-1);
+ }
+
+ /* Did we got another update? */
+ if (isr->FuncFlag == PXENV_UNDI_ISR_OUT_RECEIVE)
+ continue;
+ break;
+ }
+
+ *pkt = buf;
+ return (rsize);
+}
+
+static ssize_t
+pxe_netif_get(struct iodesc *desc, void **pkt, time_t timeout)
+{
+ time_t t;
+ void *ptr;
+ int ret = -1;
+
+ t = getsecs();
+ while ((getsecs() - t) < timeout) {
+ ret = pxe_netif_receive(&ptr);
+ if (ret != -1) {
+ *pkt = ptr;
+ break;
+ }
+ }
+ return (ret);
+}
+
+static ssize_t
+pxe_netif_put(struct iodesc *desc, void *pkt, size_t len)
+{
+ t_PXENV_UNDI_TRANSMIT *trans_p;
+ t_PXENV_UNDI_TBD *tbd_p;
+ char *data;
+
+ trans_p = (t_PXENV_UNDI_TRANSMIT *)scratch_buffer;
+ bzero(trans_p, sizeof(*trans_p));
+ tbd_p = (t_PXENV_UNDI_TBD *)(scratch_buffer + sizeof(*trans_p));
+ bzero(tbd_p, sizeof(*tbd_p));
+
+ data = scratch_buffer + sizeof(*trans_p) + sizeof(*tbd_p);
+
+ trans_p->TBD.segment = VTOPSEG(tbd_p);
+ trans_p->TBD.offset = VTOPOFF(tbd_p);
+
+ tbd_p->ImmedLength = len;
+ tbd_p->Xmit.segment = VTOPSEG(data);
+ tbd_p->Xmit.offset = VTOPOFF(data);
+ bcopy(pkt, data, len);
+
+ pxe_call(PXENV_UNDI_TRANSMIT);
+ if (trans_p->Status != 0) {
+ return (-1);
+ }
+
+ return (len);
+}
diff --git a/stand/i386/libi386/pxe.h b/stand/i386/libi386/pxe.h
new file mode 100644
index 0000000..119c86b
--- /dev/null
+++ b/stand/i386/libi386/pxe.h
@@ -0,0 +1,513 @@
+/*
+ * 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 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 0x0004
+# define FLTR_SRC_RTG 0x0008
+
+ 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 address 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 receiver 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 returned 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_RECEIVE 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/stand/i386/libi386/pxetramp.s b/stand/i386/libi386/pxetramp.s
new file mode 100644
index 0000000..dcf1441
--- /dev/null
+++ b/stand/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/stand/i386/libi386/relocater_tramp.S b/stand/i386/libi386/relocater_tramp.S
new file mode 100644
index 0000000..6db4a75
--- /dev/null
+++ b/stand/i386/libi386/relocater_tramp.S
@@ -0,0 +1,358 @@
+/*-
+ * Copyright 2015 Toomas Soome <tsoome@me.com>
+ * 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$
+ */
+
+
+/*
+ * relocate is needed to support loading code which has to be located
+ * below 1MB, as both BTX and loader are using low memory area.
+ *
+ * relocate and start loaded code. Since loaded code may need to be
+ * placed to already occupied memory area, this code is moved to safe
+ * memory area and then btx __exec will be called with physical pointer
+ * to this area. __exec will set pointer to %eax and use call *%eax,
+ * so on entry, we have new "base" address in %eax.
+ *
+ * Relocate will first set up and load new safe GDT to shut down BTX,
+ * then loaded code will be relocated to final memory location,
+ * then machine will be switched from 32bit protected mode to 16bit
+ * protected mode following by switch to real mode with A20 enabled or
+ * disabled. Finally the loaded code will be started and it will take
+ * over the whole system.
+ *
+ * For now, the known "safe" memory area for relocate is 0x600,
+ * the actual "free" memory is supposed to start from 0x500, leaving
+ * first 0x100 bytes in reserve. As relocate code+data is very small,
+ * it will leave enough space to set up boot blocks to 0:7c00 or load
+ * linux kernel below 1MB space.
+ */
+/*
+ * segment selectors
+ */
+ .set SEL_SCODE,0x8
+ .set SEL_SDATA,0x10
+ .set SEL_RCODE,0x18
+ .set SEL_RDATA,0x20
+
+ .p2align 4
+ .globl relocater
+relocater:
+ cli
+ /*
+ * set up GDT from new location
+ */
+ movl %eax, %esi /* our base address */
+ add $(relocater.1-relocater), %eax
+ jmp *%eax
+relocater.1:
+ /* set up jump */
+ lea (relocater.2-relocater)(%esi), %eax
+ movl %eax, (jump_vector-relocater) (%esi)
+
+ /* set up gdt */
+ lea (gdt-relocater) (%esi), %eax
+ movl %eax, (gdtaddr-relocater) (%esi)
+
+ /* load gdt */
+ lgdt (gdtdesc - relocater) (%esi)
+ lidt (idt-relocater) (%esi)
+
+ /* update cs */
+ ljmp *(jump_vector-relocater) (%esi)
+
+ .code32
+relocater.2:
+ xorl %eax, %eax
+ movb $SEL_SDATA, %al
+ movw %ax, %ss
+ movw %ax, %ds
+ movw %ax, %es
+ movw %ax, %fs
+ movw %ax, %gs
+ movl %cr0, %eax /* disable paging */
+ andl $~0x80000000,%eax
+ movl %eax, %cr0
+ xorl %ecx, %ecx /* flush TLB */
+ movl %ecx, %cr3
+ cld
+/*
+ * relocate data loop. load source, dest and size from
+ * relocater_data[i], 0 value will stop the loop.
+ * registers used for move: %esi, %edi, %ecx.
+ * %ebx to keep base
+ * %edx for relocater_data offset
+ */
+ movl %esi, %ebx /* base address */
+ xorl %edx, %edx
+loop.1:
+ movl (relocater_data-relocater)(%ebx, %edx, 4), %eax
+ testl %eax, %eax
+ jz loop.2
+ movl (relocater_data-relocater)(%ebx, %edx, 4), %esi
+ inc %edx
+ movl (relocater_data-relocater)(%ebx, %edx, 4), %edi
+ inc %edx
+ movl (relocater_data-relocater)(%ebx, %edx, 4), %ecx
+ inc %edx
+ rep
+ movsb
+ jmp loop.1
+loop.2:
+ movl %ebx, %esi /* restore esi */
+ /*
+ * data is relocated, switch to 16bit mode
+ */
+ lea (relocater.3-relocater)(%esi), %eax
+ movl %eax, (jump_vector-relocater) (%esi)
+ movl $SEL_RCODE, %eax
+ movl %eax, (jump_vector-relocater+4) (%esi)
+
+ ljmp *(jump_vector-relocater) (%esi)
+relocater.3:
+ .code16
+
+ movw $SEL_RDATA, %ax
+ movw %ax, %ds
+ movw %ax, %es
+ movw %ax, %fs
+ movw %ax, %gs
+ movw %ax, %ss
+ lidt (idt-relocater) (%esi)
+ lea (relocater.4-relocater)(%esi), %eax
+ movl %eax, (jump_vector-relocater) (%esi)
+ xorl %eax, %eax
+ movl %eax, (jump_vector-relocater+4) (%esi)
+ /* clear PE */
+ movl %cr0, %eax
+ dec %al
+ movl %eax, %cr0
+ ljmp *(jump_vector-relocater) (%esi)
+relocater.4:
+ xorw %ax, %ax
+ movw %ax, %ds
+ movw %ax, %es
+ movw %ax, %fs
+ movw %ax, %gs
+ movw %ax, %ss
+ /*
+ * set real mode irq offsets
+ */
+ movw $0x7008,%bx
+ 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
+ # done
+ /*
+ * Should A20 be left enabled?
+ */
+ /* movw imm16, %ax */
+ .byte 0xb8
+ .globl relocator_a20_enabled
+relocator_a20_enabled:
+ .word 0
+ test %ax, %ax
+ jnz a20_done
+
+ movw $0xa00, %ax
+ movw %ax, %sp
+ movw %ax, %bp
+
+ /* Disable A20 */
+ movw $0x2400, %ax
+ int $0x15
+# jnc a20_done
+
+ call a20_check_state
+ testb %al, %al
+ jz a20_done
+
+ inb $0x92
+ andb $(~0x03), %al
+ outb $0x92
+ jmp a20_done
+
+a20_check_state:
+ movw $100, %cx
+1:
+ xorw %ax, %ax
+ movw %ax, %ds
+ decw %ax
+ movw %ax, %es
+ xorw %ax, %ax
+ movw $0x8000, %ax
+ movw %ax, %si
+ addw $0x10, %ax
+ movw %ax, %di
+ movb %ds:(%si), %dl
+ movb %es:(%di), %al
+ movb %al, %dh
+ decb %dh
+ movb %dh, %ds:(%si)
+ outb %al, $0x80
+ outb %al, $0x80
+ movb %es:(%di), %dh
+ subb %dh, %al
+ xorb $1, %al
+ movb %dl, %ds:(%si)
+ testb %al, %al
+ jz a20_done
+ loop 1b
+ ret
+a20_done:
+ /*
+ * set up registers
+ */
+ /* movw imm16, %ax. */
+ .byte 0xb8
+ .globl relocator_ds
+relocator_ds: .word 0
+ movw %ax, %ds
+
+ /* movw imm16, %ax. */
+ .byte 0xb8
+ .globl relocator_es
+relocator_es: .word 0
+ movw %ax, %es
+
+ /* movw imm16, %ax. */
+ .byte 0xb8
+ .globl relocator_fs
+relocator_fs: .word 0
+ movw %ax, %fs
+
+ /* movw imm16, %ax. */
+ .byte 0xb8
+ .globl relocator_gs
+relocator_gs: .word 0
+ movw %ax, %gs
+
+ /* movw imm16, %ax. */
+ .byte 0xb8
+ .globl relocator_ss
+relocator_ss: .word 0
+ movw %ax, %ss
+
+ /* movw imm16, %ax. */
+ .byte 0xb8
+ .globl relocator_sp
+relocator_sp: .word 0
+ movzwl %ax, %esp
+
+ /* movw imm32, %eax. */
+ .byte 0x66, 0xb8
+ .globl relocator_esi
+relocator_esi: .long 0
+ movl %eax, %esi
+
+ /* movw imm32, %edx. */
+ .byte 0x66, 0xba
+ .globl relocator_edx
+relocator_edx: .long 0
+
+ /* movw imm32, %ebx. */
+ .byte 0x66, 0xbb
+ .globl relocator_ebx
+relocator_ebx: .long 0
+
+ /* movw imm32, %eax. */
+ .byte 0x66, 0xb8
+ .globl relocator_eax
+relocator_eax: .long 0
+
+ /* movw imm32, %ebp. */
+ .byte 0x66, 0xbd
+ .globl relocator_ebp
+relocator_ebp: .long 0
+
+ sti
+ .byte 0xea /* ljmp */
+ .globl relocator_ip
+relocator_ip:
+ .word 0
+ .globl relocator_cs
+relocator_cs:
+ .word 0
+
+/* GDT to reset BTX */
+ .code32
+ .p2align 4
+jump_vector: .long 0
+ .long SEL_SCODE
+
+gdt: .word 0x0, 0x0 /* null entry */
+ .byte 0x0, 0x0, 0x0, 0x0
+ .word 0xffff, 0x0 /* SEL_SCODE */
+ .byte 0x0, 0x9a, 0xcf, 0x0
+ .word 0xffff, 0x0 /* SEL_SDATA */
+ .byte 0x0, 0x92, 0xcf, 0x0
+ .word 0xffff, 0x0 /* SEL_RCODE */
+ .byte 0x0, 0x9a, 0x0f, 0x0
+ .word 0xffff, 0x0 /* SEL_RDATA */
+ .byte 0x0, 0x92, 0x0f, 0x0
+gdt.1:
+
+gdtdesc: .word gdt.1 - gdt - 1 /* limit */
+gdtaddr: .long 0 /* base */
+
+idt: .word 0x3ff
+ .long 0
+
+ .globl relocater_data
+relocater_data:
+ .long 0 /* src */
+ .long 0 /* dest */
+ .long 0 /* size */
+ .long 0 /* src */
+ .long 0 /* dest */
+ .long 0 /* size */
+ .long 0 /* src */
+ .long 0 /* dest */
+ .long 0 /* size */
+ .long 0
+
+ .globl relocater_size
+relocater_size:
+ .long relocater_size-relocater
diff --git a/stand/i386/libi386/smbios.c b/stand/i386/libi386/smbios.c
new file mode 100644
index 0000000..2aa62fa
--- /dev/null
+++ b/stand/i386/libi386/smbios.c
@@ -0,0 +1,454 @@
+/*-
+ * Copyright (c) 2005-2009 Jung-uk Kim <jkim@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 <sys/endian.h>
+
+#ifdef EFI
+/* In EFI, we don't need PTOV(). */
+#define PTOV(x) (caddr_t)(x)
+#else
+#include "btxv86.h"
+#endif
+#include "smbios.h"
+
+/*
+ * Detect SMBIOS and export information about the SMBIOS into the
+ * environment.
+ *
+ * System Management BIOS Reference Specification, v2.6 Final
+ * http://www.dmtf.org/standards/published_documents/DSP0134_2.6.0.pdf
+ */
+
+/*
+ * 2.1.1 SMBIOS Structure Table Entry Point
+ *
+ * "On non-EFI systems, the SMBIOS Entry Point structure, described below, can
+ * be located by application software by searching for the anchor-string on
+ * paragraph (16-byte) boundaries within the physical memory address range
+ * 000F0000h to 000FFFFFh. This entry point encapsulates an intermediate anchor
+ * string that is used by some existing DMI browsers."
+ */
+#define SMBIOS_START 0xf0000
+#define SMBIOS_LENGTH 0x10000
+#define SMBIOS_STEP 0x10
+#define SMBIOS_SIG "_SM_"
+#define SMBIOS_DMI_SIG "_DMI_"
+
+#define SMBIOS_GET8(base, off) (*(uint8_t *)((base) + (off)))
+#define SMBIOS_GET16(base, off) (*(uint16_t *)((base) + (off)))
+#define SMBIOS_GET32(base, off) (*(uint32_t *)((base) + (off)))
+
+#define SMBIOS_GETLEN(base) SMBIOS_GET8(base, 0x01)
+#define SMBIOS_GETSTR(base) ((base) + SMBIOS_GETLEN(base))
+
+struct smbios_attr {
+ int probed;
+ caddr_t addr;
+ size_t length;
+ size_t count;
+ int major;
+ int minor;
+ int ver;
+ const char* bios_vendor;
+ const char* maker;
+ const char* product;
+ uint32_t enabled_memory;
+ uint32_t old_enabled_memory;
+ uint8_t enabled_sockets;
+ uint8_t populated_sockets;
+};
+
+static struct smbios_attr smbios;
+
+static uint8_t
+smbios_checksum(const caddr_t addr, const uint8_t len)
+{
+ uint8_t sum;
+ int i;
+
+ for (sum = 0, i = 0; i < len; i++)
+ sum += SMBIOS_GET8(addr, i);
+ return (sum);
+}
+
+static caddr_t
+smbios_sigsearch(const caddr_t addr, const uint32_t len)
+{
+ caddr_t cp;
+
+ /* Search on 16-byte boundaries. */
+ for (cp = addr; cp < addr + len; cp += SMBIOS_STEP)
+ if (strncmp(cp, SMBIOS_SIG, 4) == 0 &&
+ smbios_checksum(cp, SMBIOS_GET8(cp, 0x05)) == 0 &&
+ strncmp(cp + 0x10, SMBIOS_DMI_SIG, 5) == 0 &&
+ smbios_checksum(cp + 0x10, 0x0f) == 0)
+ return (cp);
+ return (NULL);
+}
+
+static const char*
+smbios_getstring(caddr_t addr, const int offset)
+{
+ caddr_t cp;
+ int i, idx;
+
+ idx = SMBIOS_GET8(addr, offset);
+ if (idx != 0) {
+ cp = SMBIOS_GETSTR(addr);
+ for (i = 1; i < idx; i++)
+ cp += strlen(cp) + 1;
+ return cp;
+ }
+ return (NULL);
+}
+
+static void
+smbios_setenv(const char *name, caddr_t addr, const int offset)
+{
+ const char* val;
+
+ val = smbios_getstring(addr, offset);
+ if (val != NULL)
+ setenv(name, val, 1);
+}
+
+#ifdef SMBIOS_SERIAL_NUMBERS
+
+#define UUID_SIZE 16
+#define UUID_TYPE uint32_t
+#define UUID_STEP sizeof(UUID_TYPE)
+#define UUID_ALL_BITS (UUID_SIZE / UUID_STEP)
+#define UUID_GET(base, off) (*(UUID_TYPE *)((base) + (off)))
+
+static void
+smbios_setuuid(const char *name, const caddr_t addr, const int ver)
+{
+ char uuid[37];
+ int byteorder, i, ones, zeros;
+ UUID_TYPE n;
+ uint32_t f1;
+ uint16_t f2, f3;
+
+ for (i = 0, ones = 0, zeros = 0; i < UUID_SIZE; i += UUID_STEP) {
+ n = UUID_GET(addr, i) + 1;
+ if (zeros == 0 && n == 0)
+ ones++;
+ else if (ones == 0 && n == 1)
+ zeros++;
+ else
+ break;
+ }
+
+ if (ones != UUID_ALL_BITS && zeros != UUID_ALL_BITS) {
+ /*
+ * 3.3.2.1 System UUID
+ *
+ * "Although RFC 4122 recommends network byte order for all
+ * fields, the PC industry (including the ACPI, UEFI, and
+ * Microsoft specifications) has consistently used
+ * little-endian byte encoding for the first three fields:
+ * time_low, time_mid, time_hi_and_version. The same encoding,
+ * also known as wire format, should also be used for the
+ * SMBIOS representation of the UUID."
+ *
+ * Note: We use network byte order for backward compatibility
+ * unless SMBIOS version is 2.6+ or little-endian is forced.
+ */
+#if defined(SMBIOS_LITTLE_ENDIAN_UUID)
+ byteorder = LITTLE_ENDIAN;
+#elif defined(SMBIOS_NETWORK_ENDIAN_UUID)
+ byteorder = BIG_ENDIAN;
+#else
+ byteorder = ver < 0x0206 ? BIG_ENDIAN : LITTLE_ENDIAN;
+#endif
+ if (byteorder != LITTLE_ENDIAN) {
+ f1 = ntohl(SMBIOS_GET32(addr, 0));
+ f2 = ntohs(SMBIOS_GET16(addr, 4));
+ f3 = ntohs(SMBIOS_GET16(addr, 6));
+ } else {
+ f1 = le32toh(SMBIOS_GET32(addr, 0));
+ f2 = le16toh(SMBIOS_GET16(addr, 4));
+ f3 = le16toh(SMBIOS_GET16(addr, 6));
+ }
+ sprintf(uuid,
+ "%08x-%04x-%04x-%02x%02x-%02x%02x%02x%02x%02x%02x",
+ f1, f2, f3, SMBIOS_GET8(addr, 8), SMBIOS_GET8(addr, 9),
+ SMBIOS_GET8(addr, 10), SMBIOS_GET8(addr, 11),
+ SMBIOS_GET8(addr, 12), SMBIOS_GET8(addr, 13),
+ SMBIOS_GET8(addr, 14), SMBIOS_GET8(addr, 15));
+ setenv(name, uuid, 1);
+ }
+}
+
+#undef UUID_SIZE
+#undef UUID_TYPE
+#undef UUID_STEP
+#undef UUID_ALL_BITS
+#undef UUID_GET
+
+#endif
+
+static caddr_t
+smbios_parse_table(const caddr_t addr)
+{
+ caddr_t cp;
+ int proc, size, osize, type;
+
+ type = SMBIOS_GET8(addr, 0); /* 3.1.2 Structure Header Format */
+ switch(type) {
+ case 0: /* 3.3.1 BIOS Information (Type 0) */
+ smbios_setenv("smbios.bios.vendor", addr, 0x04);
+ smbios_setenv("smbios.bios.version", addr, 0x05);
+ smbios_setenv("smbios.bios.reldate", addr, 0x08);
+ break;
+
+ case 1: /* 3.3.2 System Information (Type 1) */
+ smbios_setenv("smbios.system.maker", addr, 0x04);
+ smbios_setenv("smbios.system.product", addr, 0x05);
+ smbios_setenv("smbios.system.version", addr, 0x06);
+#ifdef SMBIOS_SERIAL_NUMBERS
+ smbios_setenv("smbios.system.serial", addr, 0x07);
+ smbios_setuuid("smbios.system.uuid", addr + 0x08, smbios.ver);
+#endif
+ if (smbios.major > 2 ||
+ (smbios.major == 2 && smbios.minor >= 4)) {
+ smbios_setenv("smbios.system.sku", addr, 0x19);
+ smbios_setenv("smbios.system.family", addr, 0x1a);
+ }
+ break;
+
+ case 2: /* 3.3.3 Base Board (or Module) Information (Type 2) */
+ smbios_setenv("smbios.planar.maker", addr, 0x04);
+ smbios_setenv("smbios.planar.product", addr, 0x05);
+ smbios_setenv("smbios.planar.version", addr, 0x06);
+#ifdef SMBIOS_SERIAL_NUMBERS
+ smbios_setenv("smbios.planar.serial", addr, 0x07);
+ smbios_setenv("smbios.planar.tag", addr, 0x08);
+#endif
+ smbios_setenv("smbios.planar.location", addr, 0x0a);
+ break;
+
+ case 3: /* 3.3.4 System Enclosure or Chassis (Type 3) */
+ smbios_setenv("smbios.chassis.maker", addr, 0x04);
+ smbios_setenv("smbios.chassis.version", addr, 0x06);
+#ifdef SMBIOS_SERIAL_NUMBERS
+ smbios_setenv("smbios.chassis.serial", addr, 0x07);
+ smbios_setenv("smbios.chassis.tag", addr, 0x08);
+#endif
+ break;
+
+ case 4: /* 3.3.5 Processor Information (Type 4) */
+ /*
+ * Offset 18h: Processor Status
+ *
+ * Bit 7 Reserved, must be 0
+ * Bit 6 CPU Socket Populated
+ * 1 - CPU Socket Populated
+ * 0 - CPU Socket Unpopulated
+ * Bit 5:3 Reserved, must be zero
+ * Bit 2:0 CPU Status
+ * 0h - Unknown
+ * 1h - CPU Enabled
+ * 2h - CPU Disabled by User via BIOS Setup
+ * 3h - CPU Disabled by BIOS (POST Error)
+ * 4h - CPU is Idle, waiting to be enabled
+ * 5-6h - Reserved
+ * 7h - Other
+ */
+ proc = SMBIOS_GET8(addr, 0x18);
+ if ((proc & 0x07) == 1)
+ smbios.enabled_sockets++;
+ if ((proc & 0x40) != 0)
+ smbios.populated_sockets++;
+ break;
+
+ case 6: /* 3.3.7 Memory Module Information (Type 6, Obsolete) */
+ /*
+ * Offset 0Ah: Enabled Size
+ *
+ * Bit 7 Bank connection
+ * 1 - Double-bank connection
+ * 0 - Single-bank connection
+ * Bit 6:0 Size (n), where 2**n is the size in MB
+ * 7Dh - Not determinable (Installed Size only)
+ * 7Eh - Module is installed, but no memory
+ * has been enabled
+ * 7Fh - Not installed
+ */
+ osize = SMBIOS_GET8(addr, 0x0a) & 0x7f;
+ if (osize > 0 && osize < 22)
+ smbios.old_enabled_memory += 1 << (osize + 10);
+ break;
+
+ case 17: /* 3.3.18 Memory Device (Type 17) */
+ /*
+ * Offset 0Ch: Size
+ *
+ * Bit 15 Granularity
+ * 1 - Value is in kilobytes units
+ * 0 - Value is in megabytes units
+ * Bit 14:0 Size
+ */
+ size = SMBIOS_GET16(addr, 0x0c);
+ if (size != 0 && size != 0xffff)
+ smbios.enabled_memory += (size & 0x8000) != 0 ?
+ (size & 0x7fff) : (size << 10);
+ break;
+
+ default: /* skip other types */
+ break;
+ }
+
+ /* Find structure terminator. */
+ cp = SMBIOS_GETSTR(addr);
+ while (SMBIOS_GET16(cp, 0) != 0)
+ cp++;
+
+ return (cp + 2);
+}
+
+static caddr_t
+smbios_find_struct(int type)
+{
+ caddr_t dmi;
+ size_t i;
+
+ if (smbios.addr == NULL)
+ return (NULL);
+
+ for (dmi = smbios.addr, i = 0;
+ dmi < smbios.addr + smbios.length && i < smbios.count; i++) {
+ if (SMBIOS_GET8(dmi, 0) == type)
+ return dmi;
+ /* Find structure terminator. */
+ dmi = SMBIOS_GETSTR(dmi);
+ while (SMBIOS_GET16(dmi, 0) != 0)
+ dmi++;
+ dmi += 2;
+ }
+
+ return (NULL);
+}
+
+static void
+smbios_probe(const caddr_t addr)
+{
+ caddr_t saddr, info;
+ uintptr_t paddr;
+
+ if (smbios.probed)
+ return;
+ smbios.probed = 1;
+
+ /* Search signatures and validate checksums. */
+ saddr = smbios_sigsearch(addr ? addr : PTOV(SMBIOS_START),
+ SMBIOS_LENGTH);
+ if (saddr == NULL)
+ return;
+
+ smbios.length = SMBIOS_GET16(saddr, 0x16); /* Structure Table Length */
+ paddr = SMBIOS_GET32(saddr, 0x18); /* Structure Table Address */
+ smbios.count = SMBIOS_GET16(saddr, 0x1c); /* No of SMBIOS Structures */
+ smbios.ver = SMBIOS_GET8(saddr, 0x1e); /* SMBIOS BCD Revision */
+
+ if (smbios.ver != 0) {
+ smbios.major = smbios.ver >> 4;
+ smbios.minor = smbios.ver & 0x0f;
+ if (smbios.major > 9 || smbios.minor > 9)
+ smbios.ver = 0;
+ }
+ if (smbios.ver == 0) {
+ smbios.major = SMBIOS_GET8(saddr, 0x06);/* SMBIOS Major Version */
+ smbios.minor = SMBIOS_GET8(saddr, 0x07);/* SMBIOS Minor Version */
+ }
+ smbios.ver = (smbios.major << 8) | smbios.minor;
+ smbios.addr = PTOV(paddr);
+
+ /* Get system information from SMBIOS */
+ info = smbios_find_struct(0x00);
+ if (info != NULL) {
+ smbios.bios_vendor = smbios_getstring(info, 0x04);
+ }
+ info = smbios_find_struct(0x01);
+ if (info != NULL) {
+ smbios.maker = smbios_getstring(info, 0x04);
+ smbios.product = smbios_getstring(info, 0x05);
+ }
+}
+
+void
+smbios_detect(const caddr_t addr)
+{
+ char buf[16];
+ caddr_t dmi;
+ size_t i;
+
+ smbios_probe(addr);
+ if (smbios.addr == NULL)
+ return;
+
+ for (dmi = smbios.addr, i = 0;
+ dmi < smbios.addr + smbios.length && i < smbios.count; i++)
+ dmi = smbios_parse_table(dmi);
+
+ sprintf(buf, "%d.%d", smbios.major, smbios.minor);
+ setenv("smbios.version", buf, 1);
+ if (smbios.enabled_memory > 0 || smbios.old_enabled_memory > 0) {
+ sprintf(buf, "%u", smbios.enabled_memory > 0 ?
+ smbios.enabled_memory : smbios.old_enabled_memory);
+ setenv("smbios.memory.enabled", buf, 1);
+ }
+ if (smbios.enabled_sockets > 0) {
+ sprintf(buf, "%u", smbios.enabled_sockets);
+ setenv("smbios.socket.enabled", buf, 1);
+ }
+ if (smbios.populated_sockets > 0) {
+ sprintf(buf, "%u", smbios.populated_sockets);
+ setenv("smbios.socket.populated", buf, 1);
+ }
+}
+
+static int
+smbios_match_str(const char* s1, const char* s2)
+{
+ return (s1 == NULL || (s2 != NULL && !strcmp(s1, s2)));
+}
+
+int
+smbios_match(const char* bios_vendor, const char* maker,
+ const char* product)
+{
+ /* XXXRP currently, only called from non-EFI. */
+ smbios_probe(NULL);
+ return (smbios_match_str(bios_vendor, smbios.bios_vendor) &&
+ smbios_match_str(maker, smbios.maker) &&
+ smbios_match_str(product, smbios.product));
+}
diff --git a/stand/i386/libi386/smbios.h b/stand/i386/libi386/smbios.h
new file mode 100644
index 0000000..03fc07e
--- /dev/null
+++ b/stand/i386/libi386/smbios.h
@@ -0,0 +1,34 @@
+/*-
+ * Copyright (c) 2015 Rui Paulo <rpaulo@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 ``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.
+ *
+ * $FreeBSD$
+ */
+#ifndef _SMBIOS_H_
+#define _SMBIOS_H_
+
+void smbios_detect(const caddr_t);
+int smbios_match(const char *, const char *, const char *);
+
+#endif /* _SMBIOS_H_ */
diff --git a/stand/i386/libi386/spinconsole.c b/stand/i386/libi386/spinconsole.c
new file mode 100644
index 0000000..1daac35
--- /dev/null
+++ b/stand/i386/libi386/spinconsole.c
@@ -0,0 +1,112 @@
+/*-
+ * spinconsole.c
+ *
+ * Author: Maksym Sobolyev <sobomax@sippysoft.com>
+ * Copyright (c) 2009 Sippy Software, 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 spinc_probe(struct console *cp);
+static int spinc_init(int arg);
+static void spinc_putchar(int c);
+static int spinc_getchar(void);
+static int spinc_ischar(void);
+
+extern struct console *consoles[];
+
+struct console spinconsole = {
+ "spinconsole",
+ "spin port",
+ 0,
+ spinc_probe,
+ spinc_init,
+ spinc_putchar,
+ spinc_getchar,
+ spinc_ischar
+};
+
+static struct console *parent = NULL;
+
+static void
+spinc_probe(struct console *cp)
+{
+
+ if (parent == NULL)
+ parent = consoles[0];
+ parent->c_probe(cp);
+}
+
+static int
+spinc_init(int arg)
+{
+
+ return(parent->c_init(arg));
+}
+
+static void
+spinc_putchar(int c)
+{
+ static unsigned tw_chars = 0x5C2D2F7C; /* "\-/|" */
+ static time_t lasttime = 0;
+ time_t now;
+
+ now = time(0);
+ if (now < (lasttime + 1))
+ return;
+#ifdef TERM_EMU
+ if (lasttime > 0)
+ parent->c_out('\b');
+#endif
+ lasttime = now;
+ parent->c_out((char)tw_chars);
+ tw_chars = (tw_chars >> 8) | ((tw_chars & (unsigned long)0xFF) << 24);
+}
+
+static int
+spinc_getchar(void)
+{
+
+ return(-1);
+}
+
+static int
+spinc_ischar(void)
+{
+
+ return(0);
+}
diff --git a/stand/i386/libi386/time.c b/stand/i386/libi386/time.c
new file mode 100644
index 0000000..7636ace
--- /dev/null
+++ b/stand/i386/libi386/time.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$");
+
+#include <stand.h>
+#include <btxv86.h>
+#include "bootstrap.h"
+#include "libi386.h"
+
+time_t getsecs(void);
+static int bios_seconds(void);
+
+/*
+ * Return the BIOS time-of-day value.
+ *
+ * XXX uses undocumented BCD support from libstand.
+ */
+static int
+bios_seconds(void)
+{
+ 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 */
+
+ return (hr * 3600 + minute * 60 + sec);
+}
+
+/*
+ * Return the time in seconds since the beginning of the day.
+ *
+ * Some BIOSes (notably qemu) don't correctly read the RTC
+ * registers in an atomic way, sometimes returning bogus values.
+ * Therefore we "debounce" the reading by accepting it only when
+ * we got 8 identical values in succession.
+ *
+ * If we pass midnight, don't wrap back to 0.
+ */
+time_t
+time(time_t *t)
+{
+ static time_t lasttime;
+ time_t now, check;
+ int same, try;
+
+ same = try = 0;
+ check = bios_seconds();
+ do {
+ now = check;
+ check = bios_seconds();
+ if (check != now)
+ same = 0;
+ } while (++same < 8 && ++try < 1000);
+
+ if (now < lasttime)
+ now += 24 * 3600;
+ lasttime = now;
+
+ if (t != NULL)
+ *t = now;
+ return(now);
+}
+
+time_t
+getsecs(void)
+{
+ time_t n = 0;
+ time(&n);
+ return n;
+}
+
+/*
+ * 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/stand/i386/libi386/vidconsole.c b/stand/i386/libi386/vidconsole.c
new file mode 100644
index 0000000..073d531
--- /dev/null
+++ b/stand/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(int *x, int *y);
+void curs_move(int *_x, int *_y, 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(&curx, &cury);
+ curs_move(&curx, &cury, 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? */
+}
+
+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, curx, cury);
+ return;
+ case '\n':
+ cury++;
+ if (cury > 24) {
+ scroll_up(1, fg_c, bg_c);
+ cury--;
+ } else {
+ curs_move(&curx, &cury, curx, cury);
+ }
+ return;
+ case '\b':
+ if (curx > 0) {
+ curx--;
+ curs_move(&curx, &cury, 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, curx, cury);
+#endif
+ }
+}
+
+#ifdef TERM_EMU
+
+/* Get cursor position on the screen. Result is in edx. Sets
+ * curx and cury appropriately.
+ */
+void
+get_pos(int *x, int *y)
+{
+
+ v86.ctl = 0;
+ v86.addr = 0x10;
+ v86.eax = 0x0300;
+ v86.ebx = 0x0;
+ v86int();
+ *x = v86.edx & 0x00ff;
+ *y = (v86.edx & 0xff00) >> 8;
+}
+
+/* Move cursor to x rows and y cols (0-based). */
+void
+curs_move(int *_x, int *_y, int x, int y)
+{
+
+ v86.ctl = 0;
+ v86.addr = 0x10;
+ v86.eax = 0x0200;
+ v86.ebx = 0x0;
+ v86.edx = ((0x00ff & y) << 8) + (0x00ff & x);
+ v86int();
+ *_x = x;
+ *_y = 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(&curx, &cury);
+ 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(&curx, &cury, 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(int 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_ZR(v86.efl));
+}
+
+#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 (1);
+ }
+ }
+
+ return (0);
+}
+#endif /* KEYBOARD_PROBE */
diff --git a/stand/i386/loader/Makefile b/stand/i386/loader/Makefile
new file mode 100644
index 0000000..40ac46c
--- /dev/null
+++ b/stand/i386/loader/Makefile
@@ -0,0 +1,89 @@
+# $FreeBSD$
+
+HAVE_GELI= yes
+
+LOADER_NET_SUPPORT?= yes
+LOADER_NFS_SUPPORT?= yes
+LOADER_TFTP_SUPPORT?= yes
+LOADER_CD9660_SUPPORT?= no
+LOADER_EXT2FS_SUPPORT?= no
+LOADER_MSDOS_SUPPORT?= no
+LOADER_UFS_SUPPORT?= yes
+LOADER_GZIP_SUPPORT?= yes
+LOADER_BZIP2_SUPPORT?= yes
+
+.include <bsd.init.mk>
+
+MK_SSP= no
+
+LOADER?= loader
+PROG= ${LOADER}.sym
+MAN=
+INTERNALPROG=
+NEWVERSWHAT?= "bootstrap loader" x86
+VERSION_FILE= ${.CURDIR}/../loader/version
+
+.PATH: ${BOOTSRC}/i386/loader
+
+# architecture-specific loader code
+SRCS= main.c conf.c vers.c chain.c
+
+# Include bcache code.
+HAVE_BCACHE= yes
+
+# Enable PnP and ISA-PnP code.
+HAVE_PNP= yes
+HAVE_ISABUS= yes
+
+.if ${MK_LOADER_FIREWIRE} == "yes"
+CFLAGS+= -DLOADER_FIREWIRE_SUPPORT
+LIBFIREWIRE= ${BOOTOBJ}/i386/libfirewire/libfirewire.a
+.endif
+
+.if exists(${.CURDIR}/help.i386)
+HELP_FILES+= help.i386
+.else
+HELP_FILES=
+.endif
+
+# Always add MI sources
+.include "${BOOTSRC}/loader.mk"
+
+CLEANFILES+= ${LOADER} ${LOADER}.bin
+
+CFLAGS+= -Wall
+LDFLAGS+= -static -Ttext 0x0
+
+# i386 standalone support library
+LIBI386= ${BOOTOBJ}/i386/libi386/libi386.a
+CFLAGS+= -I${BOOTSRC}/i386
+
+# BTX components
+CFLAGS+= -I${BTXLIB}
+
+# Debug me!
+#CFLAGS+= -g
+#LDFLAGS+= -g
+
+${LOADER}: ${LOADER}.bin ${BTXLDR} ${BTXKERN}
+ btxld -v -f aout -e ${LOADER_ADDRESS} -o ${.TARGET} -l ${BTXLDR} \
+ -b ${BTXKERN} ${LOADER}.bin
+
+${LOADER}.bin: ${LOADER}.sym
+ strip -R .comment -R .note -o ${.TARGET} ${.ALLSRC}
+
+FILES+= ${LOADER}
+# XXX INSTALLFLAGS_loader= -b
+FILESMODE_${LOADER}= ${BINMODE} -b
+
+# XXX crt0.o needs to be first for pxeboot(8) to work
+OBJS= ${BTXCRT}
+
+DPADD= ${LIBFICL32} ${LIBFIREWIRE} ${LIBZFSBOOT} ${LIBI386} ${LIBGELIBOOT} ${LIBSA32}
+LDADD= ${LIBFICL32} ${LIBFIREWIRE} ${LIBZFSBOOT} ${LIBI386} ${LIBGELIBOOT} ${LIBSA32}
+
+.if ${MACHINE_CPUARCH} == "amd64"
+CFLAGS+= -DLOADER_PREFER_AMD64
+.endif
+
+.include <bsd.prog.mk>
diff --git a/stand/i386/loader/Makefile.depend b/stand/i386/loader/Makefile.depend
new file mode 100644
index 0000000..89d5422
--- /dev/null
+++ b/stand/i386/loader/Makefile.depend
@@ -0,0 +1,21 @@
+# $FreeBSD$
+# Autogenerated - do NOT edit!
+
+DIRDEPS = \
+ include \
+ include/xlocale \
+ lib/libstand \
+ sys/boot/ficl32 \
+ sys/boot/geli \
+ sys/boot/i386/btx/btx \
+ sys/boot/i386/btx/btxldr \
+ sys/boot/i386/btx/lib \
+ sys/boot/i386/libi386 \
+ sys/boot/libstand32 \
+
+
+.include <dirdeps.mk>
+
+.if ${DEP_RELDIR} == ${_DEP_RELDIR}
+# local dependencies - needed for -jN in clean tree
+.endif
diff --git a/stand/i386/loader/chain.c b/stand/i386/loader/chain.c
new file mode 100644
index 0000000..d6810ef
--- /dev/null
+++ b/stand/i386/loader/chain.c
@@ -0,0 +1,134 @@
+/*-
+ * Copyright 2015 Toomas Soome <tsoome@me.com>
+ * 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.
+ */
+
+/*
+ * Chain loader to load BIOS boot block either from MBR or PBR.
+ *
+ * Note the boot block location 0000:7c000 conflicts with loader, so we need to
+ * read in to temporary space and relocate on exec, when btx is stopped.
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <stand.h>
+#include <sys/param.h>
+#include <sys/linker.h>
+#include <sys/diskmbr.h>
+
+#include "bootstrap.h"
+#include "libi386/libi386.h"
+#include "btxv86.h"
+
+/*
+ * The MBR/VBR is located in first sector of disk/partition.
+ * Read 512B to temporary location and set up relocation. Then
+ * exec relocator.
+ */
+#define SECTOR_SIZE (512)
+
+COMMAND_SET(chain, "chain", "chain load boot block from device", command_chain);
+
+static int
+command_chain(int argc, char *argv[])
+{
+ int fd, len, size = SECTOR_SIZE;
+ struct stat st;
+ vm_offset_t mem = 0x100000;
+ struct i386_devdesc *rootdev;
+
+ if (argc == 1) {
+ command_errmsg = "no device or file name specified";
+ return (CMD_ERROR);
+ }
+ if (argc != 2) {
+ command_errmsg = "invalid trailing arguments";
+ return (CMD_ERROR);
+ }
+
+ fd = open(argv[1], O_RDONLY);
+ if (fd == -1) {
+ command_errmsg = "open failed";
+ return (CMD_ERROR);
+ }
+
+ len = strlen(argv[1]);
+ if (argv[1][len-1] != ':') {
+ if (fstat(fd, &st) == -1) {
+ command_errmsg = "stat failed";
+ close(fd);
+ return (CMD_ERROR);
+ }
+ size = st.st_size;
+ } else if (strncmp(argv[1], "disk", 4) != 0) {
+ command_errmsg = "can only use disk device";
+ close(fd);
+ return (CMD_ERROR);
+ }
+
+ i386_getdev((void **)(&rootdev), argv[1], NULL);
+ if (rootdev == NULL) {
+ command_errmsg = "can't determine root device";
+ return (CMD_ERROR);
+ }
+
+ if (archsw.arch_readin(fd, mem, SECTOR_SIZE) != SECTOR_SIZE) {
+ command_errmsg = "failed to read disk";
+ close(fd);
+ return (CMD_ERROR);
+ }
+ close(fd);
+
+ if (*((uint16_t *)PTOV(mem + DOSMAGICOFFSET)) != DOSMAGIC) {
+ command_errmsg = "wrong magic";
+ return (CMD_ERROR);
+ }
+
+ relocater_data[0].src = mem;
+ relocater_data[0].dest = 0x7C00;
+ relocater_data[0].size = SECTOR_SIZE;
+
+ relocator_edx = bd_unit2bios(rootdev->d_unit);
+ relocator_esi = relocater_size;
+ relocator_ds = 0;
+ relocator_es = 0;
+ relocator_fs = 0;
+ relocator_gs = 0;
+ relocator_ss = 0;
+ relocator_cs = 0;
+ relocator_sp = 0x7C00;
+ relocator_ip = 0x7C00;
+ relocator_a20_enabled = 0;
+
+ i386_copyin(relocater, 0x600, relocater_size);
+
+ dev_cleanup();
+
+ __exec((void *)0x600);
+
+ panic("exec returned");
+ return (CMD_ERROR); /* not reached */
+}
diff --git a/stand/i386/loader/conf.c b/stand/i386/loader/conf.c
new file mode 100644
index 0000000..29ce1e3
--- /dev/null
+++ b/stand/i386/loader/conf.c
@@ -0,0 +1,159 @@
+/*-
+ * 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"
+#if defined(LOADER_ZFS_SUPPORT)
+#include "../zfs/libzfs.h"
+#endif
+
+/*
+ * 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_FIREWIRE_SUPPORT)
+extern struct devsw fwohci;
+#endif
+
+/* Exported for libstand */
+struct devsw *devsw[] = {
+ &bioscd,
+ &biosdisk,
+#if defined(LOADER_NFS_SUPPORT) || defined(LOADER_TFTP_SUPPORT)
+ &pxedisk,
+#endif
+#if defined(LOADER_FIREWIRE_SUPPORT)
+ &fwohci,
+#endif
+#if defined(LOADER_ZFS_SUPPORT)
+ &zfs_dev,
+#endif
+ NULL
+};
+
+struct fs_ops *file_system[] = {
+#if defined(LOADER_ZFS_SUPPORT)
+ &zfs_fsops,
+#endif
+ &ufs_fsops,
+ &ext2fs_fsops,
+ &dosfs_fsops,
+ &cd9660_fsops,
+#if defined(LOADER_NANDFS_SUPPORT)
+ &nandfs_fsops,
+#endif
+#ifdef LOADER_NFS_SUPPORT
+ &nfs_fsops,
+#endif
+#ifdef LOADER_TFTP_SUPPORT
+ &tftp_fsops,
+#endif
+#ifdef LOADER_GZIP_SUPPORT
+ &gzipfs_fsops,
+#endif
+#ifdef LOADER_BZIP2_SUPPORT
+ &bzipfs_fsops,
+#endif
+#ifdef LOADER_SPLIT_SUPPORT
+ &splitfs_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 i386_elf_obj;
+extern struct file_format amd64_elf;
+extern struct file_format amd64_elf_obj;
+extern struct file_format multiboot;
+extern struct file_format multiboot_obj;
+
+struct file_format *file_formats[] = {
+ &multiboot,
+ &multiboot_obj,
+#ifdef LOADER_PREFER_AMD64
+ &amd64_elf,
+ &amd64_elf_obj,
+#endif
+ &i386_elf,
+ &i386_elf_obj,
+#ifndef LOADER_PREFER_AMD64
+ &amd64_elf,
+ &amd64_elf_obj,
+#endif
+ 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;
+#if defined(LOADER_FIREWIRE_SUPPORT)
+extern struct console dconsole;
+#endif
+extern struct console nullconsole;
+extern struct console spinconsole;
+
+struct console *consoles[] = {
+ &vidconsole,
+ &comconsole,
+#if defined(LOADER_FIREWIRE_SUPPORT)
+ &dconsole,
+#endif
+ &nullconsole,
+ &spinconsole,
+ 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/stand/i386/loader/help.i386 b/stand/i386/loader/help.i386
new file mode 100644
index 0000000..0ff6286
--- /dev/null
+++ b/stand/i386/loader/help.i386
@@ -0,0 +1,54 @@
+################################################################################
+# 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 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.
+
+################################################################################
+# Tsmap DDisplay BIOS SMAP table
+
+ smap
+
+ Displays the BIOS SMAP (system memory map) table.
+
+################################################################################
+# Tchain DChain load disk block
+
+ chain disk:
+
+ chain will read stage1 (MBR or VBR) boot block from specified device
+ to address 0000:7C00 and attempts to run it. Use lsdev to get available
+ device names. Disk name must end with colon.
+
+################################################################################
diff --git a/stand/i386/loader/loader.rc b/stand/i386/loader/loader.rc
new file mode 100644
index 0000000..287c05e
--- /dev/null
+++ b/stand/i386/loader/loader.rc
@@ -0,0 +1,18 @@
+\ Loader.rc
+\ $FreeBSD$
+\
+\ Includes additional commands
+include /boot/loader.4th
+try-include /boot/loader.rc.local
+
+\ Reads and processes loader.conf variables
+initialize
+
+\ 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/stand/i386/loader/main.c b/stand/i386/loader/main.c
new file mode 100644
index 0000000..81bc2ff
--- /dev/null
+++ b/stand/i386/loader/main.c
@@ -0,0 +1,477 @@
+/*-
+ * 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 <stddef.h>
+#include <string.h>
+#include <machine/bootinfo.h>
+#include <machine/cpufunc.h>
+#include <machine/psl.h>
+#include <sys/disk.h>
+#include <sys/reboot.h>
+#include <common/drv.h>
+
+#include "bootstrap.h"
+#include "common/bootargs.h"
+#include "libi386/libi386.h"
+#include "libi386/smbios.h"
+#include "btxv86.h"
+
+#ifdef LOADER_ZFS_SUPPORT
+#include "../zfs/libzfs.h"
+#endif
+
+CTASSERT(sizeof(struct bootargs) == BOOTARGS_SIZE);
+CTASSERT(offsetof(struct bootargs, bootinfo) == BA_BOOTINFO);
+CTASSERT(offsetof(struct bootargs, bootflags) == BA_BOOTFLAGS);
+CTASSERT(offsetof(struct bootinfo, bi_size) == BI_SIZE);
+
+/* Arguments passed in from the boot1/boot2 loader */
+static struct bootargs *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);
+#ifdef LOADER_GELI_SUPPORT
+#include "geliboot.h"
+struct geli_boot_args *gargs;
+#endif
+#ifdef LOADER_ZFS_SUPPORT
+struct zfs_boot_args *zargs;
+static void i386_zfs_probe(void);
+#endif
+
+/* from vers.c */
+extern char bootprog_info[];
+
+/* XXX debugging */
+extern char end[];
+
+static void *heap_top;
+static void *heap_bottom;
+
+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;
+
+ /* Initialize the v86 register set to a known-good state. */
+ bzero(&v86, sizeof(v86));
+ v86.efl = PSL_RESERVED_DEFAULT | PSL_I;
+
+ /*
+ * Initialise the heap as early as possible. Once this is done, malloc() is usable.
+ */
+ bios_getmem();
+
+#if defined(LOADER_BZIP2_SUPPORT) || defined(LOADER_FIREWIRE_SUPPORT) || \
+ defined(LOADER_GPT_SUPPORT) || defined(LOADER_ZFS_SUPPORT)
+ if (high_heap_size > 0) {
+ heap_top = PTOV(high_heap_base + high_heap_size);
+ heap_bottom = PTOV(high_heap_base);
+ if (high_heap_base < memtop_copyin)
+ memtop_copyin = high_heap_base;
+ } else
+#endif
+ {
+ heap_top = (void *)PTOV(bios_basemem);
+ heap_bottom = (void *)end;
+ }
+ setheap(heap_bottom, heap_top);
+
+ /*
+ * 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.
+ */
+ bi_setboothowto(initial_howto);
+ if (initial_howto & RB_MULTIPLE) {
+ if (initial_howto & RB_SERIAL)
+ setenv("console", "comconsole vidconsole", 1);
+ else
+ setenv("console", "vidconsole comconsole", 1);
+ } else if (initial_howto & RB_SERIAL)
+ setenv("console", "comconsole", 1);
+ else if (initial_howto & RB_MUTE)
+ setenv("console", "nullconsole", 1);
+ cons_probe();
+
+ /*
+ * Initialise the block cache. Set the upper limit.
+ */
+ bcache_init(32768, 512);
+
+ /*
+ * Special handling for PXE and CD booting.
+ */
+ if (kargs->bootinfo == 0) {
+ /*
+ * 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);
+ }
+
+ 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;
+#ifdef LOADER_ZFS_SUPPORT
+ archsw.arch_zfs_probe = i386_zfs_probe;
+
+#ifdef LOADER_GELI_SUPPORT
+ if ((kargs->bootflags & KARGS_FLAGS_EXTARG) != 0) {
+ zargs = (struct zfs_boot_args *)(kargs + 1);
+ if (zargs != NULL && zargs->size >= offsetof(struct zfs_boot_args, gelipw)) {
+ if (zargs->size >= offsetof(struct zfs_boot_args, keybuf_sentinel) &&
+ zargs->keybuf_sentinel == KEYBUF_SENTINEL) {
+ geli_save_keybuf(zargs->keybuf);
+ }
+ if (zargs->gelipw[0] != '\0') {
+ setenv("kern.geom.eli.passphrase", zargs->gelipw, 1);
+ explicit_bzero(zargs->gelipw, sizeof(zargs->gelipw));
+ }
+ }
+ }
+#endif /* LOADER_GELI_SUPPORT */
+#else /* !LOADER_ZFS_SUPPORT */
+#ifdef LOADER_GELI_SUPPORT
+ if ((kargs->bootflags & KARGS_FLAGS_EXTARG) != 0) {
+ gargs = (struct geli_boot_args *)(kargs + 1);
+ if (gargs != NULL && gargs->size >= offsetof(struct geli_boot_args, gelipw)) {
+ if (gargs->keybuf_sentinel == KEYBUF_SENTINEL) {
+ geli_save_keybuf(gargs->keybuf);
+ }
+ if (gargs->gelipw[0] != '\0') {
+ setenv("kern.geom.eli.passphrase", gargs->gelipw, 1);
+ explicit_bzero(gargs->gelipw, sizeof(gargs->gelipw));
+ }
+ }
+ }
+#endif /* LOADER_GELI_SUPPORT */
+#endif /* LOADER_ZFS_SUPPORT */
+
+ /*
+ * 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();
+
+ /* detect SMBIOS for future reference */
+ smbios_detect(NULL);
+
+ /* detect PCI BIOS for future reference */
+ biospci_detect();
+
+ printf("\n%s", bootprog_info);
+
+ extract_currdev(); /* set $currdev and $loaddev */
+ setenv("LINES", "24", 1); /* optional */
+
+ bios_getsmap();
+
+ interact(NULL);
+
+ /* 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;
+#ifdef LOADER_ZFS_SUPPORT
+ char buf[20];
+#endif
+ int 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 == 0) {
+ if ((kargs->bootflags & KARGS_FLAGS_CD) != 0) {
+ /* we are booting from a CD with cdboot */
+ new_currdev.d_dev = &bioscd;
+ new_currdev.d_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_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;
+ }
+#ifdef LOADER_ZFS_SUPPORT
+ } else if ((kargs->bootflags & KARGS_FLAGS_ZFS) != 0) {
+ zargs = NULL;
+ /* check for new style extended argument */
+ if ((kargs->bootflags & KARGS_FLAGS_EXTARG) != 0)
+ zargs = (struct zfs_boot_args *)(kargs + 1);
+
+ if (zargs != NULL &&
+ zargs->size >= offsetof(struct zfs_boot_args, primary_pool)) {
+ /* sufficient data is provided */
+ new_currdev.d_kind.zfs.pool_guid = zargs->pool;
+ new_currdev.d_kind.zfs.root_guid = zargs->root;
+ if (zargs->size >= sizeof(*zargs) && zargs->primary_vdev != 0) {
+ sprintf(buf, "%llu", zargs->primary_pool);
+ setenv("vfs.zfs.boot.primary_pool", buf, 1);
+ sprintf(buf, "%llu", zargs->primary_vdev);
+ setenv("vfs.zfs.boot.primary_vdev", buf, 1);
+ }
+ } else {
+ /* old style zfsboot block */
+ new_currdev.d_kind.zfs.pool_guid = kargs->zfspool;
+ new_currdev.d_kind.zfs.root_guid = 0;
+ }
+ new_currdev.d_dev = &zfs_dev;
+#endif
+ } 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_SLICE(initial_bootdev) - 1;
+ new_currdev.d_kind.biosdisk.partition = B_PARTITION(initial_bootdev);
+ biosdev = initial_bootinfo->bi_bios_dev;
+
+ /*
+ * If we are booted by an old bootstrap, we have to guess at the BIOS
+ * unit number. We will lose 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_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_unit = 0;
+ }
+
+#ifdef LOADER_ZFS_SUPPORT
+ if (new_currdev.d_type == DEVT_ZFS)
+ init_zfs_bootenv(zfs_fmtdev(&new_currdev));
+#endif
+
+ 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, upper limit at %p\n", heap_bottom,
+ sbrk(0), heap_top);
+ return(CMD_OK);
+}
+
+#ifdef LOADER_ZFS_SUPPORT
+COMMAND_SET(lszfs, "lszfs", "list child datasets of a zfs dataset",
+ command_lszfs);
+
+static int
+command_lszfs(int argc, char *argv[])
+{
+ int err;
+
+ if (argc != 2) {
+ command_errmsg = "wrong number of arguments";
+ return (CMD_ERROR);
+ }
+
+ err = zfs_list(argv[1]);
+ if (err != 0) {
+ command_errmsg = strerror(err);
+ return (CMD_ERROR);
+ }
+
+ return (CMD_OK);
+}
+
+COMMAND_SET(reloadbe, "reloadbe", "refresh the list of ZFS Boot Environments",
+ command_reloadbe);
+
+static int
+command_reloadbe(int argc, char *argv[])
+{
+ int err;
+ char *root;
+
+ if (argc > 2) {
+ command_errmsg = "wrong number of arguments";
+ return (CMD_ERROR);
+ }
+
+ if (argc == 2) {
+ err = zfs_bootenv(argv[1]);
+ } else {
+ root = getenv("zfs_be_root");
+ if (root == NULL) {
+ /* There does not appear to be a ZFS pool here, exit without error */
+ return (CMD_OK);
+ }
+ err = zfs_bootenv(root);
+ }
+
+ if (err != 0) {
+ command_errmsg = strerror(err);
+ return (CMD_ERROR);
+ }
+
+ return (CMD_OK);
+}
+#endif
+
+/* ISA bus access functions for PnP. */
+static int
+isa_inb(int port)
+{
+
+ return (inb(port));
+}
+
+static void
+isa_outb(int port, int value)
+{
+
+ outb(port, value);
+}
+
+#ifdef LOADER_ZFS_SUPPORT
+static void
+i386_zfs_probe(void)
+{
+ char devname[32];
+ int unit;
+
+ /*
+ * Open all the disks we can find and see if we can reconstruct
+ * ZFS pools from them.
+ */
+ for (unit = 0; unit < MAXBDDEV; unit++) {
+ if (bd_unit2bios(unit) == -1)
+ break;
+ sprintf(devname, "disk%d:", unit);
+ zfs_probe_dev(devname, NULL);
+ }
+}
+
+uint64_t
+ldi_get_size(void *priv)
+{
+ int fd = (uintptr_t) priv;
+ uint64_t size;
+
+ ioctl(fd, DIOCGMEDIASIZE, &size);
+ return (size);
+}
+#endif
diff --git a/stand/i386/loader/version b/stand/i386/loader/version
new file mode 100644
index 0000000..7a2acaf
--- /dev/null
+++ b/stand/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/stand/i386/mbr/Makefile b/stand/i386/mbr/Makefile
new file mode 100644
index 0000000..9bfa0bd
--- /dev/null
+++ b/stand/i386/mbr/Makefile
@@ -0,0 +1,17 @@
+# $FreeBSD$
+
+PROG= mbr
+STRIP=
+BINMODE=${NOBINMODE}
+MAN=
+SRCS= ${PROG}.s
+
+# MBR flags: 0x80 -- try packet interface (also known as EDD or LBA)
+BOOT_MBR_FLAGS?= 0x80
+
+ORG= 0x600
+
+AFLAGS+=--defsym FLAGS=${BOOT_MBR_FLAGS}
+LDFLAGS+=${LDFLAGS_BIN}
+
+.include <bsd.prog.mk>
diff --git a/stand/i386/mbr/Makefile.depend b/stand/i386/mbr/Makefile.depend
new file mode 100644
index 0000000..f80275d
--- /dev/null
+++ b/stand/i386/mbr/Makefile.depend
@@ -0,0 +1,11 @@
+# $FreeBSD$
+# Autogenerated - do NOT edit!
+
+DIRDEPS = \
+
+
+.include <dirdeps.mk>
+
+.if ${DEP_RELDIR} == ${_DEP_RELDIR}
+# local dependencies - needed for -jN in clean tree
+.endif
diff --git a/stand/i386/mbr/mbr.s b/stand/i386/mbr/mbr.s
new file mode 100644
index 0000000..3cfc20d
--- /dev/null
+++ b/stand/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 FL_PACKET,0x80 # Flag: try EDD
+
+ .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
+ testb $FL_PACKET,flags # Try EDD?
+ jz 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-1,0x90
+flags: .byte FLAGS # Flags
+
+partbl: .fill 0x10,0x4,0x0 # Partition table
+ .word MAGIC # Magic number
diff --git a/stand/i386/pmbr/Makefile b/stand/i386/pmbr/Makefile
new file mode 100644
index 0000000..1bfe0ee
--- /dev/null
+++ b/stand/i386/pmbr/Makefile
@@ -0,0 +1,14 @@
+# $FreeBSD$
+
+PROG= pmbr
+STRIP=
+BINMODE=${NOBINMODE}
+MAN=
+SRCS= ${PROG}.s
+
+ORG= 0x600
+
+AFLAGS+=--defsym FLAGS=${BOOT_MBR_FLAGS}
+LDFLAGS+=${LDFLAGS_BIN}
+
+.include <bsd.prog.mk>
diff --git a/stand/i386/pmbr/Makefile.depend b/stand/i386/pmbr/Makefile.depend
new file mode 100644
index 0000000..f80275d
--- /dev/null
+++ b/stand/i386/pmbr/Makefile.depend
@@ -0,0 +1,11 @@
+# $FreeBSD$
+# Autogenerated - do NOT edit!
+
+DIRDEPS = \
+
+
+.include <dirdeps.mk>
+
+.if ${DEP_RELDIR} == ${_DEP_RELDIR}
+# local dependencies - needed for -jN in clean tree
+.endif
diff --git a/stand/i386/pmbr/pmbr.s b/stand/i386/pmbr/pmbr.s
new file mode 100644
index 0000000..1a75881
--- /dev/null
+++ b/stand/i386/pmbr/pmbr.s
@@ -0,0 +1,252 @@
+#-
+# Copyright (c) 2007 Yahoo!, Inc.
+# All rights reserved.
+# Written by: John Baldwin <jhb@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.
+# 3. Neither the name of the author nor the names of any co-contributors
+# may be used to endorse or promote products derived from this software
+# without specific prior written permission.
+#
+# THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+# ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+# SUCH DAMAGE.
+#
+# $FreeBSD$
+#
+# Partly from: src/sys/boot/i386/mbr/mbr.s 1.7
+
+# A 512 byte PMBR boot manager that looks for a FreeBSD boot GPT partition
+# and boots it.
+
+ .set LOAD,0x7c00 # Load address
+ .set EXEC,0x600 # Execution address
+ .set MAGIC,0xaa55 # Magic: bootable
+ .set SECSIZE,0x200 # Size of a single disk sector
+ .set DISKSIG,440 # Disk signature offset
+ .set STACK,EXEC+SECSIZE*4 # Stack address
+ .set GPT_ADDR,STACK # GPT header address
+ .set GPT_SIG,0
+ .set GPT_SIG_0,0x20494645 # "EFI "
+ .set GPT_SIG_1,0x54524150 # "PART"
+ .set GPT_MYLBA,24
+ .set GPT_PART_LBA,72
+ .set GPT_NPART,80
+ .set GPT_PART_SIZE,84
+ .set PART_ADDR,GPT_ADDR+SECSIZE # GPT partition array address
+ .set PART_TYPE,0
+ .set PART_START_LBA,32
+ .set PART_END_LBA,40
+ .set DPBUF,PART_ADDR+SECSIZE
+ .set DPBUF_SEC,0x10 # Number of sectors
+
+ .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 $STACK,%sp # stack
+#
+# Relocate ourself to a lower address so that we have more room to load
+# other sectors.
+#
+ movw $main-EXEC+LOAD,%si # Source
+ movw $main,%di # Destination
+ movw $SECSIZE-(main-start),%cx # Byte count
+ rep # Relocate
+ movsb # code
+#
+# Jump to the relocated code.
+#
+ jmp main-LOAD+EXEC # To relocated code
+#
+# Validate drive number in %dl.
+#
+main: cmpb $0x80,%dl # Drive valid?
+ jb main.1 # No
+ movb NHRDRV,%dh # Calculate the highest
+ addb $0x80,%dh # drive number available
+ cmpb %dh,%dl # Within range?
+ jb main.2 # Yes
+main.1: movb $0x80,%dl # Assume drive 0x80
+#
+# Load the GPT header and verify signature. Try LBA 1 for the primary one and
+# the last LBA for the backup if it is broken.
+#
+main.2: call getdrvparams # Read drive parameters
+ movb $1,%dh # %dh := 1 (reading primary)
+main.2a: movw $GPT_ADDR,%bx
+ movw $lba,%si
+ call read # Read header and check GPT sig
+ cmpl $GPT_SIG_0,GPT_ADDR+GPT_SIG
+ jnz main.2b
+ cmpl $GPT_SIG_1,GPT_ADDR+GPT_SIG+4
+ jnz main.2b
+ jmp load_part
+main.2b: cmpb $1,%dh # Reading primary?
+ jne err_pt # If no - invalid table found
+#
+# Try alternative LBAs from the last sector for the GPT header.
+#
+main.3: movb $0,%dh # %dh := 0 (reading backup)
+ movw $DPBUF+DPBUF_SEC,%si # %si = last sector + 1
+ movw $lba,%di # %di = $lba
+main.3a: decl (%si) # 0x0(%si) = last sec (0-31)
+ movw $2,%cx
+ rep
+ movsw # $lastsec--, copy it to $lba
+ jmp main.2a # Read the next sector
+#
+# Load a partition table sector from disk and look for a FreeBSD boot
+# partition.
+#
+load_part: movw $GPT_ADDR+GPT_PART_LBA,%si
+ movw $PART_ADDR,%bx
+ call read
+scan: movw %bx,%si # Compare partition UUID
+ movw $boot_uuid,%di # with FreeBSD boot UUID
+ movb $0x10,%cl
+ repe cmpsb
+ jnz next_part # Didn't match, next partition
+#
+# We found a boot partition. Load it into RAM starting at 0x7c00.
+#
+ movw %bx,%di # Save partition pointer in %di
+ leaw PART_START_LBA(%di),%si
+ movw $LOAD/16,%bx
+ movw %bx,%es
+ xorw %bx,%bx
+load_boot: push %si # Save %si
+ call read
+ pop %si # Restore
+ movl PART_END_LBA(%di),%eax # See if this was the last LBA
+ cmpl (%si),%eax
+ jnz next_boot
+ movl PART_END_LBA+4(%di),%eax
+ cmpl 4(%si),%eax
+ jnz next_boot
+ mov %bx,%es # Reset %es to zero
+ jmp LOAD # Jump to boot code
+next_boot: incl (%si) # Next LBA
+ adcl $0,4(%si)
+ mov %es,%ax # Adjust segment for next
+ addw $SECSIZE/16,%ax # sector
+ cmp $0x9000,%ax # Don't load past 0x90000,
+ jae err_big # 545k should be enough for
+ mov %ax,%es # any boot code. :)
+ jmp load_boot
+#
+# Move to the next partition. If we walk off the end of the sector, load
+# the next sector. We assume that partition entries are smaller than 64k
+# and that they won't span a sector boundary.
+#
+# XXX: Should we int 0x18 instead of err_noboot if we hit the end of the table?
+#
+next_part: decl GPT_ADDR+GPT_NPART # Was this the last partition?
+ jz err_noboot
+ movw GPT_ADDR+GPT_PART_SIZE,%ax
+ addw %ax,%bx # Next partition
+ cmpw $PART_ADDR+0x200,%bx # Still in sector?
+ jb scan
+ incl GPT_ADDR+GPT_PART_LBA # Next sector
+ adcl $0,GPT_ADDR+GPT_PART_LBA+4
+ jmp load_part
+#
+# Load a sector (64-bit LBA at %si) from disk %dl into %es:%bx by creating
+# a EDD packet on the stack and passing it to the BIOS. Trashes %ax and %si.
+#
+read: pushl 0x4(%si) # Set the LBA
+ pushl 0x0(%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
+ int $0x13 # Call the BIOS
+ add $0x10,%sp # Restore stack
+ jc err_rd # If error
+ ret
+#
+# Check the number of LBAs on the drive index %dx. Trashes %ax and %si.
+#
+getdrvparams:
+ movw $DPBUF,%si # Set the address of result buf
+ movw $0x001e,(%si) # len
+ movw $0x4800,%ax # BIOS: Read Drive Parameters
+ int $0x13 # Call the BIOS
+ jc err_rd # "I/O error" if error
+ ret
+#
+# Various error message entry points.
+#
+err_big: movw $msg_big,%si # "Boot loader too
+ jmp putstr # large"
+
+err_pt: movw $msg_pt,%si # "Invalid partition
+ jmp putstr # table"
+
+err_rd: movw $msg_rd,%si # "I/O error loading
+ jmp putstr # boot loader"
+
+err_noboot: movw $msg_noboot,%si # "Missing boot
+ jmp putstr # loader"
+#
+# 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_big: .asciz "Boot loader too large"
+msg_pt: .asciz "Invalid partition table"
+msg_rd: .asciz "I/O error loading boot loader"
+msg_noboot: .asciz "Missing boot loader"
+
+lba: .quad 1 # LBA of GPT header
+
+boot_uuid: .long 0x83bd6b9d
+ .word 0x7f41
+ .word 0x11dc
+ .byte 0xbe
+ .byte 0x0b
+ .byte 0x00
+ .byte 0x15
+ .byte 0x60
+ .byte 0xb8
+ .byte 0x4f
+ .byte 0x0f
+
+ .org DISKSIG,0x90
+sig: .long 0 # OS Disk Signature
+ .word 0 # "Unknown" in PMBR
+
+partbl: .fill 0x10,0x4,0x0 # Partition table
+ .word MAGIC # Magic number
diff --git a/stand/i386/pxeldr/Makefile b/stand/i386/pxeldr/Makefile
new file mode 100644
index 0000000..819283c
--- /dev/null
+++ b/stand/i386/pxeldr/Makefile
@@ -0,0 +1,47 @@
+# $FreeBSD$
+
+.include <bsd.init.mk>
+
+PROG= ${LDR}
+INTERNALPROG=
+FILES= ${BOOT}
+MAN= ${BOOT}.8
+SRCS= ${LDR}.S
+CLEANFILES+= ${BOOT}
+
+BOOT= pxeboot
+LDR= pxeldr
+ORG= 0x7c00
+LOADER= loader
+
+.if defined(BOOT_PXELDR_PROBE_KEYBOARD)
+CFLAGS+=-DPROBE_KEYBOARD
+.endif
+
+.if defined(BOOT_PXELDR_ALWAYS_SERIAL)
+CFLAGS+=-DALWAYS_SERIAL
+.endif
+
+CFLAGS+=-I${BOOTSRC}/i386/common
+
+LOADERBIN= ${BOOTOBJ}/i386/loader/loader.bin
+
+CLEANFILES+= ${BOOT}.tmp
+
+${BOOT}: ${LDR} ${LOADER}
+ cat ${LDR} ${LOADER} > ${.TARGET}.tmp
+ ${DD} if=${.TARGET}.tmp of=${.TARGET} obs=2k conv=osync
+ rm ${.TARGET}.tmp
+
+LDFLAGS+=${LDFLAGS_BIN}
+
+CLEANFILES+= ${LOADER}
+
+${LOADER}: ${LOADERBIN} ${BTXLDR} ${BTXKERN}
+ btxld -v -f aout -e ${LOADER_ADDRESS} -o ${.TARGET} -l ${BTXLDR} \
+ -b ${BTXKERN} ${LOADERBIN}
+
+.include <bsd.prog.mk>
+
+# XXX: clang integrated-as doesn't grok .codeNN directives yet
+CFLAGS.pxeldr.S= ${CLANG_NO_IAS}
diff --git a/stand/i386/pxeldr/Makefile.depend b/stand/i386/pxeldr/Makefile.depend
new file mode 100644
index 0000000..c6f574a
--- /dev/null
+++ b/stand/i386/pxeldr/Makefile.depend
@@ -0,0 +1,15 @@
+# $FreeBSD$
+# Autogenerated - do NOT edit!
+
+DIRDEPS = \
+ include \
+ sys/boot/i386/btx/btx \
+ sys/boot/i386/btx/btxldr \
+ sys/boot/i386/loader \
+
+
+.include <dirdeps.mk>
+
+.if ${DEP_RELDIR} == ${_DEP_RELDIR}
+# local dependencies - needed for -jN in clean tree
+.endif
diff --git a/stand/i386/pxeldr/pxeboot.8 b/stand/i386/pxeldr/pxeboot.8
new file mode 100644
index 0000000..0194e21
--- /dev/null
+++ b/stand/i386/pxeldr/pxeboot.8
@@ -0,0 +1,158 @@
+.\" 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$
+.\"
+.Dd May 27, 2017
+.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.
+.Pp
+The DHCP client will set a DHCP user class named
+.Va FreeBSD
+to allow flexible configuration of the DHCP server.
+.Pp
+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;
+ if exists user-class and option user-class = "FreeBSD" {
+ option root-path "tftp://10.0.0.1/FreeBSD";
+ }
+}
+
+.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
+Valid
+.Va option root-path
+Syntax is the following
+.Bl -tag -width <scheme>://ip/path indent
+.It /path
+path to the root filesystem on the NFS server
+.It ip:/path
+path to the root filesystem on the NFS server
+.Ar ip
+.It nfs:/path
+path to the root filesystem on the NFS server
+.It nfs://ip/path
+path to the root filesystem on the NFS server
+.Ar ip
+.It tftp:/path
+path to the root filesystem on the TFTP server
+.It tftp://ip/path
+path to the root filesystem on the TFTP server
+.Ar ip
+.El
+.Pp
+.Nm
+defaults to a conservative 1024 byte NFS data packet size.
+This may be changed by setting the
+.Va nfs.read_size
+variable in
+.Pa /boot/loader.conf .
+Valid values range from 1024 to 16384 bytes.
+.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://www.intel.com/design/archives/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/stand/i386/pxeldr/pxeldr.S b/stand/i386/pxeldr/pxeldr.S
new file mode 100644
index 0000000..ee1e18a
--- /dev/null
+++ b/stand/i386/pxeldr/pxeldr.S
@@ -0,0 +1,301 @@
+/*
+ * Copyright (c) 2000 John Baldwin
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * 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$
+ */
+
+/*
+ * 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.
+ */
+
+#include <sys/reboot.h>
+#include <bootargs.h>
+
+/*
+ * 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
+/*
+ * 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
+#endif
+#ifdef PROBE_KEYBOARD
+/*
+ * Look at the BIOS data area to see if we have an enhanced keyboard. If not,
+ * set the RBX_DUAL and RBX_SERIAL bits in the howto byte.
+ */
+ testb $KEYBOARD_BIT, MEM_BIOS_KEYBOARD # keyboard present?
+ jnz keyb # yes, so skip
+ orl $(RB_MULTIPLE | RB_SERIAL), (%bx) # enable serial console
+keyb:
+#endif
+/*
+ * 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. Put an upper limit on the amount of time we wait for the
+ * keyboard controller to get ready (65K x ISA access time). If
+ * we wait more than that amount, the hardware is probably
+ * legacy-free and simply doesn't have a keyboard controller.
+ * Thus, the A20 line is already enabled.
+ */
+seta20: cli # Disable interrupts
+ xor %cx,%cx # Clear
+seta20.1: inc %cx # Increment, overflow?
+ jz seta20.3 # Yes
+ 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
+seta20.3: 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/stand/i386/zfsboot/Makefile b/stand/i386/zfsboot/Makefile
new file mode 100644
index 0000000..f0523df
--- /dev/null
+++ b/stand/i386/zfsboot/Makefile
@@ -0,0 +1,93 @@
+# $FreeBSD$
+
+HAVE_GELI=yes
+
+.include <bsd.init.mk>
+
+.PATH: ${BOOTSRC}/i386/boot2 ${BOOTSRC}/i386/common ${SASRC}
+
+FILES= zfsboot
+MAN= zfsboot.8
+
+NM?= nm
+
+BOOT_COMCONSOLE_PORT?= 0x3f8
+BOOT_COMCONSOLE_SPEED?= 9600
+B2SIOFMT?= 0x3
+
+REL1= 0x700
+ORG1= 0x7c00
+ORG2= 0x2000
+
+CFLAGS+=-DBOOTPROG=\"zfsboot\" \
+ -O1 \
+ -DZFS -DBOOT2 \
+ -DSIOPRT=${BOOT_COMCONSOLE_PORT} \
+ -DSIOFMT=${B2SIOFMT} \
+ -DSIOSPD=${BOOT_COMCONSOLE_SPEED} \
+ -I${LDRSRC} \
+ -I${BOOTSRC}/i386/common \
+ -I${BOOTSRC}/i386 \
+ -I${ZFSSRC} \
+ -I${SYSDIR}/cddl/boot/zfs \
+ -I${BTXLIB} \
+ -I${BOOTSRC}/i386/boot2 \
+ -Wall -Waggregate-return -Wbad-function-cast -Wno-cast-align \
+ -Wmissing-declarations -Wmissing-prototypes -Wnested-externs \
+ -Wpointer-arith -Wshadow -Wstrict-prototypes -Wwrite-strings \
+ -Winline
+
+CFLAGS.gcc+= --param max-inline-insns-single=100
+.if ${MACHINE} == "amd64"
+LIBZFSBOOT=${BOOTOBJ}/zfs32/libzfsboot.a
+.else
+LIBZFSBOOT=${BOOTOBJ}/zfs/libzfsboot.a
+.endif
+
+LD_FLAGS+=${LD_FLAGS_BIN}
+
+CLEANFILES+= zfsboot
+
+zfsboot: zfsboot1 zfsboot2
+ cat zfsboot1 zfsboot2 > zfsboot
+
+CLEANFILES+= zfsboot1 zfsldr.out zfsldr.o
+
+zfsboot1: zfsldr.out
+ ${OBJCOPY} -S -O binary zfsldr.out ${.TARGET}
+
+zfsldr.out: zfsldr.o
+ ${LD} ${LD_FLAGS} -e start -Ttext ${ORG1} -o ${.TARGET} zfsldr.o
+
+CLEANFILES+= zfsboot2 zfsboot.ld zfsboot.ldr zfsboot.bin zfsboot.out \
+ zfsboot.o zfsboot.s zfsboot.s.tmp sio.o cons.o drv.o
+
+# We currently allow 128k bytes for zfsboot - in practice it could be
+# any size up to 3.5Mb but keeping it fixed size simplifies zfsldr.
+#
+BOOT2SIZE= 131072
+
+zfsboot2: zfsboot.ld
+ @set -- `ls -l ${.ALLSRC}`; x=$$((${BOOT2SIZE}-$$5)); \
+ echo "$$x bytes available"; test $$x -ge 0
+ ${DD} if=${.ALLSRC} of=${.TARGET} obs=${BOOT2SIZE} conv=osync
+
+zfsboot.ld: zfsboot.ldr zfsboot.bin ${BTXKERN}
+ btxld -v -E ${ORG2} -f bin -b ${BTXKERN} -l zfsboot.ldr \
+ -o ${.TARGET} -P 1 zfsboot.bin
+
+zfsboot.ldr:
+ cp /dev/null ${.TARGET}
+
+zfsboot.bin: zfsboot.out
+ ${OBJCOPY} -S -O binary zfsboot.out ${.TARGET}
+
+zfsboot.out: ${BTXCRT} zfsboot.o sio.o drv.o cons.o
+ ${LD} ${LD_FLAGS} -Ttext ${ORG2} -o ${.TARGET} ${.ALLSRC} ${LIBZFSBOOT} ${LIBGELIBOOT} ${LIBSA32}
+
+SRCS= zfsboot.c
+
+.include <bsd.prog.mk>
+
+# XXX: clang integrated-as doesn't grok .codeNN directives yet
+CFLAGS.zfsldr.S= ${CLANG_NO_IAS}
diff --git a/stand/i386/zfsboot/Makefile.depend b/stand/i386/zfsboot/Makefile.depend
new file mode 100644
index 0000000..63a43d8
--- /dev/null
+++ b/stand/i386/zfsboot/Makefile.depend
@@ -0,0 +1,16 @@
+# $FreeBSD$
+# Autogenerated - do NOT edit!
+
+DIRDEPS = \
+ include \
+ include/xlocale \
+ sys/boot/i386/btx/btx \
+ sys/boot/i386/btx/lib \
+ sys/boot/libstand32 \
+
+
+.include <dirdeps.mk>
+
+.if ${DEP_RELDIR} == ${_DEP_RELDIR}
+# local dependencies - needed for -jN in clean tree
+.endif
diff --git a/stand/i386/zfsboot/zfsboot.8 b/stand/i386/zfsboot/zfsboot.8
new file mode 100644
index 0000000..6450939
--- /dev/null
+++ b/stand/i386/zfsboot/zfsboot.8
@@ -0,0 +1,133 @@
+.\" Copyright (c) 2014 Andriy Gapon <avg@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 AUTHORS 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 AUTHORS 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$
+.\"
+.Dd September 15, 2014
+.Dt ZFSBOOT 8
+.Os
+.Sh NAME
+.Nm zfsboot
+.Nd bootcode for ZFS on BIOS-based computers
+.Sh DESCRIPTION
+.Nm
+is used on BIOS-based computers to boot from a filesystem in
+a ZFS pool.
+.Nm
+is installed in two parts on a disk or a partition used by a ZFS pool.
+The first part, a single-sector starter boot block, is installed
+at the beginning of the disk or partition.
+The second part, a main boot block, is installed at a special offset
+within the disk or partition.
+Both areas are reserved by the ZFS on-disk specification for boot use.
+If
+.Nm
+is installed in a partition, then that partition should be made
+bootable using appropriate configuration and boot blocks described in
+.Xr boot 8 .
+.Sh BOOTING
+The
+.Nm
+boot process is very similar to that of
+.Xr gptzfsboot 8 .
+One significant difference is that
+.Nm
+does not currently support the GPT partitioning scheme.
+Thus only whole disks and MBR partitions, traditionally referred to as
+slices, are probed for ZFS disk labels.
+See the BUGS section in
+.Xr gptzfsboot 8
+for some limitations of the MBR scheme support.
+.Sh USAGE
+.Nm
+supports all the same prompt and configuration file arguments as
+.Xr gptzfsboot 8 .
+.Sh FILES
+.Bl -tag -width /boot/zfsboot -compact
+.It Pa /boot/zfsboot
+boot code binary
+.It Pa /boot.config
+parameters for the boot block
+.Pq optional
+.It Pa /boot/config
+alternative parameters for the boot block
+.Pq optional
+.El
+.Sh EXAMPLES
+.Nm
+is typically installed using
+.Xr dd 1 .
+To install
+.Nm
+on the
+.Pa ada0
+drive:
+.Bd -literal -offset indent
+dd if=/boot/zfsboot of=/dev/ada0 count=1
+dd if=/boot/zfsboot of=/dev/ada0 iseek=1 oseek=1024
+.Ed
+.Pp
+If the drive is currently in use, the GEOM safety will prevent writes
+and must be disabled before running the above commands:
+.Bd -literal -offset indent
+sysctl kern.geom.debugflags=0x10
+.Ed
+.Pp
+.Nm
+can also be installed in an MBR slice:
+.Bd -literal -offset indent
+gpart create -s mbr ada0
+gpart add -t freebsd ada0
+gpart create -s BSD ada0s1
+gpart bootcode -b /boot/boot0 ada0
+gpart set -a active -i 1 ada0
+dd if=/boot/zfsboot of=/dev/ada0s1 count=1
+dd if=/boot/zfsboot of=/dev/ada0s1 iseek=1 oseek=1024
+.Ed
+.Pp
+Note that commands to create and populate a pool are not shown
+in the example above.
+.Sh SEE ALSO
+.Xr dd 1 ,
+.Xr boot.config 5 ,
+.Xr boot 8 ,
+.Xr gptzfsboot 8 ,
+.Xr loader 8 ,
+.Xr zfsloader 8 ,
+.Xr zpool 8
+.Sh HISTORY
+.Nm
+appeared in FreeBSD 7.3.
+.Sh AUTHORS
+This manual page was written by
+.An Andriy Gapon Aq avg@FreeBSD.org .
+.Sh BUGS
+Installing
+.Nm
+with
+.Xr dd 1
+is a hack.
+ZFS needs a command to properly install
+.Nm
+onto a ZFS-controlled disk or partition.
diff --git a/stand/i386/zfsboot/zfsboot.c b/stand/i386/zfsboot/zfsboot.c
new file mode 100644
index 0000000..88b5231
--- /dev/null
+++ b/stand/i386/zfsboot/zfsboot.c
@@ -0,0 +1,1151 @@
+/*-
+ * 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/errno.h>
+#include <sys/diskmbr.h>
+#ifdef GPT
+#include <sys/gpt.h>
+#endif
+#include <sys/reboot.h>
+#include <sys/queue.h>
+
+#include <machine/bootinfo.h>
+#include <machine/elf.h>
+#include <machine/pc/bios.h>
+
+#include <stdarg.h>
+#include <stddef.h>
+
+#include <a.out.h>
+
+#include <btxv86.h>
+
+#include "lib.h"
+#include "rbx.h"
+#include "drv.h"
+#include "edd.h"
+#include "util.h"
+#include "cons.h"
+#include "bootargs.h"
+#include "paths.h"
+
+#include "libzfs.h"
+
+#define ARGS 0x900
+#define NOPT 14
+#define NDEV 3
+
+#define BIOS_NUMDRIVES 0x475
+#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
+
+#define DEV_GELIBOOT_BSIZE 4096
+
+extern uint32_t _end;
+
+#ifdef GPT
+static const uuid_t freebsd_zfs_uuid = GPT_ENT_TYPE_FREEBSD_ZFS;
+#endif
+static const char optstr[NOPT] = "DhaCcdgmnpqrsv"; /* Also 'P', 'S' */
+static const unsigned char flags[NOPT] = {
+ RBX_DUAL,
+ RBX_SERIAL,
+ RBX_ASKNAME,
+ RBX_CDROM,
+ RBX_CONFIG,
+ RBX_KDB,
+ RBX_GDB,
+ RBX_MUTE,
+ RBX_NOINTR,
+ RBX_PAUSE,
+ RBX_QUIET,
+ RBX_DFLTROOT,
+ RBX_SINGLE,
+ RBX_VERBOSE
+};
+uint32_t opts;
+
+static const unsigned char dev_maj[NDEV] = {30, 4, 2};
+
+static char cmd[512];
+static char cmddup[512];
+static char kname[1024];
+static char rootname[256];
+static int comspeed = SIOSPD;
+static struct bootinfo bootinfo;
+static uint32_t bootdev;
+static struct zfs_boot_args zfsargs;
+static struct zfsmount zfsmount;
+
+vm_offset_t high_heap_base;
+uint32_t bios_basemem, bios_extmem, high_heap_size;
+
+static struct bios_smap smap;
+
+/*
+ * The minimum amount of memory to reserve in bios_extmem for the heap.
+ */
+#define HEAP_MIN (64 * 1024 * 1024)
+
+static char *heap_next;
+static char *heap_end;
+
+/* Buffers that must not span a 64k boundary. */
+#define READ_BUF_SIZE 8192
+struct dmadat {
+ char rdbuf[READ_BUF_SIZE]; /* for reading large things */
+ char secbuf[READ_BUF_SIZE]; /* for MBR/disklabel */
+};
+static struct dmadat *dmadat;
+
+void exit(int);
+void reboot(void);
+static void load(void);
+static int parse_cmd(void);
+static void bios_getmem(void);
+void *malloc(size_t n);
+void free(void *ptr);
+int main(void);
+
+void *
+malloc(size_t n)
+{
+ char *p = heap_next;
+ if (p + n > heap_end) {
+ printf("malloc failure\n");
+ for (;;)
+ ;
+ /* NOTREACHED */
+ return (0);
+ }
+ heap_next += n;
+ return (p);
+}
+
+void
+free(void *ptr)
+{
+
+ return;
+}
+
+static char *
+strdup(const char *s)
+{
+ char *p = malloc(strlen(s) + 1);
+ strcpy(p, s);
+ return (p);
+}
+
+#ifdef LOADER_GELI_SUPPORT
+#include "geliboot.c"
+static char gelipw[GELI_PW_MAXLEN];
+static struct keybuf *gelibuf;
+#endif
+
+#include "zfsimpl.c"
+
+/*
+ * Read from a dnode (which must be from a ZPL filesystem).
+ */
+static int
+zfs_read(spa_t *spa, const dnode_phys_t *dnode, off_t *offp, void *start, size_t size)
+{
+ const znode_phys_t *zp = (const znode_phys_t *) dnode->dn_bonus;
+ size_t n;
+ int rc;
+
+ n = size;
+ if (*offp + n > zp->zp_size)
+ n = zp->zp_size - *offp;
+
+ rc = dnode_read(spa, dnode, *offp, start, n);
+ if (rc)
+ return (-1);
+ *offp += n;
+
+ return (n);
+}
+
+/*
+ * Current ZFS pool
+ */
+static spa_t *spa;
+static spa_t *primary_spa;
+static vdev_t *primary_vdev;
+
+/*
+ * A wrapper for dskread that doesn't have to worry about whether the
+ * buffer pointer crosses a 64k boundary.
+ */
+static int
+vdev_read(vdev_t *vdev, void *priv, off_t off, void *buf, size_t bytes)
+{
+ char *p;
+ daddr_t lba, alignlba;
+ off_t diff;
+ unsigned int nb, alignnb;
+ struct dsk *dsk = (struct dsk *) priv;
+
+ if ((off & (DEV_BSIZE - 1)) || (bytes & (DEV_BSIZE - 1)))
+ return -1;
+
+ p = buf;
+ lba = off / DEV_BSIZE;
+ lba += dsk->start;
+ /*
+ * Align reads to 4k else 4k sector GELIs will not decrypt.
+ * Round LBA down to nearest multiple of DEV_GELIBOOT_BSIZE bytes.
+ */
+ alignlba = rounddown2(off, DEV_GELIBOOT_BSIZE) / DEV_BSIZE;
+ /*
+ * The read must be aligned to DEV_GELIBOOT_BSIZE bytes relative to the
+ * start of the GELI partition, not the start of the actual disk.
+ */
+ alignlba += dsk->start;
+ diff = (lba - alignlba) * DEV_BSIZE;
+
+ while (bytes > 0) {
+ nb = bytes / DEV_BSIZE;
+ /*
+ * Ensure that the read size plus the leading offset does not
+ * exceed the size of the read buffer.
+ */
+ if (nb > (READ_BUF_SIZE - diff) / DEV_BSIZE)
+ nb = (READ_BUF_SIZE - diff) / DEV_BSIZE;
+ /*
+ * Round the number of blocks to read up to the nearest multiple
+ * of DEV_GELIBOOT_BSIZE.
+ */
+ alignnb = roundup2(nb * DEV_BSIZE + diff, DEV_GELIBOOT_BSIZE)
+ / DEV_BSIZE;
+
+ if (drvread(dsk, dmadat->rdbuf, alignlba, alignnb))
+ return -1;
+#ifdef LOADER_GELI_SUPPORT
+ /* decrypt */
+ if (is_geli(dsk) == 0) {
+ if (geli_read(dsk, ((alignlba - dsk->start) *
+ DEV_BSIZE), dmadat->rdbuf, alignnb * DEV_BSIZE))
+ return (-1);
+ }
+#endif
+ memcpy(p, dmadat->rdbuf + diff, nb * DEV_BSIZE);
+ p += nb * DEV_BSIZE;
+ lba += nb;
+ alignlba += alignnb;
+ bytes -= nb * DEV_BSIZE;
+ /* Don't need the leading offset after the first block. */
+ diff = 0;
+ }
+
+ return 0;
+}
+
+static int
+vdev_write(vdev_t *vdev, void *priv, off_t off, void *buf, size_t bytes)
+{
+ char *p;
+ daddr_t lba;
+ unsigned int nb;
+ struct dsk *dsk = (struct dsk *) priv;
+
+ if ((off & (DEV_BSIZE - 1)) || (bytes & (DEV_BSIZE - 1)))
+ return -1;
+
+ p = buf;
+ lba = off / DEV_BSIZE;
+ lba += dsk->start;
+ while (bytes > 0) {
+ nb = bytes / DEV_BSIZE;
+ if (nb > READ_BUF_SIZE / DEV_BSIZE)
+ nb = READ_BUF_SIZE / DEV_BSIZE;
+ memcpy(dmadat->rdbuf, p, nb * DEV_BSIZE);
+ if (drvwrite(dsk, dmadat->rdbuf, lba, nb))
+ return -1;
+ p += nb * DEV_BSIZE;
+ lba += nb;
+ bytes -= nb * DEV_BSIZE;
+ }
+
+ return 0;
+}
+
+static int
+xfsread(const dnode_phys_t *dnode, off_t *offp, void *buf, size_t nbyte)
+{
+ if ((size_t)zfs_read(spa, dnode, offp, buf, nbyte) != nbyte) {
+ printf("Invalid format\n");
+ return -1;
+ }
+ return 0;
+}
+
+/*
+ * Read Pad2 (formerly "Boot Block Header") area of the first
+ * vdev label of the given vdev.
+ */
+static int
+vdev_read_pad2(vdev_t *vdev, char *buf, size_t size)
+{
+ blkptr_t bp;
+ char *tmp = zap_scratch;
+ off_t off = offsetof(vdev_label_t, vl_pad2);
+
+ if (size > VDEV_PAD_SIZE)
+ size = VDEV_PAD_SIZE;
+
+ BP_ZERO(&bp);
+ BP_SET_LSIZE(&bp, VDEV_PAD_SIZE);
+ BP_SET_PSIZE(&bp, VDEV_PAD_SIZE);
+ BP_SET_CHECKSUM(&bp, ZIO_CHECKSUM_LABEL);
+ BP_SET_COMPRESS(&bp, ZIO_COMPRESS_OFF);
+ DVA_SET_OFFSET(BP_IDENTITY(&bp), off);
+ if (vdev_read_phys(vdev, &bp, tmp, off, 0))
+ return (EIO);
+ memcpy(buf, tmp, size);
+ return (0);
+}
+
+static int
+vdev_clear_pad2(vdev_t *vdev)
+{
+ char *zeroes = zap_scratch;
+ uint64_t *end;
+ off_t off = offsetof(vdev_label_t, vl_pad2);
+
+ memset(zeroes, 0, VDEV_PAD_SIZE);
+ end = (uint64_t *)(zeroes + VDEV_PAD_SIZE);
+ /* ZIO_CHECKSUM_LABEL magic and pre-calcualted checksum for all zeros */
+ end[-5] = 0x0210da7ab10c7a11;
+ end[-4] = 0x97f48f807f6e2a3f;
+ end[-3] = 0xaf909f1658aacefc;
+ end[-2] = 0xcbd1ea57ff6db48b;
+ end[-1] = 0x6ec692db0d465fab;
+ if (vdev_write(vdev, vdev->v_read_priv, off, zeroes, VDEV_PAD_SIZE))
+ return (EIO);
+ return (0);
+}
+
+static void
+bios_getmem(void)
+{
+ uint64_t size;
+
+ /* 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 bios_smap);
+ v86.edx = SMAP_SIG;
+ v86.es = VTOPSEG(&smap);
+ v86.edi = VTOPOFF(&smap);
+ v86int();
+ if (V86_CY(v86.efl) || (v86.eax != SMAP_SIG))
+ break;
+ /* look for a low-memory segment that's large enough */
+ if ((smap.type == SMAP_TYPE_MEMORY) && (smap.base == 0) &&
+ (smap.length >= (512 * 1024)))
+ bios_basemem = smap.length;
+ /* look for the first segment in 'extended' memory */
+ if ((smap.type == SMAP_TYPE_MEMORY) && (smap.base == 0x100000)) {
+ bios_extmem = smap.length;
+ }
+
+ /*
+ * Look for the largest segment in 'extended' memory beyond
+ * 1MB but below 4GB.
+ */
+ if ((smap.type == SMAP_TYPE_MEMORY) && (smap.base > 0x100000) &&
+ (smap.base < 0x100000000ull)) {
+ size = smap.length;
+
+ /*
+ * If this segment crosses the 4GB boundary, truncate it.
+ */
+ if (smap.base + size > 0x100000000ull)
+ size = 0x100000000ull - smap.base;
+
+ if (size > high_heap_size) {
+ high_heap_size = size;
+ high_heap_base = smap.base;
+ }
+ }
+ } 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_CY(v86.efl)) {
+ 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;
+ }
+
+ /*
+ * If we have extended memory and did not find a suitable heap
+ * region in the SMAP, use the last 3MB of 'extended' memory as a
+ * high heap candidate.
+ */
+ if (bios_extmem >= HEAP_MIN && high_heap_size < HEAP_MIN) {
+ high_heap_size = HEAP_MIN;
+ high_heap_base = bios_extmem + 0x100000 - HEAP_MIN;
+ }
+}
+
+/*
+ * Try to detect a device supported by the legacy int13 BIOS
+ */
+static int
+int13probe(int drive)
+{
+ v86.ctl = V86_FLAGS;
+ v86.addr = 0x13;
+ v86.eax = 0x800;
+ v86.edx = drive;
+ v86int();
+
+ if (!V86_CY(v86.efl) && /* carry clear */
+ ((v86.edx & 0xff) != (drive & DRV_MASK))) { /* unit # OK */
+ if ((v86.ecx & 0x3f) == 0) { /* absurd sector size */
+ return(0); /* skip device */
+ }
+ return (1);
+ }
+ return(0);
+}
+
+/*
+ * We call this when we find a ZFS vdev - ZFS consumes the dsk
+ * structure so we must make a new one.
+ */
+static struct dsk *
+copy_dsk(struct dsk *dsk)
+{
+ struct dsk *newdsk;
+
+ newdsk = malloc(sizeof(struct dsk));
+ *newdsk = *dsk;
+ return (newdsk);
+}
+
+/*
+ * Get disk size from eax=0x800 and 0x4800. We need to probe both
+ * because 0x4800 may not be available and we would like to get more
+ * or less correct disk size - if it is possible at all.
+ * Note we do not really want to touch drv.c because that code is shared
+ * with boot2 and we can not afford to grow that code.
+ */
+static uint64_t
+drvsize_ext(struct dsk *dskp)
+{
+ uint64_t size, tmp;
+ int cyl, hds, sec;
+
+ v86.ctl = V86_FLAGS;
+ v86.addr = 0x13;
+ v86.eax = 0x800;
+ v86.edx = dskp->drive;
+ v86int();
+
+ /* Don't error out if we get bad sector number, try EDD as well */
+ if (V86_CY(v86.efl) || /* carry set */
+ (v86.edx & 0xff) <= (unsigned)(dskp->drive & 0x7f)) /* unit # bad */
+ return (0);
+
+ cyl = ((v86.ecx & 0xc0) << 2) + ((v86.ecx & 0xff00) >> 8) + 1;
+ /* Convert max head # -> # of heads */
+ hds = ((v86.edx & 0xff00) >> 8) + 1;
+ sec = v86.ecx & 0x3f;
+
+ size = (uint64_t)cyl * hds * sec;
+
+ /* Determine if we can use EDD with this device. */
+ v86.ctl = V86_FLAGS;
+ v86.addr = 0x13;
+ v86.eax = 0x4100;
+ v86.edx = dskp->drive;
+ v86.ebx = 0x55aa;
+ v86int();
+ if (V86_CY(v86.efl) || /* carry set */
+ (v86.ebx & 0xffff) != 0xaa55 || /* signature */
+ (v86.ecx & EDD_INTERFACE_FIXED_DISK) == 0)
+ return (size);
+
+ tmp = drvsize(dskp);
+ if (tmp > size)
+ size = tmp;
+
+ return (size);
+}
+
+/*
+ * The "layered" ioctl to read disk/partition size. Unfortunately
+ * the zfsboot case is hardest, because we do not have full software
+ * stack available, so we need to do some manual work here.
+ */
+uint64_t
+ldi_get_size(void *priv)
+{
+ struct dsk *dskp = priv;
+ uint64_t size = dskp->size;
+
+ if (dskp->start == 0)
+ size = drvsize_ext(dskp);
+
+ return (size * DEV_BSIZE);
+}
+
+static void
+probe_drive(struct dsk *dsk)
+{
+#ifdef GPT
+ struct gpt_hdr hdr;
+ struct gpt_ent *ent;
+ unsigned part, entries_per_sec;
+ daddr_t slba;
+#endif
+#if defined(GPT) || defined(LOADER_GELI_SUPPORT)
+ daddr_t elba;
+#endif
+
+ struct dos_partition *dp;
+ char *sec;
+ unsigned i;
+
+ /*
+ * If we find a vdev on the whole disk, stop here.
+ */
+ if (vdev_probe(vdev_read, dsk, NULL) == 0)
+ return;
+
+#ifdef LOADER_GELI_SUPPORT
+ /*
+ * Taste the disk, if it is GELI encrypted, decrypt it and check to see if
+ * it is a usable vdev then. Otherwise dig
+ * out the partition table and probe each slice/partition
+ * in turn for a vdev or GELI encrypted vdev.
+ */
+ elba = drvsize_ext(dsk);
+ if (elba > 0) {
+ elba--;
+ }
+ if (geli_taste(vdev_read, dsk, elba) == 0) {
+ if (geli_havekey(dsk) == 0 || geli_passphrase(&gelipw, dsk->unit,
+ ':', 0, dsk) == 0) {
+ if (vdev_probe(vdev_read, dsk, NULL) == 0) {
+ return;
+ }
+ }
+ }
+#endif /* LOADER_GELI_SUPPORT */
+
+ sec = dmadat->secbuf;
+ dsk->start = 0;
+
+#ifdef GPT
+ /*
+ * First check for GPT.
+ */
+ if (drvread(dsk, sec, 1, 1)) {
+ return;
+ }
+ memcpy(&hdr, sec, sizeof(hdr));
+ if (memcmp(hdr.hdr_sig, GPT_HDR_SIG, sizeof(hdr.hdr_sig)) != 0 ||
+ hdr.hdr_lba_self != 1 || hdr.hdr_revision < 0x00010000 ||
+ hdr.hdr_entsz < sizeof(*ent) || DEV_BSIZE % hdr.hdr_entsz != 0) {
+ goto trymbr;
+ }
+
+ /*
+ * Probe all GPT partitions for the presence of ZFS pools. We
+ * return the spa_t for the first we find (if requested). This
+ * will have the effect of booting from the first pool on the
+ * disk.
+ *
+ * If no vdev is found, GELI decrypting the device and try again
+ */
+ entries_per_sec = DEV_BSIZE / hdr.hdr_entsz;
+ slba = hdr.hdr_lba_table;
+ elba = slba + hdr.hdr_entries / entries_per_sec;
+ while (slba < elba) {
+ dsk->start = 0;
+ if (drvread(dsk, sec, slba, 1))
+ return;
+ for (part = 0; part < entries_per_sec; part++) {
+ ent = (struct gpt_ent *)(sec + part * hdr.hdr_entsz);
+ if (memcmp(&ent->ent_type, &freebsd_zfs_uuid,
+ sizeof(uuid_t)) == 0) {
+ dsk->start = ent->ent_lba_start;
+ dsk->size = ent->ent_lba_end - ent->ent_lba_start + 1;
+ dsk->slice = part + 1;
+ dsk->part = 255;
+ if (vdev_probe(vdev_read, dsk, NULL) == 0) {
+ /*
+ * This slice had a vdev. We need a new dsk
+ * structure now since the vdev now owns this one.
+ */
+ dsk = copy_dsk(dsk);
+ }
+#ifdef LOADER_GELI_SUPPORT
+ else if (geli_taste(vdev_read, dsk, ent->ent_lba_end -
+ ent->ent_lba_start) == 0) {
+ if (geli_havekey(dsk) == 0 || geli_passphrase(&gelipw,
+ dsk->unit, 'p', dsk->slice, dsk) == 0) {
+ /*
+ * This slice has GELI, check it for ZFS.
+ */
+ if (vdev_probe(vdev_read, dsk, NULL) == 0) {
+ /*
+ * This slice had a vdev. We need a new dsk
+ * structure now since the vdev now owns this one.
+ */
+ dsk = copy_dsk(dsk);
+ }
+ break;
+ }
+ }
+#endif /* LOADER_GELI_SUPPORT */
+ }
+ }
+ slba++;
+ }
+ return;
+trymbr:
+#endif /* GPT */
+
+ if (drvread(dsk, sec, DOSBBSECTOR, 1))
+ return;
+ dp = (void *)(sec + DOSPARTOFF);
+
+ for (i = 0; i < NDOSPART; i++) {
+ if (!dp[i].dp_typ)
+ continue;
+ dsk->start = dp[i].dp_start;
+ dsk->size = dp[i].dp_size;
+ dsk->slice = i + 1;
+ if (vdev_probe(vdev_read, dsk, NULL) == 0) {
+ dsk = copy_dsk(dsk);
+ }
+#ifdef LOADER_GELI_SUPPORT
+ else if (geli_taste(vdev_read, dsk, dp[i].dp_size -
+ dp[i].dp_start) == 0) {
+ if (geli_havekey(dsk) == 0 || geli_passphrase(&gelipw, dsk->unit,
+ 's', i, dsk) == 0) {
+ /*
+ * This slice has GELI, check it for ZFS.
+ */
+ if (vdev_probe(vdev_read, dsk, NULL) == 0) {
+ /*
+ * This slice had a vdev. We need a new dsk
+ * structure now since the vdev now owns this one.
+ */
+ dsk = copy_dsk(dsk);
+ }
+ break;
+ }
+ }
+#endif /* LOADER_GELI_SUPPORT */
+ }
+}
+
+int
+main(void)
+{
+ dnode_phys_t dn;
+ off_t off;
+ struct dsk *dsk;
+ int autoboot, i;
+ int nextboot;
+ int rc;
+
+ dmadat = (void *)(roundup2(__base + (int32_t)&_end, 0x10000) - __base);
+
+ bios_getmem();
+
+ if (high_heap_size > 0) {
+ heap_end = PTOV(high_heap_base + high_heap_size);
+ heap_next = PTOV(high_heap_base);
+ } else {
+ heap_next = (char *)dmadat + sizeof(*dmadat);
+ heap_end = (char *)PTOV(bios_basemem);
+ }
+
+ dsk = malloc(sizeof(struct dsk));
+ 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;
+ dsk->part = 0;
+ dsk->start = 0;
+ dsk->size = 0;
+
+ bootinfo.bi_version = BOOTINFO_VERSION;
+ bootinfo.bi_size = sizeof(bootinfo);
+ bootinfo.bi_basemem = bios_basemem / 1024;
+ bootinfo.bi_extmem = bios_extmem / 1024;
+ bootinfo.bi_memsizes_valid++;
+ bootinfo.bi_bios_dev = dsk->drive;
+
+ bootdev = MAKEBOOTDEV(dev_maj[dsk->type],
+ dsk->slice, dsk->unit, dsk->part);
+
+ /* Process configuration file */
+
+ autoboot = 1;
+
+#ifdef LOADER_GELI_SUPPORT
+ geli_init();
+#endif
+ zfs_init();
+
+ /*
+ * Probe the boot drive first - we will try to boot from whatever
+ * pool we find on that drive.
+ */
+ probe_drive(dsk);
+
+ /*
+ * Probe the rest of the drives that the bios knows about. This
+ * will find any other available pools and it may fill in missing
+ * vdevs for the boot pool.
+ */
+#ifndef VIRTUALBOX
+ for (i = 0; i < *(unsigned char *)PTOV(BIOS_NUMDRIVES); i++)
+#else
+ for (i = 0; i < MAXBDDEV; i++)
+#endif
+ {
+ if ((i | DRV_HARD) == *(uint8_t *)PTOV(ARGS))
+ continue;
+
+ if (!int13probe(i | DRV_HARD))
+ break;
+
+ dsk = malloc(sizeof(struct dsk));
+ dsk->drive = i | DRV_HARD;
+ dsk->type = dsk->drive & TYPE_AD;
+ dsk->unit = i;
+ dsk->slice = 0;
+ dsk->part = 0;
+ dsk->start = 0;
+ dsk->size = 0;
+ probe_drive(dsk);
+ }
+
+ /*
+ * The first discovered pool, if any, is the pool.
+ */
+ spa = spa_get_primary();
+ if (!spa) {
+ printf("%s: No ZFS pools located, can't boot\n", BOOTPROG);
+ for (;;)
+ ;
+ }
+
+ primary_spa = spa;
+ primary_vdev = spa_get_primary_vdev(spa);
+
+ nextboot = 0;
+ rc = vdev_read_pad2(primary_vdev, cmd, sizeof(cmd));
+ if (vdev_clear_pad2(primary_vdev))
+ printf("failed to clear pad2 area of primary vdev\n");
+ if (rc == 0) {
+ if (*cmd) {
+ /*
+ * We could find an old-style ZFS Boot Block header here.
+ * Simply ignore it.
+ */
+ if (*(uint64_t *)cmd != 0x2f5b007b10c) {
+ /*
+ * Note that parse() is destructive to cmd[] and we also want
+ * to honor RBX_QUIET option that could be present in cmd[].
+ */
+ nextboot = 1;
+ memcpy(cmddup, cmd, sizeof(cmd));
+ if (parse_cmd()) {
+ printf("failed to parse pad2 area of primary vdev\n");
+ reboot();
+ }
+ if (!OPT_CHECK(RBX_QUIET))
+ printf("zfs nextboot: %s\n", cmddup);
+ }
+ /* Do not process this command twice */
+ *cmd = 0;
+ }
+ } else
+ printf("failed to read pad2 area of primary vdev\n");
+
+ /* Mount ZFS only if it's not already mounted via nextboot parsing. */
+ if (zfsmount.spa == NULL &&
+ (zfs_spa_init(spa) != 0 || zfs_mount(spa, 0, &zfsmount) != 0)) {
+ printf("%s: failed to mount default pool %s\n",
+ BOOTPROG, spa->spa_name);
+ autoboot = 0;
+ } else if (zfs_lookup(&zfsmount, PATH_CONFIG, &dn) == 0 ||
+ zfs_lookup(&zfsmount, PATH_DOTCONFIG, &dn) == 0) {
+ off = 0;
+ zfs_read(spa, &dn, &off, cmd, sizeof(cmd));
+ }
+
+ if (*cmd) {
+ /*
+ * Note that parse_cmd() is destructive to cmd[] and we also want
+ * to honor RBX_QUIET option that could be present in cmd[].
+ */
+ memcpy(cmddup, cmd, sizeof(cmd));
+ if (parse_cmd())
+ autoboot = 0;
+ if (!OPT_CHECK(RBX_QUIET))
+ printf("%s: %s\n", PATH_CONFIG, cmddup);
+ /* Do not process this command twice */
+ *cmd = 0;
+ }
+
+ /* Do not risk waiting at the prompt forever. */
+ if (nextboot && !autoboot)
+ reboot();
+
+ /*
+ * Try to exec /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_LOADER_ZFS, sizeof(PATH_LOADER_ZFS));
+ if (!keyhit(3)) {
+ load();
+ memcpy(kname, PATH_KERNEL, sizeof(PATH_KERNEL));
+ }
+ }
+
+ /* Present the user with the boot2 prompt. */
+
+ for (;;) {
+ if (!autoboot || !OPT_CHECK(RBX_QUIET)) {
+ printf("\nFreeBSD/x86 boot\n");
+ if (zfs_rlookup(spa, zfsmount.rootobj, rootname) != 0)
+ printf("Default: %s/<0x%llx>:%s\n"
+ "boot: ",
+ spa->spa_name, zfsmount.rootobj, kname);
+ else if (rootname[0] != '\0')
+ printf("Default: %s/%s:%s\n"
+ "boot: ",
+ spa->spa_name, rootname, kname);
+ else
+ printf("Default: %s:%s\n"
+ "boot: ",
+ spa->spa_name, kname);
+ }
+ if (ioctrl & IO_SERIAL)
+ sio_flush();
+ if (!autoboot || keyhit(5))
+ getstr(cmd, sizeof(cmd));
+ else if (!autoboot || !OPT_CHECK(RBX_QUIET))
+ putchar('\n');
+ autoboot = 0;
+ if (parse_cmd())
+ putchar('\a');
+ else
+ load();
+ }
+}
+
+/* XXX - Needed for btxld to link the boot2 binary; do not remove. */
+void
+exit(int x)
+{
+ __exit(x);
+}
+
+void
+reboot(void)
+{
+ __exit(0);
+}
+
+static void
+load(void)
+{
+ union {
+ struct exec ex;
+ Elf32_Ehdr eh;
+ } hdr;
+ static Elf32_Phdr ep[2];
+ static Elf32_Shdr es[2];
+ caddr_t p;
+ dnode_phys_t dn;
+ off_t off;
+ uint32_t addr, x;
+ int fmt, i, j;
+
+ if (zfs_lookup(&zfsmount, kname, &dn)) {
+ printf("\nCan't find %s\n", kname);
+ return;
+ }
+ off = 0;
+ if (xfsread(&dn, &off, &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);
+ off = PAGE_SIZE;
+ if (xfsread(&dn, &off, p, hdr.ex.a_text))
+ return;
+ p += roundup2(hdr.ex.a_text, PAGE_SIZE);
+ if (xfsread(&dn, &off, 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(&dn, &off, p, hdr.ex.a_syms))
+ return;
+ p += hdr.ex.a_syms;
+ if (xfsread(&dn, &off, p, sizeof(int)))
+ return;
+ x = *(uint32_t *)p;
+ p += sizeof(int);
+ x -= sizeof(int);
+ if (xfsread(&dn, &off, p, x))
+ return;
+ p += x;
+ }
+ } else {
+ off = hdr.eh.e_phoff;
+ for (j = i = 0; i < hdr.eh.e_phnum && j < 2; i++) {
+ if (xfsread(&dn, &off, 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);
+ off = ep[i].p_offset;
+ if (xfsread(&dn, &off, 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) {
+ off = hdr.eh.e_shoff + sizeof(es[0]) *
+ (hdr.eh.e_shstrndx + 1);
+ if (xfsread(&dn, &off, &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);
+ off = es[i].sh_offset;
+ if (xfsread(&dn, &off, 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);
+ zfsargs.size = sizeof(zfsargs);
+ zfsargs.pool = zfsmount.spa->spa_guid;
+ zfsargs.root = zfsmount.rootobj;
+ zfsargs.primary_pool = primary_spa->spa_guid;
+#ifdef LOADER_GELI_SUPPORT
+ explicit_bzero(gelipw, sizeof(gelipw));
+ gelibuf = malloc(sizeof(struct keybuf) + (GELI_MAX_KEYS * sizeof(struct keybuf_ent)));
+ geli_fill_keybuf(gelibuf);
+ zfsargs.notapw = '\0';
+ zfsargs.keybuf_sentinel = KEYBUF_SENTINEL;
+ zfsargs.keybuf = gelibuf;
+#else
+ zfsargs.gelipw[0] = '\0';
+#endif
+ if (primary_vdev != NULL)
+ zfsargs.primary_vdev = primary_vdev->v_guid;
+ else
+ printf("failed to detect primary vdev\n");
+ __exec((caddr_t)addr, RB_BOOTINFO | (opts & RBX_MASK),
+ bootdev,
+ KARGS_FLAGS_ZFS | KARGS_FLAGS_EXTARG,
+ (uint32_t) spa->spa_guid,
+ (uint32_t) (spa->spa_guid >> 32),
+ VTOP(&bootinfo),
+ zfsargs);
+}
+
+static int
+zfs_mount_ds(char *dsname)
+{
+ uint64_t newroot;
+ spa_t *newspa;
+ char *q;
+
+ q = strchr(dsname, '/');
+ if (q)
+ *q++ = '\0';
+ newspa = spa_find_by_name(dsname);
+ if (newspa == NULL) {
+ printf("\nCan't find ZFS pool %s\n", dsname);
+ return -1;
+ }
+
+ if (zfs_spa_init(newspa))
+ return -1;
+
+ newroot = 0;
+ if (q) {
+ if (zfs_lookup_dataset(newspa, q, &newroot)) {
+ printf("\nCan't find dataset %s in ZFS pool %s\n",
+ q, newspa->spa_name);
+ return -1;
+ }
+ }
+ if (zfs_mount(newspa, newroot, &zfsmount)) {
+ printf("\nCan't mount ZFS dataset\n");
+ return -1;
+ }
+ spa = newspa;
+ return (0);
+}
+
+static int
+parse_cmd(void)
+{
+ char *arg = cmd;
+ char *ep, *p, *q;
+ const char *cp;
+ int c, i, j;
+
+ while ((c = *arg++)) {
+ if (c == ' ' || c == '\t' || c == '\n')
+ continue;
+ for (p = arg; *p && *p != '\n' && *p != ' ' && *p != '\t'; p++);
+ ep = p;
+ if (*p)
+ *p++ = 0;
+ if (c == '-') {
+ while ((c = *arg++)) {
+ if (c == 'P') {
+ if (*(uint8_t *)PTOV(0x496) & 0x10) {
+ cp = "yes";
+ } else {
+ opts |= OPT_SET(RBX_DUAL) | OPT_SET(RBX_SERIAL);
+ cp = "no";
+ }
+ printf("Keyboard: %s\n", cp);
+ continue;
+ } else if (c == 'S') {
+ j = 0;
+ while ((unsigned int)(i = *arg++ - '0') <= 9)
+ j = j * 10 + i;
+ if (j > 0 && i == -'0') {
+ comspeed = j;
+ break;
+ }
+ /* Fall through to error below ('S' not in optstr[]). */
+ }
+ for (i = 0; c != optstr[i]; i++)
+ if (i == NOPT - 1)
+ return -1;
+ opts ^= OPT_SET(flags[i]);
+ }
+ ioctrl = OPT_CHECK(RBX_DUAL) ? (IO_SERIAL|IO_KEYBOARD) :
+ OPT_CHECK(RBX_SERIAL) ? IO_SERIAL : IO_KEYBOARD;
+ if (ioctrl & IO_SERIAL) {
+ if (sio_init(115200 / comspeed) != 0)
+ ioctrl &= ~IO_SERIAL;
+ }
+ } if (c == '?') {
+ dnode_phys_t dn;
+
+ if (zfs_lookup(&zfsmount, arg, &dn) == 0) {
+ zap_list(spa, &dn);
+ }
+ return -1;
+ } else {
+ arg--;
+
+ /*
+ * Report pool status if the comment is 'status'. Lets
+ * hope no-one wants to load /status as a kernel.
+ */
+ if (!strcmp(arg, "status")) {
+ spa_all_status();
+ return -1;
+ }
+
+ /*
+ * If there is "zfs:" prefix simply ignore it.
+ */
+ if (strncmp(arg, "zfs:", 4) == 0)
+ arg += 4;
+
+ /*
+ * If there is a colon, switch pools.
+ */
+ q = strchr(arg, ':');
+ if (q) {
+ *q++ = '\0';
+ if (zfs_mount_ds(arg) != 0)
+ return -1;
+ arg = q;
+ }
+ if ((i = ep - arg)) {
+ if ((size_t)i >= sizeof(kname))
+ return -1;
+ memcpy(kname, arg, i + 1);
+ }
+ }
+ arg = p;
+ }
+ return 0;
+}
diff --git a/stand/i386/zfsboot/zfsldr.S b/stand/i386/zfsboot/zfsldr.S
new file mode 100644
index 0000000..3e85e27
--- /dev/null
+++ b/stand/i386/zfsboot/zfsldr.S
@@ -0,0 +1,283 @@
+/*
+ * 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_ARG,0x900 # Arguments
+ .set MEM_ORG,0x7c00 # Origin
+ .set MEM_BUF,0x8000 # 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
+
+/* Misc. Constants */
+ .set SIZ_PAG,0x1000 # Page size
+ .set SIZ_SEC,0x200 # Sector size
+ .set COPY_BLKS,0x8 # Number of blocks
+ # to copy for boot2
+ .set COPY_BLK_SZ,0x8000 # Copy in 32k blocks; must be
+ # a multiple of 16 bytes
+ .set NSECT,(COPY_BLK_SZ / SIZ_SEC * COPY_BLKS)
+ .globl start
+ .code16
+
+/*
+ * Load the rest of zfsboot2 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.
+ */
+start: 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
+/*
+ * 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 read.
+ * 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.
+ */
+ call check_edd # Make sure EDD works
+ mov $part4,%si # Dummy partition
+ xor %eax,%eax # Read MBR
+ movl $MEM_BUF,%ebx # from first
+ call read # sector
+ 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
+
+/*
+ * 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 COPY_BLKS*COPY_BLK_SZ/SIZ_SEC sectors
+ * starting at sector 1024 (i.e. after the two vdev labels). We don't
+ * have do anything fancy here to allow for an extra copy of boot1 and
+ * a partition table (compare to this section of the UFS bootstrap) so we
+ * just load it all at 0x9000. The first part of boot2 is BTX, which wants
+ * to run at 0x9000. 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 zfsboot2, we use an entry point of
+ * 0x2000. That entry point is relative to MEM_USR; thus boot2.bin
+ * starts at 0xc000.
+ *
+ * The load area and the target area for the client overlap so we have
+ * to use a decrementing string move. We also play segment register
+ * games with the destination address for the move so that the client
+ * can be larger than 16k (which would overflow the zero segment since
+ * the client starts at 0xc000).
+ */
+main.5: mov %dx,MEM_ARG # Save args
+ mov $NSECT,%cx # Sector count
+ movl $1024,%eax # Offset to boot2
+ mov $MEM_BTX,%ebx # Destination buffer
+main.6: pushal # Save params
+ call read # Read disk
+ popal # Restore
+ incl %eax # Advance to
+ add $SIZ_SEC,%ebx # next sector
+ loop main.6 # If not last, read another
+
+ mov $MEM_BTX,%bx # BTX
+ mov 0xa(%bx),%si # Get BTX length and set
+ add %bx,%si # %si to start of boot2
+ dec %si # Set %ds:%si to point at the
+ mov %si,%ax # last byte we want to copy
+ shr $4,%ax # from boot2, with %si made as
+ add $(COPY_BLKS*COPY_BLK_SZ/16),%ax # small as possible.
+ and $0xf,%si #
+ mov %ax,%ds #
+ mov $(MEM_USR+2*SIZ_PAG)/16,%ax # Set %es:(-1) to point at
+ add $(COPY_BLKS*COPY_BLK_SZ/16),%ax # the last byte we
+ mov %ax,%es # want to copy boot2 into.
+ mov $COPY_BLKS,%bx # Copy COPY_BLKS 32k blocks
+copyloop:
+ add $COPY_BLK_SZ,%si # Adjust %ds:%si to point at
+ mov %ds,%ax # the end of the next 32k to
+ sub $COPY_BLK_SZ/16,%ax # copy from boot2
+ mov %ax,%ds
+ mov $COPY_BLK_SZ-1,%di # Adjust %es:%di to point at
+ mov %es,%ax # the end of the next 32k into
+ sub $COPY_BLK_SZ/16,%ax # which we want boot2 copied
+ mov %ax,%es
+ mov $COPY_BLK_SZ,%cx # Copy 32k
+ std
+ rep movsb
+ dec %bx
+ jnz copyloop
+ mov %cx,%ds # Reset %ds and %es
+ mov %cx,%es
+ cld # Back to increment
+
+/*
+ * Enable A20 so we can access memory above 1 meg.
+ * Use the zero-valued %cx as a timeout for embedded hardware which do not
+ * have a keyboard controller.
+ */
+seta20: cli # Disable interrupts
+seta20.1: dec %cx # Timeout?
+ jz seta20.3 # Yes
+ 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
+seta20.3: sti # Enable interrupts
+
+ jmp start+MEM_JMP-MEM_ORG # Start BTX
+
+
+/*
+ * Read a sector from the disk. Sets up an EDD packet on the stack
+ * and passes it to read. We assume that the destination address is
+ * always segment-aligned.
+ *
+ * %eax - int - LBA to read in relative to partition start
+ * %ebx - ptr - destination address
+ * %dl - byte - drive to read from
+ * %si - ptr - MBR partition entry
+ */
+read: xor %ecx,%ecx # Get
+ addl 0x8(%si),%eax # LBA
+ adc $0,%ecx
+ pushl %ecx # Starting absolute block
+ pushl %eax # block number
+ shr $4,%ebx # Convert to segment
+ push %bx # Address of
+ push $0 # transfer buffer
+ push $0x1 # Read 1 sector
+ push $0x10 # Size of packet
+ mov %sp,%si # Packet pointer
+ mov $0x42,%ah # BIOS: Extended
+ int $0x13 # read
+ jc read.1 # If error, fail
+ lea 0x10(%si),%sp # Clear stack
+ ret # If success, return
+read.1: mov %ah,%al # Format
+ mov $read_err,%di # error
+ call hex8 # code
+ mov $msg_read,%si # 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
+ ret # To caller
+/*
+ * Check to see if the disk supports EDD. zfsboot requires EDD and does not
+ * support older C/H/S disk I/O.
+ */
+check_edd: cmpb $0x80,%dl # Hard drive?
+ jb check_edd.1 # No, fail to boot
+ mov $0x55aa,%bx # Magic
+ push %dx # Save
+ movb $0x41,%ah # BIOS: Check
+ int $0x13 # extensions present
+ pop %dx # Restore
+ jc check_edd.1 # If error, fail
+ cmp $0xaa55,%bx # Magic?
+ jne check_edd.1 # No, so fail
+ testb $0x1,%cl # Packet interface?
+ jz check_edd.1 # No, so fail
+ ret # EDD ok, keep booting
+check_edd.1: mov $msg_chs,%si # Warn that CHS is
+ jmp error # unsupported and fail
+/*
+ * AL to hex, saving the result to [EDI].
+ */
+hex8: push %ax # Save
+ shrb $0x4,%al # Do upper
+ call hex8.1 # 4
+ pop %ax # 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)
+
+/* Messages */
+
+msg_chs: .asciz "CHS not supported"
+msg_read: .ascii "Read error: "
+read_err: .asciz "XX"
+msg_part: .asciz "Boot error"
+
+prompt: .asciz "\r\n"
+
+ .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/stand/i386/zfsloader/Makefile b/stand/i386/zfsloader/Makefile
new file mode 100644
index 0000000..a9fa913
--- /dev/null
+++ b/stand/i386/zfsloader/Makefile
@@ -0,0 +1,7 @@
+# $FreeBSD$
+
+LOADER= zfsloader
+NEWVERSWHAT= "ZFS enabled bootstrap loader" x86
+HAVE_ZFS= yes
+
+.include "${.CURDIR}/../loader/Makefile"
diff --git a/stand/i386/zfsloader/Makefile.depend b/stand/i386/zfsloader/Makefile.depend
new file mode 100644
index 0000000..15b0c98
--- /dev/null
+++ b/stand/i386/zfsloader/Makefile.depend
@@ -0,0 +1,22 @@
+# $FreeBSD$
+# Autogenerated - do NOT edit!
+
+DIRDEPS = \
+ include \
+ include/xlocale \
+ lib/libstand \
+ sys/boot/ficl32 \
+ sys/boot/geli \
+ sys/boot/i386/btx/btx \
+ sys/boot/i386/btx/btxldr \
+ sys/boot/i386/btx/lib \
+ sys/boot/i386/libi386 \
+ sys/boot/libstand32 \
+ sys/boot/zfs \
+
+
+.include <dirdeps.mk>
+
+.if ${DEP_RELDIR} == ${_DEP_RELDIR}
+# local dependencies - needed for -jN in clean tree
+.endif
OpenPOWER on IntegriCloud