summaryrefslogtreecommitdiff
path: root/sys/arch/amd64/stand/installboot
diff options
context:
space:
mode:
authorMichael Shalayeff <mickey@cvs.openbsd.org>2004-02-03 12:09:48 +0000
committerMichael Shalayeff <mickey@cvs.openbsd.org>2004-02-03 12:09:48 +0000
commitee64ff9774a8a97d8ac9159076e5aa096f4b0465 (patch)
tree56c0a44d5a11182f89b55b68c79f750f9a093bc6 /sys/arch/amd64/stand/installboot
parent74d9ef0d94f422fd01f5c329723d4a06537c0884 (diff)
das boot; das cloned das from das i386
Diffstat (limited to 'sys/arch/amd64/stand/installboot')
-rw-r--r--sys/arch/amd64/stand/installboot/Makefile19
-rw-r--r--sys/arch/amd64/stand/installboot/installboot.8135
-rw-r--r--sys/arch/amd64/stand/installboot/installboot.c535
-rw-r--r--sys/arch/amd64/stand/installboot/nlist.c547
4 files changed, 1236 insertions, 0 deletions
diff --git a/sys/arch/amd64/stand/installboot/Makefile b/sys/arch/amd64/stand/installboot/Makefile
new file mode 100644
index 00000000000..400dd87b217
--- /dev/null
+++ b/sys/arch/amd64/stand/installboot/Makefile
@@ -0,0 +1,19 @@
+# $OpenBSD: Makefile,v 1.1 2004/02/03 12:09:47 mickey Exp $
+
+MAN= installboot.8
+
+.if ${MACHINE} == "amd64"
+PROG= installboot
+CPPFLAGFS+=-I${.CURDIR}
+
+LDADD= -lutil
+DPADD= ${LIBUTIL}
+# Need this to work in the miniroot
+LDSTATIC= -static
+
+SADIR= ${.CURDIR}/..
+.else
+NOPROG=
+.endif
+
+.include <bsd.prog.mk>
diff --git a/sys/arch/amd64/stand/installboot/installboot.8 b/sys/arch/amd64/stand/installboot/installboot.8
new file mode 100644
index 00000000000..fc1836b2ac7
--- /dev/null
+++ b/sys/arch/amd64/stand/installboot/installboot.8
@@ -0,0 +1,135 @@
+.\" $OpenBSD: installboot.8,v 1.1 2004/02/03 12:09:47 mickey Exp $
+.\"
+.\" Copyright (c) 1997 Michael Shalayeff
+.\" 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.
+.\"
+.\" 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 REGENTS OR CONTRIBUTORS 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.
+.\"
+.\"
+.Dd December 23, 2003
+.Dt INSTALLBOOT 8 i386
+.Os
+.Sh NAME
+.Nm installboot
+.Nd installs a bootstrap on an FFS disk or partition
+.Sh SYNOPSIS
+.Nm installboot
+.Op Fl nv
+.Ar boot
+.Ar biosboot
+.Ar disk
+.Sh DESCRIPTION
+.Nm
+is used to install a
+.Dq first-stage
+boot program into the boot area of an FFS disk partition.
+It inserts the block number and offset of the inode of
+the second-stage boot program
+.Xr boot 8
+so that the
+.Xr biosboot 8
+program can load it.
+Various filesystem parameters are also patched into the boot block.
+.Pp
+The options are as follows:
+.Bl -tag -width flag_opt
+.It Fl n
+Do not actually write anything on the disk.
+.It Fl v
+Be verbose, printing out the data that are stored in
+.Ar biosboot
+along with lots of other information.
+.El
+.Pp
+The arguments are:
+.Bl -tag -width biosboot
+.It Ar boot
+The name of the second-stage boot program in the mounted file system
+where the first-stage boot program is to be installed.
+This should be a full pathname.
+.It Ar biosboot
+The name of the prototype file for the first stage boot program,
+usually
+.Pa /usr/mdec/biosboot .
+.It Ar disk
+The name of the disk containing the partition in which the second-stage
+boot program resides and the first-stage boot program is to be installed.
+This can either be specified in short form (e.g.,
+.Sq sd0
+or
+.Sq wd0 ) ,
+or as the explicit device node, such as
+.Pa /dev/rsd0c
+or
+.Pa /dev/rwd0c .
+.Pp
+Note that you must be in single-user mode or have your kernel in
+insecure mode (see the
+.Xr sysctl 8
+.Va kern.securelevel
+variable or
+.Pa /etc/rc.securelevel )
+to enable access to the raw partition of a mounted disk.
+.Pp
+The
+.Sq c
+partition is always used to represent the
+.Dq entire
+disk on i386.
+.El
+.Sh EXAMPLES
+The typical use is
+.Bd -literal -offset indent
+# cp /usr/mdec/boot /boot
+# /usr/mdec/installboot -n -v /boot /usr/mdec/biosboot sd0
+.Ed
+.Pp
+And if the information supplied looks right, run the above without the
+.Fl n
+flag.
+If you are upgrading an old system, you may need to perform
+some additional steps first.
+For example:
+.Bd -literal -offset indent
+boot the floppy.fs filesystem floppy
+# fsck /dev/rsd0a
+# mount /dev/sd0a /mnt
+# cp /usr/mdec/boot /mnt/boot
+# /usr/mdec/installboot -v /mnt/boot /usr/mdec/biosboot sd0
+.Ed
+.Sh SEE ALSO
+.Xr biosboot 8 ,
+.Xr boot 8 ,
+.Xr disklabel 8 ,
+.Xr fdisk 8 ,
+.Xr init 8
+.Sh CAVEATS
+The disklabel
+.Va d_type
+field must be set to a value other than
+.Dq unknown .
+.Pp
+.Pa /boot
+must be on the drive/partition specified by
+.Pa disk ;
+you cannot perform cross-device
+.Nm Ns s .
diff --git a/sys/arch/amd64/stand/installboot/installboot.c b/sys/arch/amd64/stand/installboot/installboot.c
new file mode 100644
index 00000000000..f834d769143
--- /dev/null
+++ b/sys/arch/amd64/stand/installboot/installboot.c
@@ -0,0 +1,535 @@
+/* $OpenBSD: installboot.c,v 1.1 2004/02/03 12:09:47 mickey Exp $ */
+/* $NetBSD: installboot.c,v 1.5 1995/11/17 23:23:50 gwr Exp $ */
+
+/*
+ * 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 ELFSIZE 32
+
+#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 <sys/reboot.h>
+
+#include <uvm/uvm_extern.h>
+#include <sys/sysctl.h>
+
+#include <machine/cpu.h>
+#include <machine/biosvar.h>
+
+#include <err.h>
+#include <a.out.h>
+#include <sys/exec_elf.h>
+#include <fcntl.h>
+#include <nlist.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <unistd.h>
+#include <util.h>
+
+#include "nlist.c"
+
+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 char *__progname;
+int verbose, nowrite = 0;
+char *boot, *proto, *dev, *realdev;
+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}
+};
+
+#define INODESEG 0x07e0 /* where we will put /boot's inode's block */
+#define BOOTSEG 0x07c0 /* biosboot loaded here */
+
+#define INODEOFF ((INODESEG-BOOTSEG) << 4)
+
+static char *loadproto(char *, long *);
+static int getbootparams(char *, int, struct disklabel *);
+static void devread(int, void *, daddr_t, size_t, char *);
+static void sym_set_value(struct sym_data *, char *, u_int32_t);
+static void pbr_set_symbols(char *, char *, struct sym_data *);
+static void usage(void);
+
+static void
+usage(void)
+{
+ fprintf(stderr, "usage: %s [-nv] boot biosboot device\n", __progname);
+ exit(1);
+}
+
+/*
+ * Read information about /boot's inode and filesystem parameters, then
+ * put biosboot (partition boot record) on the target drive with these
+ * parameters patched in.
+ */
+int
+main(int argc, char *argv[])
+{
+ int c;
+ int devfd;
+ char *protostore;
+ long protosize;
+ struct stat sb;
+ struct disklabel dl;
+ struct dos_mbr mbr;
+ struct dos_partition *dp;
+ off_t startoff = 0;
+
+ while ((c = getopt(argc, argv, "vn")) != -1) {
+ switch (c) {
+ case 'n':
+ /* Do not actually write the bootblock to disk. */
+ nowrite = 1;
+ break;
+ case 'v':
+ /* Give more information. */
+ verbose = 1;
+ break;
+ default:
+ usage();
+ }
+ }
+
+ if (argc - optind < 3)
+ usage();
+
+ boot = argv[optind];
+ proto = argv[optind + 1];
+ realdev = dev = argv[optind + 2];
+
+ /* Open and check raw disk device. */
+ if ((devfd = opendev(dev, (nowrite? O_RDONLY:O_RDWR),
+ OPENDEV_PART, &realdev)) < 0)
+ err(1, "open: %s", realdev);
+
+ if (verbose) {
+ fprintf(stderr, "boot: %s\n", boot);
+ fprintf(stderr, "proto: %s\n", proto);
+ fprintf(stderr, "device: %s\n", realdev);
+ }
+
+ if (ioctl(devfd, DIOCGDINFO, &dl) != 0)
+ err(1, "disklabel: %s", realdev);
+
+ /* Check disklabel. */
+ if (dl.d_magic != DISKMAGIC)
+ err(1, "bad disklabel magic=%0x8x", dl.d_magic);
+
+ /* Warn on unknown disklabel types. */
+ if (dl.d_type == 0)
+ warnx("disklabel type unknown");
+
+ /* Load proto blocks into core. */
+ if ((protostore = loadproto(proto, &protosize)) == NULL)
+ exit(1);
+
+ /* XXX - Paranoia: Make sure size is aligned! */
+ if (protosize & (DEV_BSIZE - 1))
+ err(1, "proto %s bad size=%ld", proto, protosize);
+
+ /* Write patched proto bootblock(s) into the superblock. */
+ if (protosize > SBSIZE - DEV_BSIZE)
+ errx(1, "proto bootblocks too big");
+
+ if (fstat(devfd, &sb) < 0)
+ err(1, "stat: %s", realdev);
+
+ if (!S_ISCHR(sb.st_mode))
+ errx(1, "%s: not a character device", realdev);
+
+ /* Get bootstrap parameters that are to be patched into proto. */
+ if (getbootparams(boot, devfd, &dl) != 0)
+ exit(1);
+
+ /* Patch the parameters into the proto bootstrap sector. */
+ pbr_set_symbols(proto, protostore, pbr_symbols);
+
+ if (!nowrite) {
+ /* Sync filesystems (to clean in-memory superblock?). */
+ sync(); sleep(1);
+ }
+
+ if (dl.d_type != 0 && dl.d_type != DTYPE_FLOPPY &&
+ dl.d_type != DTYPE_VND) {
+ if (lseek(devfd, (off_t)DOSBBSECTOR, SEEK_SET) < 0 ||
+ read(devfd, &mbr, sizeof(mbr)) < sizeof(mbr))
+ err(4, "can't read master boot record");
+
+ if (mbr.dmbr_sign != DOSMBR_SIGNATURE)
+ errx(1, "broken MBR");
+
+ /* Find OpenBSD partition. */
+ for (dp = mbr.dmbr_parts; dp < &mbr.dmbr_parts[NDOSPART];
+ dp++) {
+ if (dp->dp_size && dp->dp_typ == DOSPTYP_OPENBSD) {
+ startoff = (off_t)dp->dp_start * dl.d_secsize;
+ fprintf(stderr, "using MBR partition %ld: "
+ "type %d (0x%02x) offset %d (0x%x)\n",
+ (long)(dp - mbr.dmbr_parts),
+ dp->dp_typ, dp->dp_typ,
+ dp->dp_start, dp->dp_start);
+ break;
+ }
+ }
+ /* Don't check for old part number, that is ;-p */
+ if (dp >= &mbr.dmbr_parts[NDOSPART])
+ errx(1, "no OpenBSD partition");
+ }
+
+ if (!nowrite) {
+ if (lseek(devfd, startoff, SEEK_SET) < 0 ||
+ write(devfd, protostore, protosize) != protosize)
+ err(1, "write bootstrap");
+ }
+
+ (void)close(devfd);
+
+ return 0;
+}
+
+/*
+ * 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",
+ boot,
+ 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)",
+ boot, 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", boot);
+
+ 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) != tdsize) {
+ errx(1, "%s: read failed", fname);
+ }
+
+ *size = tdsize; /* not aligned to DEV_BSIZE */
+
+ if (verbose) {
+ fprintf(stderr, "%s: entry point %#x\n", fname, eh.e_entry);
+ fprintf(stderr, "proto bootblock size %ld\n", *size);
+ }
+
+ 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) != 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 *pl;
+ struct fs *fs;
+ char *buf;
+ daddr_t blk, *ap;
+ struct ufs1_dinode *ip;
+ int ndb;
+ int mib[4];
+ 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: %s", realdev);
+
+ /* 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");
+ }
+
+ pl = &dl->d_partitions[DISKPART(statbuf.st_dev)];
+ close(fd);
+
+ /* Read superblock. */
+ devread(devfd, sblock, pl->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, pl->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");
+ if (verbose)
+ fprintf(stderr, "%s is %d blocks x %d bytes\n",
+ boot, ndb, fs->fs_bsize);
+
+ /*
+ * 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 / 512));
+ sym_set_value(pbr_symbols, "_fsbtodb", fs->fs_fsbtodb);
+ sym_set_value(pbr_symbols, "_p_offset", pl->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);
+
+ return 0;
+}
+
+static 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);
+
+ if (p->sym_set)
+ errx(1, "%s already set", p->sym_name);
+
+ 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.
+ */
+static 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);
+
+ if (verbose)
+ fprintf(stderr, "%s = %u\n",
+ sym->sym_name, sym->sym_value);
+ }
+}
diff --git a/sys/arch/amd64/stand/installboot/nlist.c b/sys/arch/amd64/stand/installboot/nlist.c
new file mode 100644
index 00000000000..477c6c0f738
--- /dev/null
+++ b/sys/arch/amd64/stand/installboot/nlist.c
@@ -0,0 +1,547 @@
+/*
+ * Copyright (c) 1989, 1993
+ * The Regents of the University of California. 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. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``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 REGENTS OR CONTRIBUTORS 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.
+ */
+
+#if defined(LIBC_SCCS) && !defined(lint)
+static char rcsid[] = "$OpenBSD: nlist.c,v 1.1 2004/02/03 12:09:47 mickey Exp $";
+#endif /* LIBC_SCCS and not lint */
+
+#include <sys/types.h>
+#include <sys/param.h>
+#include <sys/mman.h>
+#include <sys/stat.h>
+
+#include <errno.h>
+#include <fcntl.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <a.out.h> /* pulls in nlist.h */
+
+#ifdef _NLIST_DO_ELF
+#include <elf_abi.h>
+#include <olf_abi.h>
+#endif
+
+#ifdef _NLIST_DO_ECOFF
+#include <sys/exec_ecoff.h>
+#endif
+
+int __fdnlist(int, struct nlist *);
+int __aout_fdnlist(int, struct nlist *);
+int __ecoff_fdnlist(int, struct nlist *);
+int __elf_fdnlist(int, struct nlist *);
+#ifdef _NLIST_DO_ELF
+int __elf_is_okay__(register Elf_Ehdr *ehdr);
+#endif
+
+#define ISLAST(p) (p->n_un.n_name == 0 || p->n_un.n_name[0] == 0)
+
+#ifdef _NLIST_DO_AOUT
+int
+__aout_fdnlist(fd, list)
+ register int fd;
+ register struct nlist *list;
+{
+ register struct nlist *p, *s;
+ register char *strtab;
+ register off_t symoff, stroff;
+ register u_long symsize;
+ register int nent, cc;
+ int strsize, usemalloc = 0;
+ struct nlist nbuf[1024];
+ struct exec exec;
+
+ if (pread(fd, &exec, sizeof(exec), (off_t)0) != sizeof(exec) ||
+ N_BADMAG(exec) || exec.a_syms == NULL)
+ return (-1);
+
+ stroff = N_STROFF(exec);
+ symoff = N_SYMOFF(exec);
+ symsize = exec.a_syms;
+
+ /* Read in the size of the string table. */
+ if (pread(fd, (void *)&strsize, sizeof(strsize), stroff) !=
+ sizeof(strsize))
+ return (-1);
+ else
+ stroff += sizeof(strsize);
+
+ /*
+ * Read in the string table. We try mmap, but that will fail
+ * for /dev/ksyms so fall back on malloc. Since OpenBSD's malloc(3)
+ * returns memory to the system on free this does not cause bloat.
+ */
+ strsize -= sizeof(strsize);
+ strtab = mmap(NULL, (size_t)strsize, PROT_READ, MAP_SHARED|MAP_FILE,
+ fd, stroff);
+ if (strtab == MAP_FAILED) {
+ usemalloc = 1;
+ if ((strtab = (char *)malloc(strsize)) == NULL)
+ return (-1);
+ errno = EIO;
+ if (pread(fd, strtab, strsize, stroff) != strsize) {
+ nent = -1;
+ goto aout_done;
+ }
+ }
+
+ /*
+ * clean out any left-over information for all valid entries.
+ * Type and value defined to be 0 if not found; historical
+ * versions cleared other and desc as well. Also figure out
+ * the largest string length so don't read any more of the
+ * string table than we have to.
+ *
+ * XXX clearing anything other than n_type and n_value violates
+ * the semantics given in the man page.
+ */
+ nent = 0;
+ for (p = list; !ISLAST(p); ++p) {
+ p->n_type = 0;
+ p->n_other = 0;
+ p->n_desc = 0;
+ p->n_value = 0;
+ ++nent;
+ }
+
+ while (symsize > 0) {
+ cc = MIN(symsize, sizeof(nbuf));
+ if (pread(fd, nbuf, cc, symoff) != cc)
+ break;
+ symsize -= cc;
+ symoff += cc;
+ for (s = nbuf; cc > 0; ++s, cc -= sizeof(*s)) {
+ char *sname = strtab + s->n_un.n_strx - sizeof(int);
+
+ if (s->n_un.n_strx == 0 || (s->n_type & N_STAB) != 0)
+ continue;
+ for (p = list; !ISLAST(p); p++) {
+ char *pname = p->n_un.n_name;
+
+ if (*sname != '_' && *pname == '_')
+ pname++;
+ if (!strcmp(sname, pname)) {
+ p->n_value = s->n_value;
+ p->n_type = s->n_type;
+ p->n_desc = s->n_desc;
+ p->n_other = s->n_other;
+ if (--nent <= 0)
+ break;
+ }
+ }
+ }
+ }
+aout_done:
+ if (usemalloc)
+ free(strtab);
+ else
+ munmap(strtab, strsize);
+ return (nent);
+}
+#endif /* _NLIST_DO_AOUT */
+
+#ifdef _NLIST_DO_ECOFF
+#define check(off, size) ((off < 0) || (off + size > mappedsize))
+#define BAD do { rv = -1; goto out; } while (0)
+#define BADUNMAP do { rv = -1; goto unmap; } while (0)
+
+int
+__ecoff_fdnlist(fd, list)
+ register int fd;
+ register struct nlist *list;
+{
+ struct nlist *p;
+ struct ecoff_exechdr *exechdrp;
+ struct ecoff_symhdr *symhdrp;
+ struct ecoff_extsym *esyms;
+ struct stat st;
+ char *mappedfile;
+ size_t mappedsize;
+ u_long symhdroff, extstroff;
+ u_int symhdrsize;
+ int rv, nent;
+ long i, nesyms;
+
+ rv = -3;
+
+ if (fstat(fd, &st) < 0)
+ BAD;
+ if (st.st_size > SIZE_T_MAX) {
+ errno = EFBIG;
+ BAD;
+ }
+ mappedsize = st.st_size;
+ mappedfile = mmap(NULL, mappedsize, PROT_READ, MAP_SHARED|MAP_FILE,
+ fd, 0);
+ if (mappedfile == MAP_FAILED)
+ BAD;
+
+ if (check(0, sizeof *exechdrp))
+ BADUNMAP;
+ exechdrp = (struct ecoff_exechdr *)&mappedfile[0];
+
+ if (ECOFF_BADMAG(exechdrp))
+ BADUNMAP;
+
+ symhdroff = exechdrp->f.f_symptr;
+ symhdrsize = exechdrp->f.f_nsyms;
+
+ if (check(symhdroff, sizeof *symhdrp) ||
+ sizeof *symhdrp != symhdrsize)
+ BADUNMAP;
+ symhdrp = (struct ecoff_symhdr *)&mappedfile[symhdroff];
+
+ nesyms = symhdrp->esymMax;
+ if (check(symhdrp->cbExtOffset, nesyms * sizeof *esyms))
+ BADUNMAP;
+ esyms = (struct ecoff_extsym *)&mappedfile[symhdrp->cbExtOffset];
+ extstroff = symhdrp->cbSsExtOffset;
+
+ /*
+ * clean out any left-over information for all valid entries.
+ * Type and value defined to be 0 if not found; historical
+ * versions cleared other and desc as well.
+ *
+ * XXX clearing anything other than n_type and n_value violates
+ * the semantics given in the man page.
+ */
+ nent = 0;
+ for (p = list; !ISLAST(p); ++p) {
+ p->n_type = 0;
+ p->n_other = 0;
+ p->n_desc = 0;
+ p->n_value = 0;
+ ++nent;
+ }
+
+ for (i = 0; i < nesyms; i++) {
+ for (p = list; !ISLAST(p); p++) {
+ char *nlistname;
+ char *symtabname;
+
+ nlistname = p->n_un.n_name;
+ if (*nlistname == '_')
+ nlistname++;
+ symtabname =
+ &mappedfile[extstroff + esyms[i].es_strindex];
+
+ if (!strcmp(symtabname, nlistname)) {
+ p->n_value = esyms[i].es_value;
+ p->n_type = N_EXT; /* XXX */
+ p->n_desc = 0; /* XXX */
+ p->n_other = 0; /* XXX */
+ if (--nent <= 0)
+ break;
+ }
+ }
+ }
+ rv = nent;
+
+unmap:
+ munmap(mappedfile, mappedsize);
+out:
+ return (rv);
+}
+#endif /* _NLIST_DO_ECOFF */
+
+#ifdef _NLIST_DO_ELF
+/*
+ * __elf_is_okay__ - Determine if ehdr really
+ * is ELF and valid for the target platform.
+ *
+ * WARNING: This is NOT a ELF ABI function and
+ * as such it's use should be restricted.
+ */
+int
+__elf_is_okay__(ehdr)
+ register Elf_Ehdr *ehdr;
+{
+ register int retval = 0;
+ /*
+ * We need to check magic, class size, endianess,
+ * and version before we look at the rest of the
+ * Elf_Ehdr structure. These few elements are
+ * represented in a machine independent fashion.
+ */
+ if ((IS_ELF(*ehdr) || IS_OLF(*ehdr)) &&
+ ehdr->e_ident[EI_CLASS] == ELF_TARG_CLASS &&
+ ehdr->e_ident[EI_DATA] == ELF_TARG_DATA &&
+ ehdr->e_ident[EI_VERSION] == ELF_TARG_VER) {
+
+ /* Now check the machine dependant header */
+ if (ehdr->e_machine == ELF_TARG_MACH &&
+ ehdr->e_version == ELF_TARG_VER)
+ retval = 1;
+ }
+
+ return retval;
+}
+
+int
+__elf_fdnlist(fd, list)
+ register int fd;
+ register struct nlist *list;
+{
+ register struct nlist *p;
+ register caddr_t strtab;
+ register Elf_Off symoff = 0, symstroff = 0;
+ register Elf_Word symsize = 0, symstrsize = 0;
+ register Elf_Sword nent, cc, i;
+ Elf_Sym sbuf[1024];
+ Elf_Sym *s;
+ Elf_Ehdr ehdr;
+ Elf_Shdr *shdr = NULL;
+ Elf_Word shdr_size;
+ struct stat st;
+ int usemalloc = 0;
+
+ /* Make sure obj is OK */
+ if (pread(fd, &ehdr, sizeof(Elf_Ehdr), (off_t)0) != sizeof(Elf_Ehdr) ||
+ /* !__elf_is_okay__(&ehdr) || */ fstat(fd, &st) < 0)
+ return (-1);
+
+ /* calculate section header table size */
+ shdr_size = ehdr.e_shentsize * ehdr.e_shnum;
+
+ /* Make sure it's not too big to mmap */
+ if (shdr_size > SIZE_T_MAX) {
+ errno = EFBIG;
+ return (-1);
+ }
+
+ /* mmap section header table */
+ shdr = (Elf_Shdr *)mmap(NULL, (size_t)shdr_size, PROT_READ,
+ MAP_SHARED|MAP_FILE, fd, (off_t) ehdr.e_shoff);
+ if (shdr == MAP_FAILED) {
+ usemalloc = 1;
+ if ((shdr = malloc(shdr_size)) == NULL)
+ return (-1);
+ if (pread(fd, shdr, shdr_size, ehdr.e_shoff) != shdr_size) {
+ free(shdr);
+ return (-1);
+ }
+ }
+
+ /*
+ * Find the symbol table entry and it's corresponding
+ * string table entry. Version 1.1 of the ABI states
+ * that there is only one symbol table but that this
+ * could change in the future.
+ */
+ for (i = 0; i < ehdr.e_shnum; i++) {
+ if (shdr[i].sh_type == SHT_SYMTAB) {
+ symoff = shdr[i].sh_offset;
+ symsize = shdr[i].sh_size;
+ symstroff = shdr[shdr[i].sh_link].sh_offset;
+ symstrsize = shdr[shdr[i].sh_link].sh_size;
+ break;
+ }
+ }
+
+ /* Flush the section header table */
+ if (usemalloc)
+ free(shdr);
+ else
+ munmap((caddr_t)shdr, shdr_size);
+
+ /* Check for files too large to mmap. */
+ /* XXX is this really possible? */
+ if (symstrsize > SIZE_T_MAX) {
+ errno = EFBIG;
+ return (-1);
+ }
+ /*
+ * Map string table into our address space. This gives us
+ * an easy way to randomly access all the strings, without
+ * making the memory allocation permanent as with malloc/free
+ * (i.e., munmap will return it to the system).
+ */
+ if (usemalloc) {
+ if ((strtab = malloc(symstrsize)) == NULL)
+ return (-1);
+ if (pread(fd, strtab, symstrsize, symstroff) != symstrsize) {
+ free(strtab);
+ return (-1);
+ }
+ } else {
+ strtab = mmap(NULL, (size_t)symstrsize, PROT_READ,
+ MAP_SHARED|MAP_FILE, fd, (off_t) symstroff);
+ if (strtab == MAP_FAILED)
+ return (-1);
+ }
+ /*
+ * clean out any left-over information for all valid entries.
+ * Type and value defined to be 0 if not found; historical
+ * versions cleared other and desc as well. Also figure out
+ * the largest string length so don't read any more of the
+ * string table than we have to.
+ *
+ * XXX clearing anything other than n_type and n_value violates
+ * the semantics given in the man page.
+ */
+ nent = 0;
+ for (p = list; !ISLAST(p); ++p) {
+ p->n_type = 0;
+ p->n_other = 0;
+ p->n_desc = 0;
+ p->n_value = 0;
+ ++nent;
+ }
+
+ /* Don't process any further if object is stripped. */
+ /* ELFism - dunno if stripped by looking at header */
+ if (symoff == 0)
+ goto elf_done;
+
+ while (symsize > 0) {
+ cc = MIN(symsize, sizeof(sbuf));
+ if (pread(fd, sbuf, cc, symoff) != cc)
+ break;
+ symsize -= cc;
+ symoff += cc;
+ for (s = sbuf; cc > 0; ++s, cc -= sizeof(*s)) {
+ int soff = s->st_name;
+
+ if (soff == 0)
+ continue;
+ for (p = list; !ISLAST(p); p++) {
+ char *sym;
+
+ /*
+ * First we check for the symbol as it was
+ * provided by the user. If that fails,
+ * skip the first char if it's an '_' and
+ * try again.
+ * XXX - What do we do when the user really
+ * wants '_foo' and the are symbols
+ * for both 'foo' and '_foo' in the
+ * table and 'foo' is first?
+ */
+ sym = p->n_un.n_name;
+ if (strcmp(&strtab[soff], sym) != 0 &&
+ ((sym[0] == '_') &&
+ strcmp(&strtab[soff], sym + 1) != 0))
+ continue;
+
+ p->n_value = s->st_value;
+
+ /* XXX - type conversion */
+ /* is pretty rude. */
+ switch(ELF_ST_TYPE(s->st_info)) {
+ case STT_NOTYPE:
+ switch (s->st_shndx) {
+ case SHN_UNDEF:
+ p->n_type = N_UNDF;
+ break;
+ case SHN_ABS:
+ p->n_type = N_ABS;
+ break;
+ case SHN_COMMON:
+ p->n_type = N_COMM;
+ break;
+ default:
+ p->n_type = N_COMM | N_EXT;
+ break;
+ }
+ break;
+ case STT_OBJECT:
+ p->n_type = N_DATA;
+ break;
+ case STT_FUNC:
+ p->n_type = N_TEXT;
+ break;
+ case STT_FILE:
+ p->n_type = N_FN;
+ break;
+ }
+ if (ELF_ST_BIND(s->st_info) ==
+ STB_LOCAL)
+ p->n_type = N_EXT;
+ p->n_desc = 0;
+ p->n_other = 0;
+ if (--nent <= 0)
+ break;
+ }
+ }
+ }
+elf_done:
+ if (usemalloc)
+ free(strtab);
+ else
+ munmap(strtab, symstrsize);
+ return (nent);
+}
+#endif /* _NLIST_DO_ELF */
+
+
+static struct nlist_handlers {
+ int (*fn)(int fd, struct nlist *list);
+} nlist_fn[] = {
+#ifdef _NLIST_DO_AOUT
+ { __aout_fdnlist },
+#endif
+#ifdef _NLIST_DO_ELF
+ { __elf_fdnlist },
+#endif
+#ifdef _NLIST_DO_ECOFF
+ { __ecoff_fdnlist },
+#endif
+};
+
+int
+__fdnlist(fd, list)
+ register int fd;
+ register struct nlist *list;
+{
+ int n = -1, i;
+
+ for (i = 0; i < sizeof(nlist_fn)/sizeof(nlist_fn[0]); i++) {
+ n = (nlist_fn[i].fn)(fd, list);
+ if (n != -1)
+ break;
+ }
+ return (n);
+}
+
+
+int
+nlist(name, list)
+ const char *name;
+ struct nlist *list;
+{
+ int fd, n;
+
+ fd = open(name, O_RDONLY, 0);
+ if (fd < 0)
+ return (-1);
+ n = __fdnlist(fd, list);
+ (void)close(fd);
+ return (n);
+}