diff options
Diffstat (limited to 'sys/boot/i386')
-rw-r--r-- | sys/boot/i386/Makefile | 4 | ||||
-rw-r--r-- | sys/boot/i386/libi386/bootinfo32.c | 1 | ||||
-rw-r--r-- | sys/boot/i386/libi386/devicename.c | 2 | ||||
-rw-r--r-- | sys/boot/i386/loader/Makefile | 10 | ||||
-rw-r--r-- | sys/boot/i386/loader/conf.c | 14 | ||||
-rw-r--r-- | sys/boot/i386/loader/main.c | 51 | ||||
-rw-r--r-- | sys/boot/i386/zfsboot/Makefile | 108 | ||||
-rw-r--r-- | sys/boot/i386/zfsboot/zfsboot.c | 944 | ||||
-rw-r--r-- | sys/boot/i386/zfsboot/zfsldr.S | 402 |
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 |