diff options
Diffstat (limited to 'sys/boot/i386/installboot/installboot.c')
-rw-r--r-- | sys/boot/i386/installboot/installboot.c | 433 |
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); +} |