diff options
author | Joel Sing <jsing@cvs.openbsd.org> | 2013-12-27 13:52:41 +0000 |
---|---|---|
committer | Joel Sing <jsing@cvs.openbsd.org> | 2013-12-27 13:52:41 +0000 |
commit | d0c198ee56e8bcc7eaf65c1620e621b604fee7e8 (patch) | |
tree | d4d4d95e96b91b595f682d37a0469c7bad17ac7e | |
parent | d86bf5da7e7bdfb9d7a617df91e84cfe97bb5430 (diff) |
Initial version of a unified installboot(8) that lives outside of
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.
-rw-r--r-- | usr.sbin/installboot/Makefile | 23 | ||||
-rw-r--r-- | usr.sbin/installboot/i386/Makefile.inc | 10 | ||||
-rw-r--r-- | usr.sbin/installboot/i386/i386_installboot.c | 567 | ||||
-rw-r--r-- | usr.sbin/installboot/i386/i386_installboot.h | 54 | ||||
-rw-r--r-- | usr.sbin/installboot/i386/i386_softraid.c | 195 | ||||
-rw-r--r-- | usr.sbin/installboot/installboot.c | 103 | ||||
-rw-r--r-- | usr.sbin/installboot/installboot.h | 33 | ||||
-rw-r--r-- | usr.sbin/installboot/softraid.c | 97 |
8 files changed, 1082 insertions, 0 deletions
diff --git a/usr.sbin/installboot/Makefile b/usr.sbin/installboot/Makefile new file mode 100644 index 00000000000..a24d18db70b --- /dev/null +++ b/usr.sbin/installboot/Makefile @@ -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 index 00000000000..da1acc8f7c1 --- /dev/null +++ b/usr.sbin/installboot/i386/Makefile.inc @@ -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 index 00000000000..6de183ecd1e --- /dev/null +++ b/usr.sbin/installboot/i386/i386_installboot.c @@ -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 index 00000000000..ac61e355fb3 --- /dev/null +++ b/usr.sbin/installboot/i386/i386_installboot.h @@ -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 index 00000000000..75756dc0216 --- /dev/null +++ b/usr.sbin/installboot/i386/i386_softraid.c @@ -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 index 00000000000..48148da9464 --- /dev/null +++ b/usr.sbin/installboot/installboot.c @@ -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 index 00000000000..ec0f56d3555 --- /dev/null +++ b/usr.sbin/installboot/installboot.h @@ -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 index 00000000000..e155cbd1c04 --- /dev/null +++ b/usr.sbin/installboot/softraid.c @@ -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; +} |