summaryrefslogtreecommitdiffstats
path: root/sys/boot/i386
diff options
context:
space:
mode:
Diffstat (limited to 'sys/boot/i386')
-rw-r--r--sys/boot/i386/Makefile4
-rw-r--r--sys/boot/i386/libi386/bootinfo32.c1
-rw-r--r--sys/boot/i386/libi386/devicename.c2
-rw-r--r--sys/boot/i386/loader/Makefile10
-rw-r--r--sys/boot/i386/loader/conf.c14
-rw-r--r--sys/boot/i386/loader/main.c51
-rw-r--r--sys/boot/i386/zfsboot/Makefile108
-rw-r--r--sys/boot/i386/zfsboot/zfsboot.c944
-rw-r--r--sys/boot/i386/zfsboot/zfsldr.S402
9 files changed, 1521 insertions, 15 deletions
diff --git a/sys/boot/i386/Makefile b/sys/boot/i386/Makefile
index b89222d..6af8642 100644
--- a/sys/boot/i386/Makefile
+++ b/sys/boot/i386/Makefile
@@ -1,7 +1,7 @@
# $FreeBSD$
-SUBDIR= mbr pmbr boot0 boot0sio btx boot2 cdboot gptboot kgzldr \
- libi386 libfirewire loader
+SUBDIR= mbr pmbr boot0 boot0sio btx boot2 cdboot gptboot zfsboot \
+ kgzldr libi386 libfirewire loader
# special boot programs, 'self-extracting boot2+loader'
SUBDIR+= pxeldr
diff --git a/sys/boot/i386/libi386/bootinfo32.c b/sys/boot/i386/libi386/bootinfo32.c
index 6b517c5..d434427 100644
--- a/sys/boot/i386/libi386/bootinfo32.c
+++ b/sys/boot/i386/libi386/bootinfo32.c
@@ -183,6 +183,7 @@ bi_load32(char *args, int *howtop, int *bootdevp, vm_offset_t *bip, vm_offset_t
break;
case DEVT_NET:
+ case DEVT_ZFS:
break;
default:
diff --git a/sys/boot/i386/libi386/devicename.c b/sys/boot/i386/libi386/devicename.c
index e1035aa..79a562b 100644
--- a/sys/boot/i386/libi386/devicename.c
+++ b/sys/boot/i386/libi386/devicename.c
@@ -167,6 +167,7 @@ i386_parsedev(struct i386_devdesc **dev, const char *devspec, const char **path)
case DEVT_CD:
case DEVT_NET:
+ case DEVT_ZFS:
unit = 0;
if (*np && (*np != ':')) {
@@ -238,6 +239,7 @@ i386_fmtdev(void *vdev)
break;
case DEVT_NET:
+ case DEVT_ZFS:
sprintf(buf, "%s%d:", dev->d_dev->dv_name, dev->d_unit);
break;
}
diff --git a/sys/boot/i386/loader/Makefile b/sys/boot/i386/loader/Makefile
index df2ccc0..79aceca 100644
--- a/sys/boot/i386/loader/Makefile
+++ b/sys/boot/i386/loader/Makefile
@@ -17,6 +17,12 @@ CFLAGS+= -DLOADER_FIREWIRE_SUPPORT
LIBFIREWIRE= ${.OBJDIR}/../libfirewire/libfirewire.a
.endif
+# Put LOADER_ZFS_SUPPORT=yes in /etc/make.conf for ZFS support
+.if defined(LOADER_ZFS_SUPPORT)
+CFLAGS+= -DLOADER_ZFS_SUPPORT
+LIBZFS= ${.OBJDIR}/../../zfs/libzfsboot.a
+.endif
+
# Enable PXE TFTP or NFS support, not both.
.if defined(LOADER_TFTP_SUPPORT)
CFLAGS+= -DLOADER_TFTP_SUPPORT
@@ -98,8 +104,8 @@ FILES+= loader.rc
# XXX crt0.o needs to be first for pxeboot(8) to work
OBJS= ${BTXCRT}
-DPADD= ${LIBFICL} ${LIBFIREWIRE} ${LIBI386} ${LIBSTAND}
-LDADD= ${LIBFICL} ${LIBFIREWIRE} ${LIBI386} -lstand
+DPADD= ${LIBFICL} ${LIBFIREWIRE} ${LIBZFS} ${LIBI386} ${LIBSTAND}
+LDADD= ${LIBFICL} ${LIBFIREWIRE} ${LIBZFS} ${LIBI386} -lstand
.include <bsd.prog.mk>
diff --git a/sys/boot/i386/loader/conf.c b/sys/boot/i386/loader/conf.c
index 245f960..05c9a9e9 100644
--- a/sys/boot/i386/loader/conf.c
+++ b/sys/boot/i386/loader/conf.c
@@ -50,6 +50,10 @@ __FBSDID("$FreeBSD$");
extern struct devsw fwohci;
#endif
+#if defined(LOADER_ZFS_SUPPORT)
+extern struct devsw zfs_dev;
+#endif
+
/* Exported for libstand */
struct devsw *devsw[] = {
&bioscd,
@@ -60,15 +64,25 @@ struct devsw *devsw[] = {
#if defined(LOADER_FIREWIRE_SUPPORT)
&fwohci,
#endif
+#if defined(LOADER_ZFS_SUPPORT)
+ &zfs_dev,
+#endif
NULL
};
+#if defined(LOADER_ZFS_SUPPORT)
+extern struct fs_ops zfs_fsops;
+#endif
+
struct fs_ops *file_system[] = {
&ufs_fsops,
&ext2fs_fsops,
&dosfs_fsops,
&cd9660_fsops,
&splitfs_fsops,
+#if defined(LOADER_ZFS_SUPPORT)
+ &zfs_fsops,
+#endif
#ifdef LOADER_GZIP_SUPPORT
&gzipfs_fsops,
#endif
diff --git a/sys/boot/i386/loader/main.c b/sys/boot/i386/loader/main.c
index 5b23670..cac28ae 100644
--- a/sys/boot/i386/loader/main.c
+++ b/sys/boot/i386/loader/main.c
@@ -44,6 +44,7 @@ __FBSDID("$FreeBSD$");
#define KARGS_FLAGS_CD 0x1
#define KARGS_FLAGS_PXE 0x2
+#define KARGS_FLAGS_ZFS 0x4
/* Arguments passed in from the boot1/boot2 loader */
static struct
@@ -51,8 +52,13 @@ static struct
u_int32_t howto;
u_int32_t bootdev;
u_int32_t bootflags;
- u_int32_t pxeinfo;
- u_int32_t res2;
+ union {
+ struct {
+ u_int32_t pxeinfo;
+ u_int32_t res2;
+ };
+ uint64_t zfspool;
+ };
u_int32_t bootinfo;
} *kargs;
@@ -96,7 +102,7 @@ main(void)
*/
bios_getmem();
-#if defined(LOADER_BZIP2_SUPPORT) || defined(LOADER_FIREWIRE_SUPPORT)
+#if defined(LOADER_BZIP2_SUPPORT) || defined(LOADER_FIREWIRE_SUPPORT) || defined(LOADER_ZFS_SUPPORT)
heap_top = PTOV(memtop_copyin);
memtop_copyin -= 0x300000;
heap_bottom = PTOV(memtop_copyin);
@@ -145,6 +151,14 @@ main(void)
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;
+
/*
* March through the device switch probing for things.
*/
@@ -172,14 +186,6 @@ main(void)
bios_getsmap();
- archsw.arch_autoload = i386_autoload;
- archsw.arch_getdev = i386_getdev;
- archsw.arch_copyin = i386_copyin;
- archsw.arch_copyout = i386_copyout;
- archsw.arch_readin = i386_readin;
- archsw.arch_isainb = isa_inb;
- archsw.arch_isaoutb = isa_outb;
-
interact(); /* doesn't return */
/* if we ever get here, it is an error */
@@ -252,6 +258,29 @@ extract_currdev(void)
i386_setcurrdev, env_nounset);
env_setenv("loaddev", EV_VOLATILE, i386_fmtdev(&new_currdev), env_noset,
env_nounset);
+
+#ifdef LOADER_ZFS_SUPPORT
+ /*
+ * If we were started from a ZFS-aware boot2, we can work out
+ * which ZFS pool we are booting from.
+ */
+ if (kargs->bootflags & KARGS_FLAGS_ZFS) {
+ /*
+ * Dig out the pool guid and convert it to a 'unit number'
+ */
+ uint64_t guid;
+ int unit;
+ char devname[32];
+ extern int zfs_guid_to_unit(uint64_t);
+
+ guid = kargs->zfspool;
+ unit = zfs_guid_to_unit(guid);
+ if (unit >= 0) {
+ sprintf(devname, "zfs%d", unit);
+ setenv("currdev", devname, 1);
+ }
+ }
+#endif
}
COMMAND_SET(reboot, "reboot", "reboot the system", command_reboot);
diff --git a/sys/boot/i386/zfsboot/Makefile b/sys/boot/i386/zfsboot/Makefile
new file mode 100644
index 0000000..41f1672
--- /dev/null
+++ b/sys/boot/i386/zfsboot/Makefile
@@ -0,0 +1,108 @@
+# $FreeBSD$
+
+.PATH: ${.CURDIR}/../boot2
+
+FILES= zfsboot
+
+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
+
+CFLAGS= -Os -g \
+ -fno-guess-branch-probability \
+ -fomit-frame-pointer \
+ -fno-unit-at-a-time \
+ -mno-align-long-strings \
+ -mrtd \
+ -mno-mmx -mno-3dnow -mno-sse -mno-sse2 -mno-sse3 \
+ -DBOOT2 \
+ -DFLAGS=${BOOT_BOOT1_FLAGS} \
+ -DSIOPRT=${BOOT_COMCONSOLE_PORT} \
+ -DSIOFMT=${B2SIOFMT} \
+ -DSIOSPD=${BOOT_COMCONSOLE_SPEED} \
+ -I${.CURDIR}/../../zfs \
+ -I${.CURDIR}/../../../cddl/boot/zfs \
+ -I${.CURDIR}/../btx/lib -I. \
+ -I${.CURDIR}/../boot2 \
+ -Wall -Waggregate-return -Wbad-function-cast -Wcast-align \
+ -Wmissing-declarations -Wmissing-prototypes -Wnested-externs \
+ -Wpointer-arith -Wshadow -Wstrict-prototypes -Wwrite-strings \
+ -Winline --param max-inline-insns-single=100
+
+LDFLAGS=-static -N --gc-sections
+
+# Pick up ../Makefile.inc early.
+.include <bsd.init.mk>
+
+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} ${LDFLAGS} -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 zfsboot.h sio.o
+
+# We currently allow 32768 bytes for zfsboot - in practice it could be
+# any size up to 3.5Mb but keeping it fixed size simplifies zfsldr.
+#
+BOOT2SIZE= 32768
+
+zfsboot2: zfsboot.ld
+ @set -- `ls -l zfsboot.ld`; x=$$((${BOOT2SIZE}-$$5)); \
+ echo "$$x bytes available"; test $$x -ge 0
+ dd if=zfsboot.ld 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
+ ${LD} ${LDFLAGS} -Ttext ${ORG2} -o ${.TARGET} ${.ALLSRC}
+
+zfsboot.o: zfsboot.s
+
+SRCS= zfsboot.c zfsboot.h
+
+zfsboot.s: zfsboot.c zfsboot.h ${.CURDIR}/../../zfs/zfsimpl.c
+ ${CC} ${CFLAGS} -S -o zfsboot.s.tmp ${.CURDIR}/zfsboot.c
+ sed -e '/align/d' -e '/nop/d' < zfsboot.s.tmp > zfsboot.s
+ rm -f zfsboot.s.tmp
+
+zfsboot.h: zfsldr.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}
+
+.if ${MACHINE_ARCH} == "amd64"
+beforedepend zfsboot.s: machine
+CLEANFILES+= machine
+machine:
+ ln -sf ${.CURDIR}/../../../i386/include machine
+.endif
+
+.include <bsd.prog.mk>
diff --git a/sys/boot/i386/zfsboot/zfsboot.c b/sys/boot/i386/zfsboot/zfsboot.c
new file mode 100644
index 0000000..9b0a465
--- /dev/null
+++ b/sys/boot/i386/zfsboot/zfsboot.c
@@ -0,0 +1,944 @@
+/*-
+ * 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>
+#include <sys/reboot.h>
+#include <sys/queue.h>
+
+#include <machine/bootinfo.h>
+#include <machine/elf.h>
+
+#include <stdarg.h>
+#include <stddef.h>
+
+#include <a.out.h>
+
+#include <btxv86.h>
+
+#include "zfsboot.h"
+#include "lib.h"
+
+#define IO_KEYBOARD 1
+#define IO_SERIAL 2
+
+#define SECOND 18 /* Circa that many ticks in a second. */
+
+#define RBX_ASKNAME 0x0 /* -a */
+#define RBX_SINGLE 0x1 /* -s */
+/* 0x2 is reserved for log2(RB_NOSYNC). */
+/* 0x3 is reserved for log2(RB_HALT). */
+/* 0x4 is reserved for log2(RB_INITNAME). */
+#define RBX_DFLTROOT 0x5 /* -r */
+#define RBX_KDB 0x6 /* -d */
+/* 0x7 is reserved for log2(RB_RDONLY). */
+/* 0x8 is reserved for log2(RB_DUMP). */
+/* 0x9 is reserved for log2(RB_MINIROOT). */
+#define RBX_CONFIG 0xa /* -c */
+#define RBX_VERBOSE 0xb /* -v */
+#define RBX_SERIAL 0xc /* -h */
+#define RBX_CDROM 0xd /* -C */
+/* 0xe is reserved for log2(RB_POWEROFF). */
+#define RBX_GDB 0xf /* -g */
+#define RBX_MUTE 0x10 /* -m */
+/* 0x11 is reserved for log2(RB_SELFTEST). */
+/* 0x12 is reserved for boot programs. */
+/* 0x13 is reserved for boot programs. */
+#define RBX_PAUSE 0x14 /* -p */
+#define RBX_QUIET 0x15 /* -q */
+#define RBX_NOINTR 0x1c /* -n */
+/* 0x1d is reserved for log2(RB_MULTIPLE) and is just misnamed here. */
+#define RBX_DUAL 0x1d /* -D */
+/* 0x1f is reserved for log2(RB_BOOTINFO). */
+
+/* pass: -a, -s, -r, -d, -c, -v, -h, -C, -g, -m, -p, -D */
+#define RBX_MASK (OPT_SET(RBX_ASKNAME) | OPT_SET(RBX_SINGLE) | \
+ OPT_SET(RBX_DFLTROOT) | OPT_SET(RBX_KDB ) | \
+ OPT_SET(RBX_CONFIG) | OPT_SET(RBX_VERBOSE) | \
+ OPT_SET(RBX_SERIAL) | OPT_SET(RBX_CDROM) | \
+ OPT_SET(RBX_GDB ) | OPT_SET(RBX_MUTE) | \
+ OPT_SET(RBX_PAUSE) | OPT_SET(RBX_DUAL))
+
+/* Hint to loader that we came from ZFS */
+#define KARGS_FLAGS_ZFS 0x4
+
+#define PATH_CONFIG "/boot.config"
+#define PATH_BOOT3 "/boot/loader"
+#define PATH_KERNEL "/boot/kernel/kernel"
+
+#define ARGS 0x900
+#define NOPT 14
+#define NDEV 3
+#define MEM_BASE 0x12
+#define MEM_EXT 0x15
+#define V86_CY(x) ((x) & 1)
+#define V86_ZR(x) ((x) & 0x40)
+
+#define DRV_HARD 0x80
+#define DRV_MASK 0x7f
+
+#define TYPE_AD 0
+#define TYPE_DA 1
+#define TYPE_MAXHARD TYPE_DA
+#define TYPE_FD 2
+
+#define OPT_SET(opt) (1 << (opt))
+#define OPT_CHECK(opt) ((opts) & OPT_SET(opt))
+
+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};
+
+struct dsk {
+ unsigned drive;
+ unsigned type;
+ unsigned unit;
+ unsigned slice;
+ unsigned part;
+ unsigned start;
+ int init;
+};
+static char cmd[512];
+static char kname[1024];
+static uint32_t opts;
+static int comspeed = SIOSPD;
+static struct bootinfo bootinfo;
+static uint32_t bootdev;
+static uint8_t ioctrl = IO_KEYBOARD;
+
+/* 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);
+static void load(void);
+static int parse(void);
+static void printf(const char *,...);
+static void putchar(int);
+static uint32_t memsize(void);
+static int drvread(struct dsk *, void *, unsigned, unsigned);
+static int keyhit(unsigned);
+static int xputc(int);
+static int xgetc(int);
+static 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 void
+strcpy(char *dst, const char *src)
+{
+ while (*src)
+ *dst++ = *src++;
+ *dst++ = 0;
+}
+
+static void
+strcat(char *dst, const char *src)
+{
+ while (*dst)
+ dst++;
+ while (*src)
+ *dst++ = *src++;
+ *dst++ = 0;
+}
+
+static int
+strcmp(const char *s1, const char *s2)
+{
+ for (; *s1 == *s2 && *s1; s1++, s2++);
+ return (unsigned char)*s1 - (unsigned char)*s2;
+}
+
+static const char *
+strchr(const char *s, char ch)
+{
+ for (; *s; s++)
+ if (*s == ch)
+ return s;
+ return 0;
+}
+
+static int
+memcmp(const void *p1, const void *p2, size_t n)
+{
+ const char *s1 = (const char *) p1;
+ const char *s2 = (const char *) p2;
+ for (; n > 0 && *s1 == *s2; s1++, s2++, n--);
+ if (n)
+ return (unsigned char)*s1 - (unsigned char)*s2;
+ else
+ return 0;
+}
+
+static void
+memset(void *p, char val, size_t n)
+{
+ char *s = (char *) p;
+ while (n--)
+ *s++ = val;
+}
+
+static void *
+malloc(size_t n)
+{
+ static char *heap_next;
+ static char *heap_end;
+
+ if (!heap_next) {
+ heap_next = (char *) dmadat + sizeof(*dmadat);
+ heap_end = (char *) (640*1024);
+ }
+
+ char *p = heap_next;
+ if (p + n > heap_end) {
+ printf("malloc failure\n");
+ for (;;)
+ ;
+ return 0;
+ }
+ heap_next += n;
+ return p;
+}
+
+static size_t
+strlen(const char *s)
+{
+ size_t len = 0;
+ while (*s++)
+ len++;
+ return len;
+}
+
+static char *
+strdup(const char *s)
+{
+ char *p = malloc(strlen(s) + 1);
+ strcpy(p, s);
+ return p;
+}
+
+#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
+ */
+spa_t *spa;
+
+/*
+ * 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;
+ unsigned int lba, nb;
+ struct dsk *dsk = (struct dsk *) priv;
+
+ if ((off & (DEV_BSIZE - 1)) || (bytes & (DEV_BSIZE - 1)))
+ return -1;
+
+ p = buf;
+ lba = off / DEV_BSIZE;
+ while (bytes > 0) {
+ nb = bytes / DEV_BSIZE;
+ if (nb > READ_BUF_SIZE / DEV_BSIZE)
+ nb = READ_BUF_SIZE / DEV_BSIZE;
+ if (drvread(dsk, dmadat->rdbuf, lba, nb))
+ return -1;
+ memcpy(p, dmadat->rdbuf, nb * DEV_BSIZE);
+ 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 %s\n", "format");
+ return -1;
+ }
+ return 0;
+}
+
+static inline uint32_t
+memsize(void)
+{
+ v86.addr = MEM_EXT;
+ v86.eax = 0x8800;
+ v86int();
+ return v86.eax;
+}
+
+static inline void
+getstr(void)
+{
+ char *s;
+ int c;
+
+ s = cmd;
+ for (;;) {
+ switch (c = xgetc(0)) {
+ case 0:
+ break;
+ case '\177':
+ case '\b':
+ if (s > cmd) {
+ s--;
+ printf("\b \b");
+ }
+ break;
+ case '\n':
+ case '\r':
+ *s = 0;
+ return;
+ default:
+ if (s - cmd < sizeof(cmd) - 1)
+ *s++ = c;
+ putchar(c);
+ }
+ }
+}
+
+static inline void
+putc(int c)
+{
+ v86.addr = 0x10;
+ v86.eax = 0xe00 | (c & 0xff);
+ v86.ebx = 0x7;
+ v86int();
+}
+
+/*
+ * 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.efl & 0x1) && /* 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);
+}
+
+static void
+probe_drive(struct dsk *dsk, spa_t **spap)
+{
+ struct dos_partition *dp;
+ char *sec;
+ unsigned i;
+
+ if (!int13probe(dsk->drive))
+ return;
+
+ /*
+ * If we find a vdev on the whole disk, stop here. Otherwise dig
+ * out the MBR and probe each slice in turn for a vdev.
+ */
+ if (vdev_probe(vdev_read, dsk, spap) == 0)
+ return;
+
+ sec = dmadat->secbuf;
+ dsk->start = 0;
+ 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;
+ if (vdev_probe(vdev_read, dsk, spap) == 0) {
+ /*
+ * We record the first pool we find (we will try to boot
+ * from that one.
+ */
+ spap = 0;
+
+ /*
+ * This slice had a vdev. We need a new dsk structure now
+ * sice the vdev now owns this one.
+ */
+ struct dsk *newdsk;
+ newdsk = malloc(sizeof(struct dsk));
+ *newdsk = *dsk;
+ dsk = newdsk;
+ }
+ }
+}
+
+int
+main(void)
+{
+ int autoboot, i;
+ dnode_phys_t dn;
+ off_t off;
+ struct dsk *dsk;
+
+ dmadat = (void *)(roundup2(__base + (int32_t)&_end, 0x10000) - __base);
+ v86.ctl = V86_FLAGS;
+
+ 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->init = 0;
+
+ bootinfo.bi_version = BOOTINFO_VERSION;
+ bootinfo.bi_size = sizeof(bootinfo);
+ bootinfo.bi_basemem = 0; /* XXX will be filled by loader or kernel */
+ bootinfo.bi_extmem = memsize();
+ bootinfo.bi_memsizes_valid++;
+ bootinfo.bi_bios_dev = dsk->drive;
+
+ bootdev = MAKEBOOTDEV(dev_maj[dsk->type],
+ dsk->slice, dsk->unit, dsk->part),
+
+ /* Process configuration file */
+
+ autoboot = 1;
+
+ zfs_init();
+
+ /*
+ * Probe the boot drive first - we will try to boot from whatever
+ * pool we find on that drive.
+ */
+ probe_drive(dsk, &spa);
+
+ /*
+ * 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.
+ */
+ for (i = 0; i < 4; i++) {
+ if ((i | DRV_HARD) == *(uint8_t *)PTOV(ARGS))
+ continue;
+
+ 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->init = 0;
+ probe_drive(dsk, 0);
+ }
+
+ /*
+ * If we didn't find a pool on the boot drive, default to the
+ * first pool we found, if any.
+ */
+ if (!spa) {
+ spa = STAILQ_FIRST(&zfs_pools);
+ if (!spa) {
+ printf("No ZFS pools located, can't boot\n");
+ for (;;)
+ ;
+ }
+ }
+
+ zfs_mount_pool(spa);
+
+ if (zfs_lookup(spa, PATH_CONFIG, &dn) == 0) {
+ off = 0;
+ xfsread(&dn, &off, cmd, sizeof(cmd));
+ }
+
+ if (*cmd) {
+ if (parse())
+ autoboot = 0;
+ if (!OPT_CHECK(RBX_QUIET))
+ printf("%s: %s", PATH_CONFIG, cmd);
+ /* Do not process this command twice */
+ *cmd = 0;
+ }
+
+ /*
+ * Try to exec stage 3 boot loader. If interrupted by a keypress,
+ * or in case of failure, try to load a kernel directly instead.
+ */
+
+ if (autoboot && !*kname) {
+ memcpy(kname, PATH_BOOT3, sizeof(PATH_BOOT3));
+ if (!keyhit(3*SECOND)) {
+ load();
+ memcpy(kname, PATH_KERNEL, sizeof(PATH_KERNEL));
+ }
+ }
+
+ /* Present the user with the boot2 prompt. */
+
+ for (;;) {
+ if (!autoboot || !OPT_CHECK(RBX_QUIET))
+ printf("\nFreeBSD/i386 boot\n"
+ "Default: %s:%s\n"
+ "boot: ",
+ spa->spa_name, kname);
+ if (ioctrl & IO_SERIAL)
+ sio_flush();
+ if (!autoboot || keyhit(5*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;
+ dnode_phys_t dn;
+ off_t off;
+ uint32_t addr, x;
+ int fmt, i, j;
+
+ if (zfs_lookup(spa, kname, &dn)) {
+ 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);
+ __exec((caddr_t)addr, RB_BOOTINFO | (opts & RBX_MASK),
+ bootdev,
+ KARGS_FLAGS_ZFS,
+ (uint32_t) spa->spa_guid,
+ (uint32_t) (spa->spa_guid >> 32),
+ VTOP(&bootinfo));
+}
+
+static int
+parse()
+{
+ char *arg = cmd;
+ char *ep, *p, *q;
+ const char *cp;
+ //unsigned int drv;
+ 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)
+ sio_init(115200 / comspeed);
+ } if (c == '?') {
+ dnode_phys_t dn;
+
+ if (zfs_lookup(spa, 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 a colon, switch pools.
+ */
+ q = (char *) strchr(arg, ':');
+ if (q) {
+ spa_t *newspa;
+
+ *q++ = 0;
+ newspa = spa_find_by_name(arg);
+ if (newspa) {
+ spa = newspa;
+ zfs_mount_pool(spa);
+ } else {
+ printf("\nCan't find ZFS pool %s\n", arg);
+ 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;
+}
+
+static void
+printf(const char *fmt,...)
+{
+ va_list ap;
+ char buf[10];
+ char *s;
+ unsigned u;
+ int c;
+ int minus;
+ int prec;
+ int len;
+ int pad;
+
+ va_start(ap, fmt);
+ while ((c = *fmt++)) {
+ if (c == '%') {
+ minus = 0;
+ prec = 0;
+ nextfmt:
+ c = *fmt++;
+ switch (c) {
+ case '-':
+ minus = 1;
+ goto nextfmt;
+ case '0':
+ case '1':
+ case '2':
+ case '3':
+ case '4':
+ case '5':
+ case '6':
+ case '7':
+ case '8':
+ case '9':
+ prec = 10 * prec + (c - '0');
+ goto nextfmt;
+ case 'c':
+ putchar(va_arg(ap, int));
+ continue;
+ case 's':
+ s = va_arg(ap, char *);
+ if (prec) {
+ len = strlen(s);
+ if (len < prec)
+ pad = prec - len;
+ else
+ pad = 0;
+ if (minus)
+ while (pad--)
+ putchar(' ');
+ for (; *s; s++)
+ putchar(*s);
+ if (!minus)
+ while (pad--)
+ putchar(' ');
+ } else {
+ for (; *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(struct dsk *dsk, void *buf, unsigned lba, unsigned nblk)
+{
+ static unsigned c = 0x2d5c7c2f;
+
+ lba += dsk->start;
+ if (!OPT_CHECK(RBX_QUIET))
+ printf("%c\b", c = c << 8 | c >> 24);
+ v86.ctl = V86_ADDR | V86_CALLF | V86_FLAGS;
+ v86.addr = XREADORG; /* call to xread in boot1 */
+ v86.es = VTOPSEG(buf);
+ v86.eax = lba;
+ v86.ebx = VTOPOFF(buf);
+ v86.ecx = lba >> 16;
+ v86.edx = nblk << 8 | dsk->drive;
+ v86int();
+ v86.ctl = V86_FLAGS;
+ if (V86_CY(v86.efl)) {
+ printf("error %u lba %u\n", v86.eax >> 8 & 0xff, lba);
+ return -1;
+ }
+ return 0;
+}
+
+static int
+keyhit(unsigned ticks)
+{
+ uint32_t t0, t1;
+
+ if (OPT_CHECK(RBX_NOINTR))
+ return 0;
+ t0 = 0;
+ for (;;) {
+ if (xgetc(1))
+ return 1;
+ t1 = *(uint32_t *)PTOV(0x46c);
+ if (!t0)
+ t0 = t1;
+ if (t1 < t0 || t1 >= t0 + ticks)
+ return 0;
+ }
+}
+
+static int
+xputc(int c)
+{
+ if (ioctrl & IO_KEYBOARD)
+ putc(c);
+ if (ioctrl & IO_SERIAL)
+ sio_putc(c);
+ return c;
+}
+
+static int
+xgetc(int fn)
+{
+ if (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;
+ }
+}
+
+static int
+getc(int fn)
+{
+ /*
+ * The extra comparison against zero 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.
+ */
+ v86.addr = 0x16;
+ v86.eax = fn << 8;
+ v86int();
+ return fn == 0 ? v86.eax & 0xff : (!V86_ZR(v86.efl) && (v86.eax & 0xff));
+}
diff --git a/sys/boot/i386/zfsboot/zfsldr.S b/sys/boot/i386/zfsboot/zfsldr.S
new file mode 100644
index 0000000..a256d30
--- /dev/null
+++ b/sys/boot/i386/zfsboot/zfsldr.S
@@ -0,0 +1,402 @@
+/*
+ * 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,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
+
+/* Flag Bits */
+ .set FL_PACKET,0x80 # Packet mode
+
+/* Misc. Constants */
+ .set SIZ_PAG,0x1000 # Page size
+ .set SIZ_SEC,0x200 # Sector size
+
+ .set NSECT,0x40
+ .globl start
+ .globl xread
+ .code16
+
+start: jmp main # Start recognizably
+
+/*
+ * This is the start of a standard BIOS Parameter Block (BPB). Most bootable
+ * FAT disks have this at the start of their MBR. While normal BIOS's will
+ * work fine without this section, IBM's El Torito emulation "fixes" up the
+ * BPB by writing into the memory copy of the MBR. Rather than have data
+ * written into our xread routine, we'll define a BPB to work around it.
+ * The data marked with (T) indicates a field required for a ThinkPad to
+ * recognize the disk and (W) indicates fields written from IBM BIOS code.
+ * The use of the BPB is based on what OpenBSD and NetBSD implemented in
+ * their boot code but the required fields were determined by trial and error.
+ *
+ * Note: If additional space is needed in boot1, one solution would be to
+ * move the "prompt" message data (below) to replace the OEM ID.
+ */
+ .org 0x03, 0x00
+oemid: .space 0x08, 0x00 # OEM ID
+
+ .org 0x0b, 0x00
+bpb: .word 512 # sector size (T)
+ .byte 0 # sectors/clustor
+ .word 0 # reserved sectors
+ .byte 0 # number of FATs
+ .word 0 # root entries
+ .word 0 # small sectors
+ .byte 0 # media type (W)
+ .word 0 # sectors/fat
+ .word 18 # sectors per track (T)
+ .word 2 # number of heads (T)
+ .long 0 # hidden sectors (W)
+ .long 0 # large sectors
+
+ .org 0x24, 0x00
+ebpb: .byte 0 # BIOS physical drive number (W)
+
+ .org 0x25,0x90
+/*
+ * Trampoline used by boot2 to call read to read data from the disk via
+ * the BIOS. Call with:
+ *
+ * %cx:%ax - long - LBA to read in
+ * %es:(%bx) - caddr_t - buffer to read data into
+ * %dl - byte - drive to read from
+ * %dh - byte - num sectors to read
+ */
+
+xread: push %ss # Address
+ pop %ds # data
+/*
+ * Setup an EDD disk packet and pass it to read
+ */
+xread.1: # Starting
+ pushl $0x0 # absolute
+ push %cx # block
+ push %ax # number
+ push %es # Address of
+ push %bx # transfer buffer
+ xor %ax,%ax # Number of
+ movb %dh,%al # blocks to
+ push %ax # transfer
+ push $0x10 # Size of packet
+ mov %sp,%bp # Packet pointer
+ callw read # Read from disk
+ lea 0x10(%bp),%sp # Clear stack
+ lret # To far caller
+/*
+ * Load the rest of boot2 and BTX up, copy the parts to the right locations,
+ * and start it all up.
+ */
+
+/*
+ * Setup the segment registers to flat addressing (segment 0) and setup the
+ * stack to end just below the start of our code.
+ */
+main: cld # String ops inc
+ xor %cx,%cx # Zero
+ mov %cx,%es # Address
+ mov %cx,%ds # data
+ mov %cx,%ss # Set up
+ mov $start,%sp # stack
+/*
+ * Relocate ourself to MEM_REL. Since %cx == 0, the inc %ch sets
+ * %cx == 0x100.
+ */
+ mov %sp,%si # Source
+ mov $MEM_REL,%di # Destination
+ incb %ch # Word count
+ rep # Copy
+ movsw # code
+/*
+ * If we are on a hard drive, then load the MBR and look for the first
+ * FreeBSD slice. We use the fake partition entry below that points to
+ * the MBR when we call nread. The first pass looks for the first active
+ * FreeBSD slice. The second pass looks for the first non-active FreeBSD
+ * slice if the first one fails.
+ */
+ mov $part4,%si # Partition
+ cmpb $0x80,%dl # Hard drive?
+ jb main.4 # No
+ movb $0x1,%dh # Block count
+ callw nread # Read MBR
+ mov $0x1,%cx # Two passes
+main.1: mov $MEM_BUF+PRT_OFF,%si # Partition table
+ movb $0x1,%dh # Partition
+main.2: cmpb $PRT_BSD,0x4(%si) # Our partition type?
+ jne main.3 # No
+ jcxz main.5 # If second pass
+ testb $0x80,(%si) # Active?
+ jnz main.5 # Yes
+main.3: add $0x10,%si # Next entry
+ incb %dh # Partition
+ cmpb $0x1+PRT_NUM,%dh # In table?
+ jb main.2 # Yes
+ dec %cx # Do two
+ jcxz main.1 # passes
+/*
+ * If we get here, we didn't find any FreeBSD slices at all, so print an
+ * error message and die.
+ */
+ mov $msg_part,%si # Message
+ jmp error # Error
+/*
+ * Floppies use partition 0 of drive 0.
+ */
+main.4: xor %dx,%dx # Partition:drive
+
+/*
+ * Ok, we have a slice and drive in %dx now, so use that to locate and
+ * load boot2. %si references the start of the slice we are looking
+ * for, so go ahead and load up the 64 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 0x8000. 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. After we have moved the client, we relocate BTX
+ * itself to 0x9000 - doing it in this order means that none of the
+ * memcpy regions overlap which would corrupt the copy. 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.
+ *
+ * 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). Relocating BTX is easy since the load
+ * area and target area do not overlap.
+ */
+main.5: mov %dx,MEM_ARG # Save args
+ movb $NSECT,%dh # Sector count
+ movw $1024,%ax # Offset to boot2
+ callw nread.1 # Read disk
+main.6: mov $MEM_BUF,%si # BTX (before reloc)
+ mov 0xa(%si),%bx # Get BTX length and set
+ mov $NSECT*SIZ_SEC-1,%di # Size of load area (less one)
+ mov %di,%si # End of load
+ add $MEM_BUF,%si # area
+ sub %bx,%di # End of client, 0xc000 rel
+ mov %di,%cx # Size of
+ inc %cx # client
+ mov $(MEM_USR+2*SIZ_PAG)>>4,%dx # Segment
+ mov %dx,%es # addressing 0xc000
+ std # Move with decrement
+ rep # Relocate
+ movsb # client
+ mov %ds,%dx # Back to
+ mov %dx,%es # zero segment
+ mov $MEM_BUF,%si # BTX (before reloc)
+ mov $MEM_BTX,%di # BTX
+ mov %bx,%cx # Get BTX length
+ cld # Increment this time
+ rep # Relocate
+ movsb # BTX
+
+/*
+ * 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: xor %ax,%ax # Sector offset in partition
+nread.1: mov $MEM_BUF,%bx # Transfer buffer
+ add 0x8(%si),%ax # Get
+ mov 0xa(%si),%cx # LBA
+ push %cs # Read from
+ callw xread.1 # disk
+ jnc return # If success, return
+ mov $msg_read,%si # Otherwise, set the error
+ # message and fall through to
+ # the error routine
+/*
+ * Print out the error message pointed to by %ds:(%si) followed
+ * by a prompt, wait for a keypress, and then reboot the machine.
+ */
+error: callw putstr # Display message
+ mov $prompt,%si # Display
+ callw putstr # prompt
+ xorb %ah,%ah # BIOS: Get
+ int $0x16 # keypress
+ movw $0x1234, BDA_BOOT # Do a warm boot
+ ljmp $0xffff,$0x0 # reboot the machine
+/*
+ * Display a null-terminated string using the BIOS output.
+ */
+putstr.0: mov $0x7,%bx # Page:attribute
+ movb $0xe,%ah # BIOS: Display
+ int $0x10 # character
+putstr: lodsb # Get char
+ testb %al,%al # End of string?
+ jne putstr.0 # No
+
+/*
+ * Overused return code. ereturn is used to return an error from the
+ * read function. Since we assume putstr succeeds, we (ab)use the
+ * same code when we return from putstr.
+ */
+ereturn: movb $0x1,%ah # Invalid
+ stc # argument
+return: retw # To caller
+/*
+ * Reads sectors from the disk. If EDD is enabled, then check if it is
+ * installed and use it if it is. If it is not installed or not enabled, then
+ * fall back to using CHS. Since we use a LBA, if we are using CHS, we have to
+ * fetch the drive parameters from the BIOS and divide it out ourselves.
+ * Call with:
+ *
+ * %dl - byte - drive number
+ * stack - 10 bytes - EDD Packet
+ */
+read: 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
+#if 0
+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
+#else
+read.1: mov $msg_chs,%si
+ jmp error
+msg_chs: .asciz "CHS not supported"
+#endif
+
+/* 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
OpenPOWER on IntegriCloud