summaryrefslogtreecommitdiffstats
path: root/sys/boot/i386/installboot/installboot.c
diff options
context:
space:
mode:
Diffstat (limited to 'sys/boot/i386/installboot/installboot.c')
-rw-r--r--sys/boot/i386/installboot/installboot.c433
1 files changed, 433 insertions, 0 deletions
diff --git a/sys/boot/i386/installboot/installboot.c b/sys/boot/i386/installboot/installboot.c
new file mode 100644
index 0000000..a5bfd3b
--- /dev/null
+++ b/sys/boot/i386/installboot/installboot.c
@@ -0,0 +1,433 @@
+/* $NetBSD: installboot.c,v 1.5 1997/11/01 06:49:50 lukem Exp $ */
+
+/*
+ * Copyright (c) 1994 Paul Kranenburg
+ * All rights reserved.
+ * Copyright (c) 1996, 1997
+ * Matthias Drochner. All rights reserved.
+ * Copyright (c) 1996, 1997
+ * Perry E. Metzger. 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.
+ * This product includes software developed for the NetBSD Project
+ * by Matthias Drochner.
+ * This product includes software developed for the NetBSD Project
+ * by Perry E. Metzger.
+ * 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 <ufs/ufs/dinode.h>
+#include <ufs/ufs/dir.h>
+#include <ufs/ffs/fs.h>
+#include <sys/errno.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>
+#include <sys/ioctl.h>
+
+#include "installboot.h"
+
+#include "bbinfo.h"
+
+#define DEFBBLKNAME "boot"
+
+struct fraglist *fraglist;
+
+struct nlist nl[] = {
+#define X_fraglist 0
+ {{"_fraglist"}},
+ {{NULL}}
+};
+
+int verbose = 0;
+
+char *
+loadprotoblocks(fname, size)
+ char *fname;
+ long *size;
+{
+ int fd;
+ size_t tdsize; /* text+data size */
+ size_t bbsize; /* boot block size (block aligned) */
+ 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 text!). */
+ 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 need only text and data.
+ */
+ tdsize = eh.a_text + eh.a_data;
+ bbsize = roundup(tdsize, DEV_BSIZE);
+
+ if ((bp = calloc(bbsize, 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 = bbsize; /* aligned to DEV_BSIZE */
+
+ fraglist = (struct fraglist *) (bp + nl[X_fraglist].n_value);
+
+ if (fraglist->magic != FRAGLISTMAGIC) {
+ warnx("invalid bootblock version");
+ goto bad;
+ }
+ if (verbose) {
+ fprintf(stderr, "%s: entry point %#lx\n", fname, eh.a_entry);
+ fprintf(stderr, "proto bootblock size %ld\n", *size);
+ fprintf(stderr, "room for %d filesystem blocks at %#lx\n",
+ fraglist->maxentries, nl[X_fraglist].n_value);
+ }
+ close(fd);
+ return bp;
+
+bad:
+ if (bp)
+ free(bp);
+ if (fd >= 0)
+ close(fd);
+ return NULL;
+}
+
+static int
+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)) {
+ warn("%s: devread: lseek", msg);
+ return (1);
+ }
+ if (read(fd, buf, size) != size) {
+ warn("%s: devread: read", msg);
+ return (1);
+ }
+ return (0);
+}
+
+/* add file system blocks to fraglist */
+static int
+add_fsblk(fs, blk, blcnt)
+ struct fs *fs;
+ daddr_t blk;
+ int blcnt;
+{
+ int nblk;
+
+ /* convert to disk blocks */
+ blk = fsbtodb(fs, blk);
+ nblk = fs->fs_bsize / DEV_BSIZE;
+ if (nblk > blcnt)
+ nblk = blcnt;
+
+ if (verbose)
+ fprintf(stderr, "dblk: %d, num: %d\n", blk, nblk);
+
+ /* start new entry or append to previous? */
+ if (!fraglist->numentries ||
+ (fraglist->entries[fraglist->numentries - 1].offset
+ + fraglist->entries[fraglist->numentries - 1].num != blk)) {
+
+ /* need new entry */
+ if (fraglist->numentries > fraglist->maxentries - 1) {
+ errx(1, "not enough fragment space in bootcode\n");
+ return(-1);
+ }
+
+ fraglist->entries[fraglist->numentries].offset = blk;
+ fraglist->entries[fraglist->numentries++].num = 0;
+ }
+ fraglist->entries[fraglist->numentries - 1].num += nblk;
+
+ return (blcnt - nblk);
+}
+
+static char sblock[SBSIZE];
+
+int
+loadblocknums(diskdev, inode)
+ char *diskdev;
+ ino_t inode;
+{
+ int devfd = -1;
+ struct fs *fs;
+ char *buf = 0;
+ daddr_t blk, *ap;
+ struct dinode *ip;
+ int i, ndb;
+ int allok = 0;
+
+ devfd = open(diskdev, O_RDONLY, 0);
+ if (devfd < 0) {
+ warn("open raw partition");
+ return (1);
+ }
+ /* Read superblock */
+ if (devread(devfd, sblock, SBLOCK, SBSIZE, "superblock"))
+ goto out;
+ fs = (struct fs *) sblock;
+
+ if (fs->fs_magic != FS_MAGIC) {
+ warnx("invalid super block");
+ goto out;
+ }
+ /* Read inode */
+ if ((buf = malloc(fs->fs_bsize)) == NULL) {
+ warnx("No memory for filesystem block");
+ goto out;
+ }
+ blk = fsbtodb(fs, ino_to_fsba(fs, inode));
+ if (devread(devfd, buf, blk, fs->fs_bsize, "inode"))
+ goto out;
+ ip = (struct dinode *) (buf) + ino_to_fsbo(fs, inode);
+
+ /*
+ * Have the inode. Figure out how many blocks we need.
+ */
+ ndb = ip->di_size / DEV_BSIZE; /* size is rounded! */
+
+ if (verbose)
+ fprintf(stderr, "Will load %d blocks.\n", ndb);
+
+ /*
+ * Get the block numbers, first direct blocks
+ */
+ ap = ip->di_db;
+ for (i = 0; i < NDADDR && *ap && ndb > 0; i++, ap++)
+ ndb = add_fsblk(fs, *ap, ndb);
+
+ 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]);
+ if (devread(devfd, buf, blk, fs->fs_bsize, "indirect block"))
+ goto out;
+ ap = (daddr_t *) buf;
+ for (; i < NINDIR(fs) && *ap && ndb > 0; i++, ap++) {
+ ndb = add_fsblk(fs, *ap, ndb);
+ }
+ }
+
+ if (!ndb)
+ allok = 1;
+ else {
+ if (ndb > 0)
+ warnx("too many fs blocks");
+ /* else, ie ndb < 0, add_fsblk returned error */
+ goto out;
+ }
+
+out:
+ if (buf)
+ free(buf);
+ if (devfd >= 0)
+ close(devfd);
+ return (!allok);
+}
+
+static void
+usage()
+{
+ fprintf(stderr,
+ "usage: installboot [-n] [-v] [-f] <boot> <device>\n");
+ exit(1);
+}
+
+int
+main(argc, argv)
+ int argc;
+ char *argv[];
+{
+ char c, *bp = 0;
+ long size;
+ ino_t inode = (ino_t) -1;
+ int devfd = -1;
+ struct disklabel dl;
+ int bsdoffs;
+ int i, res;
+ int forceifnolabel = 0;
+ char *bootblkname = DEFBBLKNAME;
+ int nowrite = 0;
+ int allok = 0;
+
+ while ((c = getopt(argc, argv, "vnf")) != -1) {
+ switch (c) {
+ case 'n':
+ /* Do not actually write the bootblock to disk */
+ nowrite = 1;
+ break;
+ case 'v':
+ /* Chat */
+ verbose = 1;
+ break;
+ case 'f':
+ /* assume zero offset if no disklabel */
+ forceifnolabel = 1;
+ break;
+ default:
+ usage();
+ }
+ }
+
+ if (argc - optind != 2) {
+ usage();
+ }
+
+ bp = loadprotoblocks(argv[optind], &size);
+ if (!bp)
+ errx(1, "error reading bootblocks");
+
+ fraglist->numentries = 0;
+
+ /* do we need the fraglist? */
+ if (size > fraglist->loadsz * DEV_BSIZE) {
+
+ inode = createfileondev(argv[optind + 1], bootblkname,
+ bp + fraglist->loadsz * DEV_BSIZE,
+ size - fraglist->loadsz * DEV_BSIZE);
+ if (inode == (ino_t) - 1)
+ goto out;
+
+ /* paranoia */
+ sync();
+ sleep(3);
+
+ if (loadblocknums(argv[optind + 1], inode))
+ goto out;
+
+ size = fraglist->loadsz * DEV_BSIZE;
+ /* size to be written to bootsect */
+ }
+
+ devfd = open(argv[optind + 1], O_RDWR, 0);
+ if (devfd < 0) {
+ warn("open raw partition RW");
+ goto out;
+ }
+ if (ioctl(devfd, DIOCGDINFO, &dl) < 0) {
+ if ((errno == EINVAL) || (errno == ENOTTY)) {
+ if (forceifnolabel)
+ bsdoffs = 0;
+ else {
+ warnx("no disklabel, use -f to install anyway");
+ goto out;
+ }
+ } else {
+ warn("get disklabel");
+ goto out;
+ }
+ } else {
+ char c = argv[optind + 1][strlen(argv[optind + 1]) - 1];
+#define isvalidpart(c) ((c) >= 'a' && (c) <= 'z')
+ if(!isvalidpart(c) || (c - 'a') >= dl.d_npartitions) {
+ warnx("invalid partition");
+ goto out;
+ }
+ bsdoffs = dl.d_partitions[c - 'a'].p_offset;
+ }
+ if (verbose)
+ fprintf(stderr, "BSD partition starts at sector %d\n", bsdoffs);
+
+ /*
+ * add offset of BSD partition to fraglist entries
+ */
+ for (i = 0; i < fraglist->numentries; i++)
+ fraglist->entries[i].offset += bsdoffs;
+
+ if (!nowrite) {
+ /*
+ * write first blocks (max loadsz) to start of BSD partition,
+ * skip disklabel (in second disk block)
+ */
+ lseek(devfd, 0, SEEK_SET);
+ res = write(devfd, bp, DEV_BSIZE);
+ if (res < 0) {
+ warn("final write1");
+ goto out;
+ }
+ lseek(devfd, 2 * DEV_BSIZE, SEEK_SET);
+ res = write(devfd, bp + 2 * DEV_BSIZE, size - 2 * DEV_BSIZE);
+ if (res < 0) {
+ warn("final write2");
+ goto out;
+ }
+ }
+ allok = 1;
+
+out:
+ if (devfd >= 0)
+ close(devfd);
+ if (bp)
+ free(bp);
+ if (inode != (ino_t) - 1) {
+ cleanupfileondev(argv[optind + 1], bootblkname, !allok || nowrite);
+ }
+ return (!allok);
+}
OpenPOWER on IntegriCloud