Initial version of a unified installboot(8) that lives outside of
authorjsing <jsing@openbsd.org>
Fri, 27 Dec 2013 13:52:40 +0000 (13:52 +0000)
committerjsing <jsing@openbsd.org>
Fri, 27 Dec 2013 13:52:40 +0000 (13:52 +0000)
sys/arch/${MACHINE}/stand. For now this only supports i386, however
additional architectures will be added and further development can happen
in tree.

Requested by deraadt@ quite some time ago.

usr.sbin/installboot/Makefile [new file with mode: 0644]
usr.sbin/installboot/i386/Makefile.inc [new file with mode: 0644]
usr.sbin/installboot/i386/i386_installboot.c [new file with mode: 0644]
usr.sbin/installboot/i386/i386_installboot.h [new file with mode: 0644]
usr.sbin/installboot/i386/i386_softraid.c [new file with mode: 0644]
usr.sbin/installboot/installboot.c [new file with mode: 0644]
usr.sbin/installboot/installboot.h [new file with mode: 0644]
usr.sbin/installboot/softraid.c [new file with mode: 0644]

diff --git a/usr.sbin/installboot/Makefile b/usr.sbin/installboot/Makefile
new file mode 100644 (file)
index 0000000..a24d18d
--- /dev/null
@@ -0,0 +1,23 @@
+#      $OpenBSD: Makefile,v 1.1 2013/12/27 13:52:40 jsing Exp $
+
+NOMAN=
+
+.if (${MACHINE} == "i386")
+
+PROG=          installboot
+
+CPPFLAGS=      -I${.CURDIR}
+LDADD=         -lutil
+DPADD=         ${LIBUTIL}
+
+LDSTATIC=      -static
+
+SRCS += installboot.c
+
+.include "${.CURDIR}/${MACHINE}/Makefile.inc"
+
+.else
+NOPROG=
+.endif
+
+.include <bsd.prog.mk>
diff --git a/usr.sbin/installboot/i386/Makefile.inc b/usr.sbin/installboot/i386/Makefile.inc
new file mode 100644 (file)
index 0000000..da1acc8
--- /dev/null
@@ -0,0 +1,10 @@
+#      $OpenBSD: Makefile.inc,v 1.1 2013/12/27 13:52:40 jsing Exp $
+
+CPPFLAGS += -I${.CURDIR}/i386
+CFLAGS += -DSOFTRAID
+
+SRCS += softraid.c
+
+.PATH:  ${.CURDIR}/i386
+SRCS += i386_installboot.c
+SRCS += i386_softraid.c
diff --git a/usr.sbin/installboot/i386/i386_installboot.c b/usr.sbin/installboot/i386/i386_installboot.c
new file mode 100644 (file)
index 0000000..6de183e
--- /dev/null
@@ -0,0 +1,567 @@
+/*     $OpenBSD: i386_installboot.c,v 1.1 2013/12/27 13:52:40 jsing Exp $      */
+/*     $NetBSD: installboot.c,v 1.5 1995/11/17 23:23:50 gwr Exp $ */
+
+/*
+ * Copyright (c) 2011 Joel Sing <jsing@openbsd.org>
+ * Copyright (c) 2010 Otto Moerbeek <otto@openbsd.org>
+ * Copyright (c) 2003 Tom Cosgrove <tom.cosgrove@arches-consulting.com>
+ * Copyright (c) 1997 Michael Shalayeff
+ * Copyright (c) 1994 Paul Kranenburg
+ * 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 by Paul Kranenburg.
+ * 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/param.h>
+#include <sys/disklabel.h>
+#include <sys/dkio.h>
+#include <sys/exec_elf.h>
+#include <sys/ioctl.h>
+#include <sys/mount.h>
+#include <sys/reboot.h>
+#include <sys/stat.h>
+#include <sys/sysctl.h>
+#include <sys/time.h>
+
+#include <ufs/ufs/dinode.h>
+#include <ufs/ufs/dir.h>
+#include <ufs/ffs/fs.h>
+
+#include <uvm/uvm_extern.h>
+
+#include <machine/cpu.h>
+#include <machine/biosvar.h>
+
+#include <a.out.h>
+#include <err.h>
+#include <fcntl.h>
+#include <nlist.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <unistd.h>
+#include <util.h>
+
+#include "installboot.h"
+#include "i386_installboot.h"
+
+char   *blkstore;
+size_t blksize;
+
+struct sym_data pbr_symbols[] = {
+       {"_fs_bsize_p", 2},
+       {"_fs_bsize_s", 2},
+       {"_fsbtodb",    1},
+       {"_p_offset",   4},
+       {"_inodeblk",   4},
+       {"_inodedbl",   4},
+       {"_nblocks",    2},
+       {NULL}
+};
+
+static void    devread(int, void *, daddr_t, size_t, char *);
+static u_int   findopenbsd(int, struct disklabel *);
+static int     getbootparams(char *, int, struct disklabel *);
+static char    *loadproto(char *, long *);
+
+/*
+ * Read information about /boot's inode and filesystem parameters, then
+ * put biosboot (partition boot record) on the target drive with these
+ * parameters patched in.
+ */
+
+void
+md_init(void)
+{
+       stages = 2;
+       stage1 = "/usr/mdec/biosboot";
+       stage2 = "/boot";
+}
+
+void
+md_loadboot(void)
+{
+       /* Load prototype boot blocks. */
+       if ((blkstore = loadproto(stage1, &blksize)) == NULL)
+               exit(1);
+
+       /* XXX - Paranoia: Make sure size is aligned! */
+       if (blksize & (DEV_BSIZE - 1))
+               errx(1, "proto %s bad size=%ld", stage1, blksize);
+
+       if (blksize > SBSIZE - DEV_BSIZE)
+               errx(1, "proto bootblocks too big");
+}
+
+void
+md_installboot(int devfd, char *dev)
+{
+       struct disklabel dl;
+
+       /* Get and check disklabel. */
+       if (ioctl(devfd, DIOCGDINFO, &dl) != 0)
+               err(1, "disklabel: %s", dev);
+       if (dl.d_magic != DISKMAGIC)
+               errx(1, "bad disklabel magic=0x%08x", dl.d_magic);
+
+       /* Warn on unknown disklabel types. */
+       if (dl.d_type == 0)
+               warnx("disklabel type unknown");
+
+       /* Get bootstrap parameters to patch into proto. */
+       if (getbootparams(stage2, devfd, &dl) != 0)
+               exit(1);
+
+       /* Write boot blocks to device. */
+       write_bootblocks(devfd, dev, &dl);
+}
+
+void
+write_bootblocks(int devfd, char *dev, struct disklabel *dl)
+{
+       struct stat     sb;
+       u_int8_t        *secbuf;
+       u_int           start = 0;
+
+       /* Write patched proto bootblock(s) into the superblock. */
+       if (fstat(devfd, &sb) < 0)
+               err(1, "fstat: %s", dev);
+
+       if (!S_ISCHR(sb.st_mode))
+               errx(1, "%s: not a character device", dev);
+
+       /* Patch the parameters into the proto bootstrap sector. */
+       pbr_set_symbols(stage1, blkstore, pbr_symbols);
+
+       if (!nowrite) {
+               /* Sync filesystems (to clean in-memory superblock?). */
+               sync(); sleep(1);
+       }
+
+       /*
+        * Find OpenBSD partition. Floppies are special, getting an
+        * everything-in-one /boot starting at sector 0.
+        */
+       if (dl->d_type != DTYPE_FLOPPY) {
+               start = findopenbsd(devfd, dl);
+               if (start == (u_int)-1)
+                       errx(1, "no OpenBSD partition");
+       }
+
+       if (verbose)
+               fprintf(stderr, "%s will be written at sector %u\n",
+                   stage1, start);
+
+       if (start + (blksize / dl->d_secsize) > BOOTBIOS_MAXSEC)
+               warnx("%s extends beyond sector %u. OpenBSD might not boot.",
+                   stage1, BOOTBIOS_MAXSEC);
+
+       if (!nowrite) {
+               if (lseek(devfd, (off_t)start * dl->d_secsize, SEEK_SET) < 0)
+                       err(1, "seek bootstrap");
+               secbuf = calloc(1, dl->d_secsize);
+               bcopy(blkstore, secbuf, blksize);
+               if (write(devfd, secbuf, dl->d_secsize) != dl->d_secsize)
+                       err(1, "write bootstrap");
+               free(secbuf);
+       }
+}
+
+u_int
+findopenbsd(int devfd, struct disklabel *dl)
+{
+       struct          dos_mbr mbr;
+       u_int           mbroff = DOSBBSECTOR;
+       u_int           mbr_eoff = DOSBBSECTOR; /* Offset of extended part. */
+       struct          dos_partition *dp;
+       u_int8_t        *secbuf;
+       u_int           maxebr = DOS_MAXEBR, nextebr;
+       int             i;
+
+again:
+       if (!maxebr--) {
+               if (verbose)
+                       fprintf(stderr, "Traversed more than %d Extended Boot "
+                           "Records (EBRs)\n", DOS_MAXEBR);
+               return ((u_int)-1);
+       }
+               
+       if (verbose)
+               fprintf(stderr, "%s boot record (%cBR) at sector %u\n",
+                   (mbroff == DOSBBSECTOR) ? "master" : "extended",
+                   (mbroff == DOSBBSECTOR) ? 'M' : 'E', mbroff);
+
+       secbuf = malloc(dl->d_secsize); 
+       if (lseek(devfd, (off_t)mbroff * dl->d_secsize, SEEK_SET) < 0 ||
+           read(devfd, secbuf, dl->d_secsize) < (ssize_t)sizeof(mbr))
+               err(4, "can't read boot record");
+       bcopy(secbuf, &mbr, sizeof(mbr));
+       free(secbuf);
+
+       if (mbr.dmbr_sign != DOSMBR_SIGNATURE)
+               errx(1, "invalid boot record signature (0x%04X) @ sector %u",
+                   mbr.dmbr_sign, mbroff);
+
+       nextebr = 0;
+       for (i = 0; i < NDOSPART; i++) {
+               dp = &mbr.dmbr_parts[i];
+               if (!dp->dp_size)
+                       continue;
+
+               if (verbose)
+                       fprintf(stderr,
+                           "\tpartition %d: type 0x%02X offset %u size %u\n",
+                           i, dp->dp_typ, dp->dp_start, dp->dp_size);
+
+               if (dp->dp_typ == DOSPTYP_OPENBSD) {
+                       if (dp->dp_start > (dp->dp_start + mbroff))
+                               continue;
+                       return (dp->dp_start + mbroff);
+               }
+
+               if (!nextebr && (dp->dp_typ == DOSPTYP_EXTEND ||
+                   dp->dp_typ == DOSPTYP_EXTENDL)) {
+                       nextebr = dp->dp_start + mbr_eoff;
+                       if (nextebr < dp->dp_start)
+                               nextebr = (u_int)-1;
+                       if (mbr_eoff == DOSBBSECTOR)
+                               mbr_eoff = dp->dp_start;
+               }
+       }
+
+       if (nextebr && nextebr != (u_int)-1) {
+               mbroff = nextebr;
+               goto again;
+       }
+
+       return ((u_int)-1);
+}
+
+/*
+ * Load the prototype boot sector (biosboot) into memory.
+ */
+static char *
+loadproto(char *fname, long *size)
+{
+       int     fd;
+       size_t  tdsize;         /* text+data size */
+       char    *bp;
+       Elf_Ehdr eh;
+       Elf_Word phsize;
+       Elf_Phdr *ph;
+
+       if ((fd = open(fname, O_RDONLY)) < 0)
+               err(1, "%s", fname);
+
+       if (read(fd, &eh, sizeof(eh)) != sizeof(eh))
+               errx(1, "%s: read failed", fname);
+
+       if (!IS_ELF(eh))
+               errx(1, "%s: bad magic: 0x%02x%02x%02x%02x", fname,
+                   eh.e_ident[EI_MAG0], eh.e_ident[EI_MAG1],
+                   eh.e_ident[EI_MAG2], eh.e_ident[EI_MAG3]);
+
+       /*
+        * We have to include the exec header in the beginning of
+        * the buffer, and leave extra space at the end in case
+        * the actual write to disk wants to skip the header.
+        */
+
+       /* Program load header. */
+       if (eh.e_phnum != 1)
+               errx(1, "%s: %u ELF load sections (only support 1)",
+                   fname, eh.e_phnum);
+
+       phsize = eh.e_phnum * sizeof(Elf_Phdr);
+       ph = malloc(phsize);
+       if (ph == NULL)
+               err(1, NULL);
+
+       lseek(fd, eh.e_phoff, SEEK_SET);
+
+       if (read(fd, ph, phsize) != phsize)
+               errx(1, "%s: can't read header", fname);
+
+       tdsize = ph->p_filesz;
+
+       /*
+        * Allocate extra space here because the caller may copy
+        * the boot block starting at the end of the exec header.
+        * This prevents reading beyond the end of the buffer.
+        */
+       if ((bp = calloc(tdsize, 1)) == NULL)
+               err(1, NULL);
+
+       /* Read the rest of the file. */
+       lseek(fd, ph->p_offset, SEEK_SET);
+       if (read(fd, bp, tdsize) != (ssize_t)tdsize)
+               errx(1, "%s: read failed", fname);
+
+       *size = tdsize; /* not aligned to DEV_BSIZE */
+
+       close(fd);
+       return bp;
+}
+
+static void
+devread(int fd, void *buf, daddr_t blk, size_t size, char *msg)
+{
+       if (lseek(fd, dbtob((off_t)blk), SEEK_SET) != dbtob((off_t)blk))
+               err(1, "%s: devread: lseek", msg);
+
+       if (read(fd, buf, size) != (ssize_t)size)
+               err(1, "%s: devread: read", msg);
+}
+
+static char sblock[SBSIZE];
+
+/*
+ * Read information about /boot's inode, then put this and filesystem
+ * parameters from the superblock into pbr_symbols.
+ */
+static int
+getbootparams(char *boot, int devfd, struct disklabel *dl)
+{
+       int             fd;
+       struct stat     statbuf, sb;
+       struct statfs   statfsbuf;
+       struct partition *pp;
+       struct fs       *fs;
+       char            *buf;
+       u_int           blk, *ap;
+       struct ufs1_dinode      *ip;
+       int             ndb;
+       int             mib[3];
+       size_t          size;
+       dev_t           dev;
+
+       /*
+        * Open 2nd-level boot program and record enough details about
+        * where it is on the filesystem represented by `devfd'
+        * (inode block, offset within that block, and various filesystem
+        * parameters essentially taken from the superblock) for biosboot
+        * to be able to load it later.
+        */
+
+       /* Make sure the (probably new) boot file is on disk. */
+       sync(); sleep(1);
+
+       if ((fd = open(boot, O_RDONLY)) < 0)
+               err(1, "open: %s", boot);
+
+       if (fstatfs(fd, &statfsbuf) != 0)
+               err(1, "statfs: %s", boot);
+
+       if (strncmp(statfsbuf.f_fstypename, "ffs", MFSNAMELEN) &&
+           strncmp(statfsbuf.f_fstypename, "ufs", MFSNAMELEN) )
+               errx(1, "%s: not on an FFS filesystem", boot);
+
+#if 0
+       if (read(fd, &eh, sizeof(eh)) != sizeof(eh))
+               errx(1, "read: %s", boot);
+
+       if (!IS_ELF(eh)) {
+               errx(1, "%s: bad magic: 0x%02x%02x%02x%02x",
+                   boot,
+                   eh.e_ident[EI_MAG0], eh.e_ident[EI_MAG1],
+                   eh.e_ident[EI_MAG2], eh.e_ident[EI_MAG3]);
+       }
+#endif
+
+       if (fsync(fd) != 0)
+               err(1, "fsync: %s", boot);
+
+       if (fstat(fd, &statbuf) != 0)
+               err(1, "fstat: %s", boot);
+
+       if (fstat(devfd, &sb) != 0)
+               err(1, "fstat: %d", devfd);
+
+       /* Check devices. */
+       mib[0] = CTL_MACHDEP;
+       mib[1] = CPU_CHR2BLK;
+       mib[2] = sb.st_rdev;
+       size = sizeof(dev);
+       if (sysctl(mib, 3, &dev, &size, NULL, 0) >= 0) {
+               if (statbuf.st_dev / MAXPARTITIONS != dev / MAXPARTITIONS)
+                       errx(1, "cross-device install");
+       }
+
+       pp = &dl->d_partitions[DISKPART(statbuf.st_dev)];
+       close(fd);
+
+       /* Read superblock. */
+       devread(devfd, sblock, DL_SECTOBLK(dl, pp->p_offset) + SBLOCK,
+           SBSIZE, "superblock");
+       fs = (struct fs *)sblock;
+
+       /* Sanity-check super-block. */
+       if (fs->fs_magic != FS_MAGIC)
+               errx(1, "Bad magic number in superblock");
+       if (fs->fs_inopb <= 0)
+               err(1, "Bad inopb=%d in superblock", fs->fs_inopb);
+
+       /* Read inode. */
+       if ((buf = malloc(fs->fs_bsize)) == NULL)
+               err(1, NULL);
+
+       blk = fsbtodb(fs, ino_to_fsba(fs, statbuf.st_ino));
+
+       devread(devfd, buf, DL_SECTOBLK(dl, pp->p_offset) + blk,
+           fs->fs_bsize, "inode");
+       ip = (struct ufs1_dinode *)(buf) + ino_to_fsbo(fs, statbuf.st_ino);
+
+       /*
+        * Have the inode.  Figure out how many filesystem blocks (not disk
+        * sectors) there are for biosboot to load.
+        */
+       ndb = howmany(ip->di_size, fs->fs_bsize);
+       if (ndb <= 0)
+               errx(1, "No blocks to load");
+
+       /*
+        * Now set the values that will need to go into biosboot
+        * (the partition boot record, a.k.a. the PBR).
+        */
+       sym_set_value(pbr_symbols, "_fs_bsize_p", (fs->fs_bsize / 16));
+       sym_set_value(pbr_symbols, "_fs_bsize_s", (fs->fs_bsize / 
+           dl->d_secsize));
+
+       /*
+        * fs_fsbtodb is the shift to convert fs_fsize to DEV_BSIZE. The
+        * ino_to_fsba() return value is the number of fs_fsize units.
+        * Calculate the shift to convert fs_fsize into physical sectors,
+        * which are added to p_offset to get the sector address BIOS
+        * will use.
+        *
+        * N.B.: ASSUMES fs_fsize is a power of 2 of d_secsize.
+        */
+       sym_set_value(pbr_symbols, "_fsbtodb",
+           ffs(fs->fs_fsize / dl->d_secsize) - 1);
+
+       sym_set_value(pbr_symbols, "_p_offset", pp->p_offset);
+       sym_set_value(pbr_symbols, "_inodeblk",
+           ino_to_fsba(fs, statbuf.st_ino));
+       ap = ip->di_db;
+       sym_set_value(pbr_symbols, "_inodedbl",
+           ((((char *)ap) - buf) + INODEOFF));
+       sym_set_value(pbr_symbols, "_nblocks", ndb);
+
+       if (verbose) {
+               fprintf(stderr, "%s is %d blocks x %d bytes\n",
+                   boot, ndb, fs->fs_bsize);
+               fprintf(stderr, "fs block shift %u; part offset %u; "
+                   "inode block %lld, offset %u\n",
+                   ffs(fs->fs_fsize / dl->d_secsize) - 1,
+                   pp->p_offset,
+                   ino_to_fsba(fs, statbuf.st_ino),
+                   (unsigned int)((((char *)ap) - buf) + INODEOFF));
+       }
+
+       return 0;
+}
+
+void
+sym_set_value(struct sym_data *sym_list, char *sym, u_int32_t value)
+{
+       struct sym_data *p;
+
+       for (p = sym_list; p->sym_name != NULL; p++) {
+               if (strcmp(p->sym_name, sym) == 0)
+                       break;
+       }
+
+       if (p->sym_name == NULL)
+               errx(1, "%s: no such symbol", sym);
+
+       p->sym_value = value;
+       p->sym_set = 1;
+}
+
+/*
+ * Write the parameters stored in sym_list into the in-memory copy of
+ * the prototype biosboot (proto), ready for it to be written to disk.
+ */
+void
+pbr_set_symbols(char *fname, char *proto, struct sym_data *sym_list)
+{
+       struct sym_data *sym;
+       struct nlist    *nl;
+       char            *vp;
+       u_int32_t       *lp;
+       u_int16_t       *wp;
+       u_int8_t        *bp;
+
+       for (sym = sym_list; sym->sym_name != NULL; sym++) {
+               if (!sym->sym_set)
+                       errx(1, "%s not set", sym->sym_name);
+
+               /* Allocate space for 2; second is null-terminator for list. */
+               nl = calloc(2, sizeof(struct nlist));
+               if (nl == NULL)
+                       err(1, NULL);
+
+               nl->n_un.n_name = sym->sym_name;
+
+               if (nlist(fname, nl) != 0)
+                       errx(1, "%s: symbol %s not found",
+                           fname, sym->sym_name);
+
+               if (nl->n_type != (N_TEXT))
+                       errx(1, "%s: %s: wrong type (%x)",
+                           fname, sym->sym_name, nl->n_type);
+
+               /* Get a pointer to where the symbol's value needs to go. */
+               vp = proto + nl->n_value;
+
+               switch (sym->sym_size) {
+               case 4:                                 /* u_int32_t */
+                       lp = (u_int32_t *) vp;
+                       *lp = sym->sym_value;
+                       break;
+               case 2:                                 /* u_int16_t */
+                       if (sym->sym_value >= 0x10000)  /* out of range */
+                               errx(1, "%s: symbol out of range (%u)",
+                                   sym->sym_name, sym->sym_value);
+                       wp = (u_int16_t *) vp;
+                       *wp = (u_int16_t) sym->sym_value;
+                       break;
+               case 1:                                 /* u_int16_t */
+                       if (sym->sym_value >= 0x100)    /* out of range */
+                               errx(1, "%s: symbol out of range (%u)",
+                                   sym->sym_name, sym->sym_value);
+                       bp = (u_int8_t *) vp;
+                       *bp = (u_int8_t) sym->sym_value;
+                       break;
+               default:
+                       errx(1, "%s: bad symbol size %d",
+                           sym->sym_name, sym->sym_size);
+                       /* NOTREACHED */
+               }
+
+               free(nl);
+       }
+}
diff --git a/usr.sbin/installboot/i386/i386_installboot.h b/usr.sbin/installboot/i386/i386_installboot.h
new file mode 100644 (file)
index 0000000..ac61e35
--- /dev/null
@@ -0,0 +1,54 @@
+/*     $OpenBSD        */
+/*
+ * Copyright (c) 2011 Joel Sing <jsing@openbsd.org>
+ * Copyright (c) 2010 Otto Moerbeek <otto@openbsd.org>
+ * Copyright (c) 2003 Tom Cosgrove <tom.cosgrove@arches-consulting.com>
+ * Copyright (c) 1997 Michael Shalayeff
+ * Copyright (c) 1994 Paul Kranenburg
+ * 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 by Paul Kranenburg.
+ * 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.
+ */
+
+#define INODESEG        0x07e0  /* where we will put /boot's inode's block */
+#define BOOTSEG         0x07c0  /* biosboot loaded here */
+
+#define INODEOFF  ((INODESEG-BOOTSEG) << 4)
+
+#define SR_FS_BLOCKSIZE (16 * 1024)
+
+struct  sym_data {
+       char            *sym_name;              /* Must be initialised */
+       int             sym_size;               /* And this one */
+       int             sym_set;                /* Rest set at runtime */
+       u_int32_t       sym_value;
+};
+
+extern struct sym_data pbr_symbols[];
+
+void   pbr_set_symbols(char *, char *, struct sym_data *);
+void   sym_set_value(struct sym_data *, char *, u_int32_t);
+void   write_bootblocks(int, char *, struct disklabel *);
diff --git a/usr.sbin/installboot/i386/i386_softraid.c b/usr.sbin/installboot/i386/i386_softraid.c
new file mode 100644 (file)
index 0000000..75756dc
--- /dev/null
@@ -0,0 +1,195 @@
+/*     $OpenBSD: i386_softraid.c,v 1.1 2013/12/27 13:52:40 jsing Exp $ */
+/*
+ * Copyright (c) 2012 Joel Sing <jsing@openbsd.org>
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include <sys/param.h>
+#include <sys/disklabel.h>
+#include <sys/dkio.h>
+#include <sys/ioctl.h>
+#include <sys/stat.h>
+
+#include <dev/biovar.h>
+#include <dev/softraidvar.h>
+#include <ufs/ufs/dinode.h>
+
+#include <err.h>
+#include <fcntl.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <util.h>
+
+#include "installboot.h"
+#include "i386_installboot.h"
+
+void   sr_install_bootblk(int, int, int);
+void   sr_install_bootldr(int, char *);
+
+void
+sr_install_bootblk(int devfd, int vol, int disk)
+{
+       struct bioc_disk bd;
+       struct disklabel dl;
+       struct partition *pp;
+       uint32_t poffset;
+       char *dev;
+       char part;
+       int diskfd;
+       int rv;
+
+       /* Get device name for this disk/chunk. */
+       memset(&bd, 0, sizeof(bd));
+       bd.bd_volid = vol;
+       bd.bd_diskid = disk;
+       rv = ioctl(devfd, BIOCDISK, &bd);
+       if (rv == -1)
+               err(1, "BIOCDISK");
+
+       /* Check disk status. */
+       if (bd.bd_status != BIOC_SDONLINE && bd.bd_status != BIOC_SDREBUILD) {
+               fprintf(stderr, "softraid chunk %u not online - skipping...\n",
+                   disk);
+               return; 
+       }
+
+       if (strlen(bd.bd_vendor) < 1)
+               errx(1, "invalid disk name");
+       part = bd.bd_vendor[strlen(bd.bd_vendor) - 1];
+       if (part < 'a' || part >= 'a' + MAXPARTITIONS)
+               errx(1, "invalid partition %c\n", part);
+       bd.bd_vendor[strlen(bd.bd_vendor) - 1] = '\0';
+
+       /* Open this device and check its disklabel. */
+       if ((diskfd = opendev(bd.bd_vendor, (nowrite? O_RDONLY:O_RDWR),
+           OPENDEV_PART, &dev)) < 0)
+               err(1, "open: %s", dev);
+
+       /* Get and check disklabel. */
+       if (ioctl(diskfd, DIOCGDINFO, &dl) != 0)
+               err(1, "disklabel: %s", dev);
+       if (dl.d_magic != DISKMAGIC)
+               err(1, "bad disklabel magic=0x%08x", dl.d_magic);
+
+       /* Warn on unknown disklabel types. */
+       if (dl.d_type == 0)
+               warnx("disklabel type unknown");
+
+       /* Determine poffset and set symbol value. */
+       pp = &dl.d_partitions[part - 'a'];
+       if (pp->p_offseth != 0)
+               errx(1, "partition offset too high");
+       poffset = pp->p_offset;                 /* Offset of RAID partition. */
+       poffset += SR_BOOT_LOADER_OFFSET;       /* SR boot loader area. */
+       sym_set_value(pbr_symbols, "_p_offset", poffset);
+
+       if (verbose)
+               fprintf(stderr, "%s%c: installing boot blocks on %s, "
+                   "part offset %u\n", bd.bd_vendor, part, dev, poffset);
+
+       /* Write boot blocks to device. */
+       write_bootblocks(diskfd, dev, &dl);
+
+       close(diskfd);
+}
+
+void
+sr_install_bootldr(int devfd, char *dev)
+{
+       struct bioc_installboot bb;
+       struct stat sb;
+       struct ufs1_dinode *ino_p;
+       uint32_t bootsize, inodeblk, inodedbl;
+       uint16_t bsize = SR_FS_BLOCKSIZE;
+       uint16_t nblocks;
+       uint8_t bshift = 5;             /* fragsize == blocksize */
+       int fd, i, rv;
+       u_char *p;
+
+       /*
+        * Install boot loader into softraid boot loader storage area.
+        *
+        * In order to allow us to reuse the existing biosboot we construct
+        * a fake FFS filesystem with a single inode, which points to the
+        * boot loader.
+        */
+
+       nblocks = howmany(SR_BOOT_LOADER_SIZE, SR_FS_BLOCKSIZE / DEV_BSIZE);
+       inodeblk = nblocks - 1;
+       bootsize = nblocks * SR_FS_BLOCKSIZE;
+
+       p = malloc(bootsize);
+       if (p == NULL)
+               err(1, NULL);
+       
+       memset(p, 0, bootsize);
+       fd = open(stage2, O_RDONLY, 0);
+       if (fd == -1)
+               err(1, NULL);
+
+       if (fstat(fd, &sb) == -1)
+               err(1, NULL);
+
+       nblocks = howmany(sb.st_blocks, SR_FS_BLOCKSIZE / DEV_BSIZE);
+       if (sb.st_blocks * S_BLKSIZE > bootsize -
+           (int)(sizeof(struct ufs1_dinode)))
+               errx(1, "boot code will not fit");
+
+       /* We only need to fill the direct block array. */
+       ino_p = (struct ufs1_dinode *)&p[bootsize - sizeof(struct ufs1_dinode)];
+
+       ino_p->di_mode = sb.st_mode;
+       ino_p->di_nlink = 1;
+       ino_p->di_inumber = 0xfeebfaab;
+       ino_p->di_size = read(fd, p, sb.st_blocks * S_BLKSIZE);
+       ino_p->di_blocks = nblocks;
+       for (i = 0; i < nblocks; i++)
+               ino_p->di_db[i] = i;
+
+       inodedbl = ((u_char*)&ino_p->di_db[0] -
+           &p[bootsize - SR_FS_BLOCKSIZE]) + INODEOFF;
+
+       bb.bb_bootldr = p;
+       bb.bb_bootldr_size = bootsize;
+       bb.bb_bootblk = "XXX";
+       bb.bb_bootblk_size = sizeof("XXX");
+       strncpy(bb.bb_dev, dev, sizeof(bb.bb_dev));
+       if (!nowrite) {
+               if (verbose)
+                       fprintf(stderr, "%s: installing boot loader on "
+                           "softraid volume\n", dev);
+               rv = ioctl(devfd, BIOCINSTALLBOOT, &bb);
+               if (rv != 0)
+                       errx(1, "softraid installboot failed");
+       }
+
+       /*
+        * Set the values that will need to go into biosboot
+        * (the partition boot record, a.k.a. the PBR).
+        */
+       sym_set_value(pbr_symbols, "_fs_bsize_p", (bsize / 16));
+       sym_set_value(pbr_symbols, "_fs_bsize_s", (bsize / 512));
+       sym_set_value(pbr_symbols, "_fsbtodb", bshift);
+       sym_set_value(pbr_symbols, "_inodeblk", inodeblk);
+       sym_set_value(pbr_symbols, "_inodedbl", inodedbl);
+       sym_set_value(pbr_symbols, "_nblocks", nblocks);
+
+       if (verbose)
+               fprintf(stderr, "%s is %d blocks x %d bytes\n",
+                   stage2, nblocks, bsize);
+
+       close(fd);
+}
diff --git a/usr.sbin/installboot/installboot.c b/usr.sbin/installboot/installboot.c
new file mode 100644 (file)
index 0000000..48148da
--- /dev/null
@@ -0,0 +1,103 @@
+/*     $OpenBSD: installboot.c,v 1.1 2013/12/27 13:52:40 jsing Exp $   */
+
+/*
+ * Copyright (c) 2012, 2013 Joel Sing <jsing@openbsd.org>
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include <err.h>
+#include <fcntl.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <util.h>
+
+#include "installboot.h"
+
+int    nowrite;
+int    stages;
+int    verbose;
+
+char   *stage1;
+char   *stage2;
+
+static __dead void
+usage(void)
+{
+       extern char *__progname;
+
+       fprintf(stderr, "usage: %s [-nv] disk [stage1%s]\n",
+           __progname, (stages >= 2) ? " [stage2]" : "");
+
+       exit(1);
+}
+
+int
+main(int argc, char **argv)
+{
+       char *dev, *realdev;
+       int devfd;
+       char opt;
+
+       md_init();
+
+       while ((opt = getopt(argc, argv, "nv")) != -1) {
+               switch (opt) {
+               case 'n':
+                       nowrite = 1;
+                       break;
+               case 'v':
+                       verbose = 1;
+                       break;
+               default:
+                       usage();
+               }
+       }
+
+       argc -= optind;
+       argv += optind;
+
+       if (argc < 1 || argc > stages + 1)
+               usage();
+
+       dev = argv[0];
+       if (argc > 1)
+               stage1 = argv[1];
+       if (argc > 2)
+               stage2 = argv[2];
+
+       if ((devfd = opendev(dev, (nowrite ? O_RDONLY : O_RDWR), OPENDEV_PART,
+           &realdev)) < 0)
+               err(1, "open: %s", realdev);
+
+        if (verbose) {
+               fprintf(stderr, "%s bootstrap on %s\n",
+                   (nowrite ? "would install" : "installing"), realdev);
+               if (stage1)
+                       fprintf(stderr, "using first-stage %s", stage1);
+               if (stage2)
+                       fprintf(stderr, ", second-stage %s", stage2);
+               fprintf(stderr, "\n");
+       } 
+
+       md_loadboot();
+
+#ifdef SOFTRAID
+       sr_installboot(devfd, dev);
+#else
+       md_installboot(devfd, realdev);
+#endif
+
+       return 0;
+}
diff --git a/usr.sbin/installboot/installboot.h b/usr.sbin/installboot/installboot.h
new file mode 100644 (file)
index 0000000..ec0f56d
--- /dev/null
@@ -0,0 +1,33 @@
+/*     $OpenBSD: installboot.h,v 1.1 2013/12/27 13:52:40 jsing Exp $   */
+/*
+ * Copyright (c) 2012, 2013 Joel Sing <jsing@openbsd.org>
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+extern int nowrite;
+extern int stages;
+extern int verbose;
+
+extern char *stage1;
+extern char *stage2;
+
+void   md_init(void);
+void   md_loadboot(void);
+void   md_installboot(int, char *);
+
+#ifdef SOFTRAID
+void   sr_installboot(int, char *);
+void   sr_install_bootblk(int, int, int);
+void   sr_install_bootldr(int, char *);
+#endif
diff --git a/usr.sbin/installboot/softraid.c b/usr.sbin/installboot/softraid.c
new file mode 100644 (file)
index 0000000..e155cbd
--- /dev/null
@@ -0,0 +1,97 @@
+/*     $OpenBSD: softraid.c,v 1.1 2013/12/27 13:52:40 jsing Exp $      */
+/*
+ * Copyright (c) 2012 Joel Sing <jsing@openbsd.org>
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include <sys/param.h>
+#include <sys/dkio.h>
+#include <sys/ioctl.h>
+
+#include <dev/biovar.h>
+
+#include <err.h>
+#include <stdio.h>
+#include <string.h>
+
+#include "installboot.h"
+
+static int sr_volume(int, char *, int *, int *);
+
+void
+sr_installboot(int devfd, char *dev)
+{
+       int     vol = -1, ndisks = 0, disk;
+
+       /* Use the normal process if this is not a softraid volume. */
+       if (!sr_volume(devfd, dev, &vol, &ndisks)) {
+               md_installboot(devfd, dev);
+               return;
+       }
+       
+       /* Install boot loader in softraid volume. */
+       sr_install_bootldr(devfd, dev);
+
+       /* Install boot block on each disk that is part of this volume. */
+       for (disk = 0; disk < ndisks; disk++)
+               sr_install_bootblk(devfd, vol, disk);
+}
+
+int
+sr_volume(int devfd, char *dev, int *vol, int *disks)
+{
+       struct  bioc_inq bi;
+       struct  bioc_vol bv;
+       int     rv, i;
+
+       /*
+        * Determine if the given device is a softraid volume.
+        */
+
+       /* Get volume information. */
+       memset(&bi, 0, sizeof(bi));
+       rv = ioctl(devfd, BIOCINQ, &bi);
+       if (rv == -1)
+               return 0;
+
+       /* XXX - softraid volumes will always have a "softraid0" controller. */
+       if (strncmp(bi.bi_dev, "softraid0", sizeof("softraid0")))
+               return 0;
+
+       /*
+        * XXX - this only works with the real disk name (e.g. sd0) - this
+        * should be extracted from the device name, or better yet, fixed in
+        * the softraid ioctl.
+        */
+       /* Locate specific softraid volume. */
+       for (i = 0; i < bi.bi_novol; i++) {
+               memset(&bv, 0, sizeof(bv));
+               bv.bv_volid = i;
+               rv = ioctl(devfd, BIOCVOL, &bv);
+               if (rv == -1)
+                       err(1, "BIOCVOL");
+
+               if (strncmp(dev, bv.bv_dev, sizeof(bv.bv_dev)) == 0) {
+                       *vol = i;
+                       *disks = bv.bv_nodisk;
+                       break;
+               }
+       }
+
+       if (verbose)
+               fprintf(stderr, "%s: softraid volume with %i disk(s)\n",
+                   dev, *disks);
+
+       return 1;
+}