root/arch/i386/stand/installboot/installboot.c

/* [<][>][^][v][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. usage
  2. main
  3. loadproto
  4. devread
  5. getbootparams
  6. sym_set_value
  7. pbr_set_symbols

    1 /*      $OpenBSD: installboot.c,v 1.49 2006/02/14 17:16:19 aaron Exp $  */
    2 /*      $NetBSD: installboot.c,v 1.5 1995/11/17 23:23:50 gwr Exp $ */
    3 
    4 /*
    5  * Copyright (c) 2003 Tom Cosgrove <tom.cosgrove@arches-consulting.com>
    6  * Copyright (c) 1997 Michael Shalayeff
    7  * Copyright (c) 1994 Paul Kranenburg
    8  * All rights reserved.
    9  *
   10  * Redistribution and use in source and binary forms, with or without
   11  * modification, are permitted provided that the following conditions
   12  * are met:
   13  * 1. Redistributions of source code must retain the above copyright
   14  *    notice, this list of conditions and the following disclaimer.
   15  * 2. Redistributions in binary form must reproduce the above copyright
   16  *    notice, this list of conditions and the following disclaimer in the
   17  *    documentation and/or other materials provided with the distribution.
   18  * 3. All advertising materials mentioning features or use of this software
   19  *    must display the following acknowledgement:
   20  *      This product includes software developed by Paul Kranenburg.
   21  * 4. The name of the author may not be used to endorse or promote products
   22  *    derived from this software without specific prior written permission
   23  *
   24  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
   25  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
   26  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
   27  * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
   28  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
   29  * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
   30  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
   31  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
   32  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
   33  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
   34  */
   35 
   36 #include <sys/param.h>
   37 #include <sys/mount.h>
   38 #include <sys/time.h>
   39 #include <sys/stat.h>
   40 #include <sys/disklabel.h>
   41 #include <sys/dkio.h>
   42 #include <sys/ioctl.h>
   43 #include <ufs/ufs/dinode.h>
   44 #include <ufs/ufs/dir.h>
   45 #include <ufs/ffs/fs.h>
   46 #include <sys/reboot.h>
   47 
   48 #include <uvm/uvm_extern.h>
   49 #include <sys/sysctl.h>
   50 
   51 #include <machine/cpu.h>
   52 #include <machine/biosvar.h>
   53 
   54 #include <err.h>
   55 #include <a.out.h>
   56 #include <sys/exec_elf.h>
   57 #include <fcntl.h>
   58 #include <nlist.h>
   59 #include <stdlib.h>
   60 #include <stdio.h>
   61 #include <string.h>
   62 #include <unistd.h>
   63 #include <util.h>
   64 
   65 struct  sym_data {
   66         char            *sym_name;              /* Must be initialised */
   67         int             sym_size;               /* And this one */
   68         int             sym_set;                /* Rest set at runtime */
   69         u_int32_t       sym_value;
   70 };
   71 
   72 extern  char *__progname;
   73 int     verbose, nowrite = 0;
   74 char    *boot, *proto, *dev, *realdev;
   75 struct sym_data pbr_symbols[] = {
   76         {"_fs_bsize_p", 2},
   77         {"_fs_bsize_s", 2},
   78         {"_fsbtodb",    1},
   79         {"_p_offset",   4},
   80         {"_inodeblk",   4},
   81         {"_inodedbl",   4},
   82         {"_nblocks",    2},
   83         {NULL}
   84 };
   85 
   86 #define INODESEG        0x07e0  /* where we will put /boot's inode's block */
   87 #define BOOTSEG         0x07c0  /* biosboot loaded here */
   88 
   89 #define INODEOFF  ((INODESEG-BOOTSEG) << 4)
   90 
   91 static char     *loadproto(char *, long *);
   92 static int      getbootparams(char *, int, struct disklabel *);
   93 static void     devread(int, void *, daddr_t, size_t, char *);
   94 static void     sym_set_value(struct sym_data *, char *, u_int32_t);
   95 static void     pbr_set_symbols(char *, char *, struct sym_data *);
   96 static void     usage(void);
   97 
   98 static void
   99 usage(void)
  100 {
  101         fprintf(stderr, "usage: %s [-nv] boot biosboot device\n", __progname);
  102         exit(1);
  103 }
  104 
  105 /*
  106  * Read information about /boot's inode and filesystem parameters, then
  107  * put biosboot (partition boot record) on the target drive with these
  108  * parameters patched in.
  109  */
  110 int
  111 main(int argc, char *argv[])
  112 {
  113         int     c;
  114         int     devfd;
  115         char    *protostore;
  116         long    protosize;
  117         struct  stat sb;
  118         struct  disklabel dl;
  119         struct  dos_mbr mbr;
  120         struct  dos_partition *dp;
  121         off_t   startoff = 0;
  122 
  123         while ((c = getopt(argc, argv, "vn")) != -1) {
  124                 switch (c) {
  125                 case 'n':
  126                         /* Do not actually write the bootblock to disk. */
  127                         nowrite = 1;
  128                         break;
  129                 case 'v':
  130                         /* Give more information. */
  131                         verbose = 1;
  132                         break;
  133                 default:
  134                         usage();
  135                 }
  136         }
  137 
  138         if (argc - optind < 3)
  139                 usage();
  140 
  141         boot = argv[optind];
  142         proto = argv[optind + 1];
  143         realdev = dev = argv[optind + 2];
  144 
  145         /* Open and check raw disk device. */
  146         if ((devfd = opendev(dev, (nowrite? O_RDONLY:O_RDWR),
  147             OPENDEV_PART, &realdev)) < 0)
  148                 err(1, "open: %s", realdev);
  149 
  150         if (verbose) {
  151                 fprintf(stderr, "boot: %s\n", boot);
  152                 fprintf(stderr, "proto: %s\n", proto);
  153                 fprintf(stderr, "device: %s\n", realdev);
  154         }
  155 
  156         if (ioctl(devfd, DIOCGDINFO, &dl) != 0)
  157                 err(1, "disklabel: %s", realdev);
  158 
  159         /* Check disklabel. */
  160         if (dl.d_magic != DISKMAGIC)
  161                 err(1, "bad disklabel magic=0x%08x", dl.d_magic);
  162 
  163         /* Warn on unknown disklabel types. */
  164         if (dl.d_type == 0)
  165                 warnx("disklabel type unknown");
  166 
  167         /* Load proto blocks into core. */
  168         if ((protostore = loadproto(proto, &protosize)) == NULL)
  169                 exit(1);
  170 
  171         /* XXX - Paranoia: Make sure size is aligned! */
  172         if (protosize & (DEV_BSIZE - 1))
  173                 errx(1, "proto %s bad size=%ld", proto, protosize);
  174 
  175         /* Write patched proto bootblock(s) into the superblock. */
  176         if (protosize > SBSIZE - DEV_BSIZE)
  177                 errx(1, "proto bootblocks too big");
  178 
  179         if (fstat(devfd, &sb) < 0)
  180                 err(1, "stat: %s", realdev);
  181 
  182         if (!S_ISCHR(sb.st_mode))
  183                 errx(1, "%s: not a character device", realdev);
  184 
  185         /* Get bootstrap parameters that are to be patched into proto. */
  186         if (getbootparams(boot, devfd, &dl) != 0)
  187                 exit(1);
  188 
  189         /* Patch the parameters into the proto bootstrap sector. */
  190         pbr_set_symbols(proto, protostore, pbr_symbols);
  191 
  192         if (!nowrite) {
  193                 /* Sync filesystems (to clean in-memory superblock?). */
  194                 sync(); sleep(1);
  195         }
  196 
  197         if (dl.d_type != 0 && dl.d_type != DTYPE_FLOPPY &&
  198             dl.d_type != DTYPE_VND) {
  199                 if (lseek(devfd, (off_t)DOSBBSECTOR, SEEK_SET) < 0 ||
  200                     read(devfd, &mbr, sizeof(mbr)) < sizeof(mbr))
  201                         err(4, "can't read master boot record");
  202 
  203                 if (mbr.dmbr_sign != DOSMBR_SIGNATURE)
  204                         errx(1, "broken MBR");
  205 
  206                 /* Find OpenBSD partition. */
  207                 for (dp = mbr.dmbr_parts; dp < &mbr.dmbr_parts[NDOSPART];
  208                     dp++) {
  209                         if (dp->dp_size && dp->dp_typ == DOSPTYP_OPENBSD) {
  210                                 startoff = (off_t)dp->dp_start * dl.d_secsize;
  211                                 fprintf(stderr, "using MBR partition %ld: "
  212                                     "type %d (0x%02x) offset %d (0x%x)\n",
  213                                     (long)(dp - mbr.dmbr_parts),
  214                                     dp->dp_typ, dp->dp_typ,
  215                                     dp->dp_start, dp->dp_start);
  216                                 break;
  217                         }
  218                 }
  219                 /* Don't check for old part number, that is ;-p */
  220                 if (dp >= &mbr.dmbr_parts[NDOSPART])
  221                         errx(1, "no OpenBSD partition");
  222         }
  223 
  224         if (!nowrite) {
  225                 if (lseek(devfd, startoff, SEEK_SET) < 0 ||
  226                     write(devfd, protostore, protosize) != protosize)
  227                         err(1, "write bootstrap");
  228         }
  229 
  230         (void)close(devfd);
  231 
  232         return 0;
  233 }
  234 
  235 /*
  236  * Load the prototype boot sector (biosboot) into memory.
  237  */
  238 static char *
  239 loadproto(char *fname, long *size)
  240 {
  241         int     fd;
  242         size_t  tdsize;         /* text+data size */
  243         char    *bp;
  244         Elf_Ehdr eh;
  245         Elf_Word phsize;
  246         Elf_Phdr *ph;
  247 
  248         if ((fd = open(fname, O_RDONLY)) < 0)
  249                 err(1, "%s", fname);
  250 
  251         if (read(fd, &eh, sizeof(eh)) != sizeof(eh))
  252                 errx(1, "%s: read failed", fname);
  253 
  254         if (!IS_ELF(eh))
  255                 errx(1, "%s: bad magic: 0x%02x%02x%02x%02x",
  256                     boot,
  257                     eh.e_ident[EI_MAG0], eh.e_ident[EI_MAG1],
  258                     eh.e_ident[EI_MAG2], eh.e_ident[EI_MAG3]);
  259 
  260         /*
  261          * We have to include the exec header in the beginning of
  262          * the buffer, and leave extra space at the end in case
  263          * the actual write to disk wants to skip the header.
  264          */
  265 
  266         /* Program load header. */
  267         if (eh.e_phnum != 1)
  268                 errx(1, "%s: %u ELF load sections (only support 1)",
  269                     boot, eh.e_phnum);
  270 
  271         phsize = eh.e_phnum * sizeof(Elf_Phdr);
  272         ph = malloc(phsize);
  273         if (ph == NULL)
  274                 err(1, NULL);
  275 
  276         lseek(fd, eh.e_phoff, SEEK_SET);
  277 
  278         if (read(fd, ph, phsize) != phsize)
  279                 errx(1, "%s: can't read header", boot);
  280 
  281         tdsize = ph->p_filesz;
  282 
  283         /*
  284          * Allocate extra space here because the caller may copy
  285          * the boot block starting at the end of the exec header.
  286          * This prevents reading beyond the end of the buffer.
  287          */
  288         if ((bp = calloc(tdsize, 1)) == NULL) {
  289                 err(1, NULL);
  290         }
  291 
  292         /* Read the rest of the file. */
  293         lseek(fd, ph->p_offset, SEEK_SET);
  294         if (read(fd, bp, tdsize) != tdsize) {
  295                 errx(1, "%s: read failed", fname);
  296         }
  297 
  298         *size = tdsize; /* not aligned to DEV_BSIZE */
  299 
  300         if (verbose) {
  301                 fprintf(stderr, "%s: entry point %#x\n", fname, eh.e_entry);
  302                 fprintf(stderr, "proto bootblock size %ld\n", *size);
  303         }
  304 
  305         close(fd);
  306         return bp;
  307 }
  308 
  309 static void
  310 devread(int fd, void *buf, daddr_t blk, size_t size, char *msg)
  311 {
  312         if (lseek(fd, dbtob((off_t)blk), SEEK_SET) != dbtob((off_t)blk))
  313                 err(1, "%s: devread: lseek", msg);
  314 
  315         if (read(fd, buf, size) != size)
  316                 err(1, "%s: devread: read", msg);
  317 }
  318 
  319 static char sblock[SBSIZE];
  320 
  321 /*
  322  * Read information about /boot's inode, then put this and filesystem
  323  * parameters from the superblock into pbr_symbols.
  324  */
  325 static int
  326 getbootparams(char *boot, int devfd, struct disklabel *dl)
  327 {
  328         int             fd;
  329         struct stat     statbuf, sb;
  330         struct statfs   statfsbuf;
  331         struct partition *pl;
  332         struct fs       *fs;
  333         char            *buf;
  334         daddr_t         blk, *ap;
  335         struct ufs1_dinode      *ip;
  336         int             ndb;
  337         int             mib[3];
  338         size_t          size;
  339         dev_t           dev;
  340 
  341         /*
  342          * Open 2nd-level boot program and record enough details about
  343          * where it is on the filesystem represented by `devfd'
  344          * (inode block, offset within that block, and various filesystem
  345          * parameters essentially taken from the superblock) for biosboot
  346          * to be able to load it later.
  347          */
  348 
  349         /* Make sure the (probably new) boot file is on disk. */
  350         sync(); sleep(1);
  351 
  352         if ((fd = open(boot, O_RDONLY)) < 0)
  353                 err(1, "open: %s", boot);
  354 
  355         if (fstatfs(fd, &statfsbuf) != 0)
  356                 err(1, "statfs: %s", boot);
  357 
  358         if (strncmp(statfsbuf.f_fstypename, "ffs", MFSNAMELEN) &&
  359             strncmp(statfsbuf.f_fstypename, "ufs", MFSNAMELEN) )
  360                 errx(1, "%s: not on an FFS filesystem", boot);
  361 
  362 #if 0
  363         if (read(fd, &eh, sizeof(eh)) != sizeof(eh))
  364                 errx(1, "read: %s", boot);
  365 
  366         if (!IS_ELF(eh)) {
  367                 errx(1, "%s: bad magic: 0x%02x%02x%02x%02x",
  368                     boot,
  369                     eh.e_ident[EI_MAG0], eh.e_ident[EI_MAG1],
  370                     eh.e_ident[EI_MAG2], eh.e_ident[EI_MAG3]);
  371         }
  372 #endif
  373 
  374         if (fsync(fd) != 0)
  375                 err(1, "fsync: %s", boot);
  376 
  377         if (fstat(fd, &statbuf) != 0)
  378                 err(1, "fstat: %s", boot);
  379 
  380         if (fstat(devfd, &sb) != 0)
  381                 err(1, "fstat: %s", realdev);
  382 
  383         /* Check devices. */
  384         mib[0] = CTL_MACHDEP;
  385         mib[1] = CPU_CHR2BLK;
  386         mib[2] = sb.st_rdev;
  387         size = sizeof(dev);
  388         if (sysctl(mib, 3, &dev, &size, NULL, 0) >= 0) {
  389                 if (statbuf.st_dev / MAXPARTITIONS != dev / MAXPARTITIONS)
  390                         errx(1, "cross-device install");
  391         }
  392 
  393         pl = &dl->d_partitions[DISKPART(statbuf.st_dev)];
  394         close(fd);
  395 
  396         /* Read superblock. */
  397         devread(devfd, sblock, pl->p_offset + SBLOCK, SBSIZE, "superblock");
  398         fs = (struct fs *)sblock;
  399 
  400         /* Sanity-check super-block. */
  401         if (fs->fs_magic != FS_MAGIC)
  402                 errx(1, "Bad magic number in superblock");
  403         if (fs->fs_inopb <= 0)
  404                 err(1, "Bad inopb=%d in superblock", fs->fs_inopb);
  405 
  406         /* Read inode. */
  407         if ((buf = malloc(fs->fs_bsize)) == NULL)
  408                 err(1, NULL);
  409 
  410         blk = fsbtodb(fs, ino_to_fsba(fs, statbuf.st_ino));
  411 
  412         devread(devfd, buf, pl->p_offset + blk, fs->fs_bsize, "inode");
  413         ip = (struct ufs1_dinode *)(buf) + ino_to_fsbo(fs, statbuf.st_ino);
  414 
  415         /*
  416          * Have the inode.  Figure out how many filesystem blocks (not disk
  417          * sectors) there are for biosboot to load.
  418          */
  419         ndb = howmany(ip->di_size, fs->fs_bsize);
  420         if (ndb <= 0)
  421                 errx(1, "No blocks to load");
  422 
  423         /*
  424          * Now set the values that will need to go into biosboot
  425          * (the partition boot record, a.k.a. the PBR).
  426          */
  427         sym_set_value(pbr_symbols, "_fs_bsize_p", (fs->fs_bsize / 16));
  428         sym_set_value(pbr_symbols, "_fs_bsize_s", (fs->fs_bsize / 512));
  429         sym_set_value(pbr_symbols, "_fsbtodb", fs->fs_fsbtodb);
  430         sym_set_value(pbr_symbols, "_p_offset", pl->p_offset);
  431         sym_set_value(pbr_symbols, "_inodeblk",
  432             ino_to_fsba(fs, statbuf.st_ino));
  433         ap = ip->di_db;
  434         sym_set_value(pbr_symbols, "_inodedbl",
  435             ((((char *)ap) - buf) + INODEOFF));
  436         sym_set_value(pbr_symbols, "_nblocks", ndb);
  437 
  438         if (verbose) {
  439                 fprintf(stderr, "%s is %d blocks x %d bytes\n",
  440                     boot, ndb, fs->fs_bsize);
  441                 fprintf(stderr, "fs block shift %u; part offset %u; "
  442                     "inode block %u, offset %ld\n",
  443                     fs->fs_fsbtodb, pl->p_offset,
  444                     ino_to_fsba(fs, statbuf.st_ino),
  445                     ((((char *)ap) - buf) + INODEOFF));
  446         }
  447 
  448         return 0;
  449 }
  450 
  451 static void
  452 sym_set_value(struct sym_data *sym_list, char *sym, u_int32_t value)
  453 {
  454         struct sym_data *p;
  455 
  456         for (p = sym_list; p->sym_name != NULL; p++) {
  457                 if (strcmp(p->sym_name, sym) == 0)
  458                         break;
  459         }
  460 
  461         if (p->sym_name == NULL)
  462                 errx(1, "%s: no such symbol", sym);
  463 
  464         if (p->sym_set)
  465                 errx(1, "%s already set", p->sym_name);
  466 
  467         p->sym_value = value;
  468         p->sym_set = 1;
  469 }
  470 
  471 /*
  472  * Write the parameters stored in sym_list into the in-memory copy of
  473  * the prototype biosboot (proto), ready for it to be written to disk.
  474  */
  475 static void
  476 pbr_set_symbols(char *fname, char *proto, struct sym_data *sym_list)
  477 {
  478         struct sym_data *sym;
  479         struct nlist    *nl;
  480         char            *vp;
  481         u_int32_t       *lp;
  482         u_int16_t       *wp;
  483         u_int8_t        *bp;
  484 
  485         for (sym = sym_list; sym->sym_name != NULL; sym++) {
  486                 if (!sym->sym_set)
  487                         errx(1, "%s not set", sym->sym_name);
  488 
  489                 /* Allocate space for 2; second is null-terminator for list. */
  490                 nl = calloc(2, sizeof(struct nlist));
  491                 if (nl == NULL)
  492                         err(1, NULL);
  493 
  494                 nl->n_un.n_name = sym->sym_name;
  495 
  496                 if (nlist(fname, nl) != 0)
  497                         errx(1, "%s: symbol %s not found",
  498                             fname, sym->sym_name);
  499 
  500                 if (nl->n_type != (N_TEXT))
  501                         errx(1, "%s: %s: wrong type (%x)",
  502                             fname, sym->sym_name, nl->n_type);
  503 
  504                 /* Get a pointer to where the symbol's value needs to go. */
  505                 vp = proto + nl->n_value;
  506 
  507                 switch (sym->sym_size) {
  508                 case 4:                                 /* u_int32_t */
  509                         lp = (u_int32_t *) vp;
  510                         *lp = sym->sym_value;
  511                         break;
  512                 case 2:                                 /* u_int16_t */
  513                         if (sym->sym_value >= 0x10000)  /* out of range */
  514                                 errx(1, "%s: symbol out of range (%u)",
  515                                     sym->sym_name, sym->sym_value);
  516                         wp = (u_int16_t *) vp;
  517                         *wp = (u_int16_t) sym->sym_value;
  518                         break;
  519                 case 1:                                 /* u_int16_t */
  520                         if (sym->sym_value >= 0x100)    /* out of range */
  521                                 errx(1, "%s: symbol out of range (%u)",
  522                                     sym->sym_name, sym->sym_value);
  523                         bp = (u_int8_t *) vp;
  524                         *bp = (u_int8_t) sym->sym_value;
  525                         break;
  526                 default:
  527                         errx(1, "%s: bad symbol size %d",
  528                             sym->sym_name, sym->sym_size);
  529                         /* NOTREACHED */
  530                 }
  531 
  532                 free(nl);
  533         }
  534 }

/* [<][>][^][v][top][bottom][index][help] */