diff options
Diffstat (limited to 'sys/arch/i386/stand/installboot')
-rw-r--r-- | sys/arch/i386/stand/installboot/Makefile | 10 | ||||
-rw-r--r-- | sys/arch/i386/stand/installboot/installboot.8 | 58 | ||||
-rw-r--r-- | sys/arch/i386/stand/installboot/installboot.c | 436 |
3 files changed, 504 insertions, 0 deletions
diff --git a/sys/arch/i386/stand/installboot/Makefile b/sys/arch/i386/stand/installboot/Makefile new file mode 100644 index 00000000000..ec82f45956c --- /dev/null +++ b/sys/arch/i386/stand/installboot/Makefile @@ -0,0 +1,10 @@ +# $OpenBSD: Makefile,v 1.2 1997/03/31 03:12:06 weingart Exp $ + +PROG= installboot +CFLAGS+=-DDEBUG -g -Wall +MAN= installboot.8 + +# Need this to work in the miniroot +LDSTATIC= -static + +.include <bsd.prog.mk> diff --git a/sys/arch/i386/stand/installboot/installboot.8 b/sys/arch/i386/stand/installboot/installboot.8 new file mode 100644 index 00000000000..b52eb6587a4 --- /dev/null +++ b/sys/arch/i386/stand/installboot/installboot.8 @@ -0,0 +1,58 @@ +.\" $OpenBSD: installboot.8,v 1.2 1997/03/31 03:12:06 weingart Exp $ +.\" +.Dd 31 May 1995 +.Dt INSTALLBOOT 8 +.Os +.Sh NAME +.Nm installboot +.Nd install a bootstrap on a UFS disk +.Sh SYNOPSIS +.Nm installboot +.Fl n | Fl v +.Ar ufsboot +.Ar bootxx +.Ar rawdev +.Sh DESCRIPTION +.Nm installboot +is used to install a "first-stage" boot program into the boot area +of a UFS disk partition, and initialize the table of block numbers the +.Ar bootxx +program uses to load the second-stage boot program. +.Pp +The options are as follows: +.Bl -tag -width flag +.It Fl n +Do not actually write anything on the disk. +.It Fl v +Be verbose, printing out the block numbers that +.Ar bootxx +will use to load +.Ar ufsboot . +.El +.Pp +The arguments are: +.Bl -tag -width ufsboot +.It Ar ufsboot +the name of the second-stage boot program in the file system +where the first-stage boot program is to be installed. +.It Ar bootxx +the name of the prototype file for the first stage boot program. +.It Ar rawdev +the name of the raw device in which the first-stage boot program +is to be installed. This should correspond to the block device +on which the file system containing +.Ar ufsboot +is mounted. +.El +.Sh BUGS +.Nm installboot +requires simultaneous access to the mounted file system and +the raw device, but that is not allowed with the kernel +.Nm securelevel +variable set to a value greater than zero (the default), so +.Nm installboot +only works in single-user mode (or insecure mode - see +.Xr init 8 ). +.Sh "SEE ALSO" +.Xr disklabel 8 , +.Xr init 8 diff --git a/sys/arch/i386/stand/installboot/installboot.c b/sys/arch/i386/stand/installboot/installboot.c new file mode 100644 index 00000000000..8b6d25a8d8e --- /dev/null +++ b/sys/arch/i386/stand/installboot/installboot.c @@ -0,0 +1,436 @@ +/* $OpenBSD: installboot.c,v 1.2 1997/03/31 03:12:07 weingart Exp $ */ +/* $NetBSD: installboot.c,v 1.5 1995/11/17 23:23:50 gwr Exp $ */ + +/* + * 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/mount.h> +#include <sys/time.h> +#include <sys/stat.h> +#include <sys/disklabel.h> +#include <sys/dkio.h> +#include <sys/ioctl.h> +#include <ufs/ufs/dinode.h> +#include <ufs/ufs/dir.h> +#include <ufs/ffs/fs.h> +#include <err.h> +#include <a.out.h> +#include <fcntl.h> +#include <nlist.h> +#include <stdlib.h> +#include <stdio.h> +#include <string.h> +#include <unistd.h> + +int verbose, nowrite; +char *boot, *proto, *dev; +struct nlist nl[] = { +#define X_BLOCK_COUNT 0 + {{"_block_count"}}, +#define X_BLOCK_TABLE 1 + {{"_block_table"}}, + {{NULL}} +}; + +u_int8_t *block_count_p; /* block count var. in prototype image */ +u_int8_t *block_table_p; /* block number array in prototype image */ +int maxblocknum; /* size of this array */ + + +char *loadprotoblocks __P((char *, long *)); +int loadblocknums __P((char *, int)); +static void devread __P((int, void *, daddr_t, size_t, char *)); +static void usage __P((void)); +static int record_block + __P((u_int8_t *, daddr_t, u_int, struct disklabel *)); + +static void +usage() +{ + fprintf(stderr, + "usage: installboot [-n] [-v] <boot> <proto> <device>\n"); + exit(1); +} + +int +main(argc, argv) + int argc; + char *argv[]; +{ + int c; + int devfd; + char *protostore; + long protosize; + + while ((c = getopt(argc, argv, "vn")) != EOF) { + switch (c) { + case 'n': + /* Do not actually write the bootblock to disk */ + nowrite = 1; + break; + case 'v': + /* Chat */ + verbose = 1; + break; + default: + usage(); + } + } + + if (argc - optind < 3) { + usage(); + } + + boot = argv[optind]; + proto = argv[optind + 1]; + dev = argv[optind + 2]; + + if (verbose) { + printf("boot: %s\n", boot); + printf("proto: %s\n", proto); + printf("device: %s\n", dev); + } + + /* Load proto blocks into core */ + if ((protostore = loadprotoblocks(proto, &protosize)) == NULL) + exit(1); + + /* XXX - Paranoia: Make sure size is aligned! */ + if (protosize & (DEV_BSIZE - 1)) + err(1, "proto bootblock bad size=%ld", protosize); + + /* Open and check raw disk device */ + if ((devfd = open(dev, O_RDONLY, 0)) < 0) + err(1, "open: %s", dev); + + /* Extract and load block numbers */ + if (loadblocknums(boot, devfd) != 0) + exit(1); + + (void)close(devfd); + + if (nowrite) + return 0; + + /* Write patched proto bootblocks into the superblock */ + if (protosize > SBSIZE - DEV_BSIZE) + errx(1, "proto bootblocks too big"); + + if ((devfd = open(dev, O_RDWR, 0)) < 0) + err(1, "open: %s", dev); + + /* Sync filesystems (to clean in-memory superblock?) */ + sync(); + + if (write(devfd, protostore, protosize) != protosize) + err(1, "write bootstrap"); + (void)close(devfd); + return 0; +} + +char * +loadprotoblocks(fname, size) + char *fname; + long *size; +{ + int fd; + size_t tdsize; /* text+data size */ + char *bp; + struct nlist *nlp; + struct exec eh; + + fd = -1; + bp = NULL; + + /* Locate block number array in proto file */ + if (nlist(fname, nl) != 0) { + warnx("nlist: %s: symbols not found", fname); + return NULL; + } + /* Validate symbol types (global data). */ + for (nlp = nl; nlp->n_un.n_name; nlp++) { + if (nlp->n_type != (N_TEXT | N_EXT)) { + warnx("nlist: %s: wrong type", nlp->n_un.n_name); + return NULL; + } + } + + if ((fd = open(fname, O_RDONLY)) < 0) { + warn("open: %s", fname); + return NULL; + } + if (read(fd, &eh, sizeof(eh)) != sizeof(eh)) { + warn("read: %s", fname); + goto bad; + } + if (N_GETMAGIC(eh) != OMAGIC) { + warn("bad magic: 0x%lx", eh.a_midmag); + goto bad; + } + /* + * 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. + */ + tdsize = eh.a_text + eh.a_data; + + /* + * 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) { + warnx("malloc: %s: no memory", fname); + goto bad; + } + /* Read the rest of the file. */ + if (read(fd, bp, tdsize) != tdsize) { + warn("read: %s", fname); + goto bad; + } + + *size = tdsize; /* not aligned to DEV_BSIZE */ + + /* Calculate the symbols' locations within the proto file */ + block_count_p = (u_int8_t *) (bp + nl[X_BLOCK_COUNT].n_value); + block_table_p = (u_int8_t *) (bp + nl[X_BLOCK_TABLE].n_value); + maxblocknum = *block_count_p; + + if (verbose) { + printf("%s: entry point %#lx\n", fname, eh.a_entry); + printf("proto bootblock size %ld\n", *size); + printf("room for %d filesystem blocks at %#lx\n", + maxblocknum, nl[X_BLOCK_TABLE].n_value); + } + + close(fd); + return bp; + + bad: + if (bp) + free(bp); + if (fd >= 0) + close(fd); + return NULL; +} + +static void +devread(fd, buf, blk, size, msg) + int fd; + void *buf; + daddr_t blk; + size_t size; + char *msg; +{ + if (lseek(fd, dbtob(blk), SEEK_SET) != dbtob(blk)) + err(1, "%s: devread: lseek", msg); + + if (read(fd, buf, size) != size) + err(1, "%s: devread: read", msg); +} + +static char sblock[SBSIZE]; + +int +loadblocknums(boot, devfd) +char *boot; +int devfd; +{ + int i, fd; + struct stat statbuf; + struct statfs statfsbuf; + struct disklabel dl; + struct fs *fs; + char *buf; + daddr_t blk, *ap; + struct dinode *ip; + int ndb; + u_int8_t *bt; + struct exec eh; + + /* + * Open 2nd-level boot program and record the block numbers + * it occupies on the filesystem represented by `devfd'. + */ + + /* Make sure the (probably new) boot file is on disk. */ + sync(); sleep(1); + + if (ioctl(devfd, DIOCGDINFO, &dl) != 0) + err(1, "disklabel: %s", dev); + + /* check disklabel */ + if (dl.d_magic != DISKMAGIC) + err(1, "bad disklabel magic=%0x8x", dl.d_magic); + + 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: must be on an FFS filesystem", boot); + } + + if (read(fd, &eh, sizeof(eh)) != sizeof(eh)) { + errx(1, "read: %s", boot); + } + + if (N_GETMAGIC(eh) != ZMAGIC) { + errx(1, "%s: bad magic: 0x%lx", boot, eh.a_midmag); + } + + if (fsync(fd) != 0) + err(1, "fsync: %s", boot); + + if (fstat(fd, &statbuf) != 0) + err(1, "fstat: %s", boot); + + close(fd); + + /* Read superblock */ + devread(devfd, sblock, 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) + errx(1, "No memory for filesystem block"); + + blk = fsbtodb(fs, ino_to_fsba(fs, statbuf.st_ino)); + devread(devfd, buf, blk, fs->fs_bsize, "inode"); + ip = (struct dinode *)(buf) + ino_to_fsbo(fs, statbuf.st_ino); + + /* + * Have the inode. Figure out how many blocks we need. + */ + ndb = howmany(ip->di_size, fs->fs_bsize); + if (ndb > maxblocknum) + errx(1, "Too many blocks"); + if (verbose) + printf("Will load %d blocks of size %d each.\n", + ndb, fs->fs_bsize); + + /* + * Get the block numbers; we don't handle fragments + */ + ap = ip->di_db; + bt = block_table_p; + for (i = 0; i < NDADDR && *ap && ndb; i++, ap++, ndb--) + bt += record_block(bt, fsbtodb(fs, *ap), + fs->fs_bsize / 512, &dl); + if (ndb != 0) { + + /* + * Just one level of indirections; there isn't much room + * for more in the 1st-level bootblocks anyway. + */ + blk = fsbtodb(fs, ip->di_ib[0]); + devread(devfd, buf, blk, fs->fs_bsize, "indirect block"); + ap = (daddr_t *)buf; + for (; i < NINDIR(fs) && *ap && ndb; i++, ap++, ndb--) + bt += record_block(bt, fsbtodb(fs, *ap), + fs->fs_bsize / 512, &dl); + } + + /* write out remaining piece */ + bt += record_block(bt, 0, 0, &dl); + /* and again */ + bt += record_block(bt, 0, 0, &dl); + *block_count_p = (bt - block_table_p) / 4; + + if (verbose) + printf("%s: %d entries total\n", boot, *block_count_p); + + return 0; +} + +static int +record_block(bt, blk, bs, dl) + u_int8_t *bt; + daddr_t blk; + u_int bs; + struct disklabel *dl; +{ + static u_int ss = 0, l = 0, i = 0; /* start and len of group */ + int ret = 0; + + if (ss == 0) { /* very beginning */ + ss = blk; + l = bs; + return 0; + } else if (l == 0) + return 0; + + /* record on track boundary or non-contig blocks or last call */ + if ((ss + l) != blk || + (ss % dl->d_nsectors + l) >= dl->d_nsectors) { + register u_int c = ss / dl->d_secpercyl, + s = ss % dl->d_nsectors + 1; + + /* nsectors */ + if ((ss % dl->d_nsectors + l) >= dl->d_nsectors) + bt[3] = dl->d_nsectors - s + 1; + else + bt[3] = l; /* non-contig or last block piece */ + + bt[2] = (ss % dl->d_secpercyl) / dl->d_nsectors; /* head */ + *(u_int16_t *)bt = (s & 0x3f) | /* sect, cyl */ + ((c & 0xff) << 8) | ((c & 0x300) >> 2); + + if (verbose) + printf("%2d: %2d @(%d %d %d) (%d-%d)\n", + i, bt[3], c, bt[2], s, ss, ss + bt[3] - 1); + + if ((ss % dl->d_nsectors + l) >= dl->d_nsectors) { + ss += bt[3]; + l -= bt[3]; + l += bs; + } else { + ss = blk; + l = bs; + } + + i++; + ret = 4; + } else { + l += bs; + } + + return ret; +} |