diff options
Diffstat (limited to 'sys/boot/common')
30 files changed, 8156 insertions, 0 deletions
diff --git a/sys/boot/common/Makefile.inc b/sys/boot/common/Makefile.inc new file mode 100644 index 0000000..3f171a3d --- /dev/null +++ b/sys/boot/common/Makefile.inc @@ -0,0 +1,36 @@ +# $FreeBSD$ + +SRCS+= bcache.c boot.c commands.c console.c devopen.c interp.c +SRCS+= interp_backslash.c interp_parse.c ls.c misc.c +SRCS+= module.c panic.c + +.if ${MACHINE_ARCH} == "i386" || ${MACHINE_ARCH} == "amd64" +SRCS+= load_elf32.c load_elf64.c +.endif +.if ${MACHINE_ARCH} == "powerpc" +SRCS+= load_elf32.c +.endif +.if ${MACHINE_ARCH} == "sparc64" || ${MACHINE_ARCH} == "ia64" || ${MACHINE_ARCH} == "alpha" +SRCS+= load_elf64.c +.endif + +.if defined(LOADER_NET_SUPPORT) +SRCS+= dev_net.c +.endif + +# Machine-independant ISA PnP +.if HAVE_ISABUS +SRCS+= isapnp.c +.endif +.if HAVE_PNP +SRCS+= pnp.c +.endif + +# Forth interpreter +.if BOOT_FORTH +SRCS+= interp_forth.c +MAN+= ../forth/loader.conf.5 +MAN+= ../forth/loader.4th.8 +.endif + +MAN+= loader.8 diff --git a/sys/boot/common/bcache.c b/sys/boot/common/bcache.c new file mode 100644 index 0000000..d61fbf0 --- /dev/null +++ b/sys/boot/common/bcache.c @@ -0,0 +1,347 @@ +/*- + * Copyright (c) 1998 Michael Smith <msmith@freebsd.org> + * 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 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 AUTHOR 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. + */ + +#include <sys/cdefs.h> +__FBSDID("$FreeBSD$"); + +/* + * Simple LRU block cache + */ + +#include <stand.h> +#include <string.h> +#include <bitstring.h> + +#include "bootstrap.h" + +/* #define BCACHE_DEBUG */ + +#ifdef BCACHE_DEBUG +#define BCACHE_TIMEOUT 10 +# define DEBUG(fmt, args...) printf("%s: " fmt "\n" , __func__ , ## args) +#else +#define BCACHE_TIMEOUT 2 +# define DEBUG(fmt, args...) +#endif + + +struct bcachectl +{ + daddr_t bc_blkno; + time_t bc_stamp; + int bc_count; +}; + +static struct bcachectl *bcache_ctl; +static caddr_t bcache_data; +static bitstr_t *bcache_miss; +static u_int bcache_nblks; +static u_int bcache_blksize; +static u_int bcache_hits, bcache_misses, bcache_ops, bcache_bypasses; +static u_int bcache_flushes; +static u_int bcache_bcount; + +static void bcache_invalidate(daddr_t blkno); +static void bcache_insert(caddr_t buf, daddr_t blkno); +static int bcache_lookup(caddr_t buf, daddr_t blkno); + +/* + * Initialise the cache for (nblks) of (bsize). + */ +int +bcache_init(u_int nblks, size_t bsize) +{ + /* discard any old contents */ + if (bcache_data != NULL) { + free(bcache_data); + bcache_data = NULL; + free(bcache_ctl); + } + + /* Allocate control structures */ + bcache_nblks = nblks; + bcache_blksize = bsize; + bcache_data = malloc(bcache_nblks * bcache_blksize); + bcache_ctl = (struct bcachectl *)malloc(bcache_nblks * sizeof(struct bcachectl)); + bcache_miss = bit_alloc((bcache_nblks + 1) / 2); + if ((bcache_data == NULL) || (bcache_ctl == NULL) || (bcache_miss == NULL)) { + if (bcache_miss) + free(bcache_miss); + if (bcache_ctl) + free(bcache_ctl); + if (bcache_data) + free(bcache_data); + bcache_data = NULL; + return(ENOMEM); + } + + return(0); +} + +/* + * Flush the cache + */ +void +bcache_flush(void) +{ + u_int i; + + bcache_flushes++; + + /* Flush the cache */ + for (i = 0; i < bcache_nblks; i++) { + bcache_ctl[i].bc_count = -1; + bcache_ctl[i].bc_blkno = -1; + } +} + +/* + * Handle a write request; write directly to the disk, and populate the + * cache with the new values. + */ +static int +write_strategy(void *devdata, int unit, int rw, daddr_t blk, size_t size, + char *buf, size_t *rsize) +{ + struct bcache_devdata *dd = (struct bcache_devdata *)devdata; + daddr_t i, nblk; + int err; + + nblk = size / bcache_blksize; + + /* Invalidate the blocks being written */ + for (i = 0; i < nblk; i++) { + bcache_invalidate(blk + i); + } + + /* Write the blocks */ + err = dd->dv_strategy(dd->dv_devdata, rw, blk, size, buf, rsize); + + /* Populate the block cache with the new data */ + if (err == 0) { + for (i = 0; i < nblk; i++) { + bcache_insert(buf + (i * bcache_blksize),blk + i); + } + } + + return err; +} + +/* + * Handle a read request; fill in parts of the request that can + * be satisfied by the cache, use the supplied strategy routine to do + * device I/O and then use the I/O results to populate the cache. + */ +static int +read_strategy(void *devdata, int unit, int rw, daddr_t blk, size_t size, + char *buf, size_t *rsize) +{ + struct bcache_devdata *dd = (struct bcache_devdata *)devdata; + int p_size, result; + daddr_t p_blk, i, j, nblk; + caddr_t p_buf; + + nblk = size / bcache_blksize; + result = 0; + + /* Satisfy any cache hits up front */ + for (i = 0; i < nblk; i++) { + if (bcache_lookup(buf + (bcache_blksize * i), blk + i)) { + bit_set(bcache_miss, i); /* cache miss */ + bcache_misses++; + } else { + bit_clear(bcache_miss, i); /* cache hit */ + bcache_hits++; + } + } + + /* Go back and fill in any misses XXX optimise */ + p_blk = -1; + p_buf = NULL; + p_size = 0; + for (i = 0; i < nblk; i++) { + if (bit_test(bcache_miss, i)) { + /* miss, add to pending transfer */ + if (p_blk == -1) { + p_blk = blk + i; + p_buf = buf + (bcache_blksize * i); + p_size = 1; + } else { + p_size++; + } + } else if (p_blk != -1) { + /* hit, complete pending transfer */ + result = dd->dv_strategy(dd->dv_devdata, rw, p_blk, p_size * bcache_blksize, p_buf, NULL); + if (result != 0) + goto done; + for (j = 0; j < p_size; j++) + bcache_insert(p_buf + (j * bcache_blksize), p_blk + j); + p_blk = -1; + } + } + if (p_blk != -1) { + /* pending transfer left */ + result = dd->dv_strategy(dd->dv_devdata, rw, p_blk, p_size * bcache_blksize, p_buf, NULL); + if (result != 0) + goto done; + for (j = 0; j < p_size; j++) + bcache_insert(p_buf + (j * bcache_blksize), p_blk + j); + } + + done: + if ((result == 0) && (rsize != NULL)) + *rsize = size; + return(result); +} + +/* + * Requests larger than 1/2 the cache size will be bypassed and go + * directly to the disk. XXX tune this. + */ +int +bcache_strategy(void *devdata, int unit, int rw, daddr_t blk, size_t size, + char *buf, size_t *rsize) +{ + static int bcache_unit = -1; + struct bcache_devdata *dd = (struct bcache_devdata *)devdata; + + bcache_ops++; + + if(bcache_unit != unit) { + bcache_flush(); + bcache_unit = unit; + } + + /* bypass large requests, or when the cache is inactive */ + if ((bcache_data == NULL) || ((size * 2 / bcache_blksize) > bcache_nblks)) { + DEBUG("bypass %d from %d", size / bcache_blksize, blk); + bcache_bypasses++; + return(dd->dv_strategy(dd->dv_devdata, rw, blk, size, buf, rsize)); + } + + switch (rw) { + case F_READ: + return read_strategy(devdata, unit, rw, blk, size, buf, rsize); + case F_WRITE: + return write_strategy(devdata, unit, rw, blk, size, buf, rsize); + } + return -1; +} + + +/* + * Insert a block into the cache. Retire the oldest block to do so, if required. + * + * XXX the LRU algorithm will fail after 2^31 blocks have been transferred. + */ +static void +bcache_insert(caddr_t buf, daddr_t blkno) +{ + time_t now; + int cand, ocount; + u_int i; + + time(&now); + cand = 0; /* assume the first block */ + ocount = bcache_ctl[0].bc_count; + + /* find the oldest block */ + for (i = 1; i < bcache_nblks; i++) { + if (bcache_ctl[i].bc_blkno == blkno) { + /* reuse old entry */ + cand = i; + break; + } + if (bcache_ctl[i].bc_count < ocount) { + ocount = bcache_ctl[i].bc_count; + cand = i; + } + } + + DEBUG("insert blk %d -> %d @ %d # %d", blkno, cand, now, bcache_bcount); + bcopy(buf, bcache_data + (bcache_blksize * cand), bcache_blksize); + bcache_ctl[cand].bc_blkno = blkno; + bcache_ctl[cand].bc_stamp = now; + bcache_ctl[cand].bc_count = bcache_bcount++; +} + +/* + * Look for a block in the cache. Blocks more than BCACHE_TIMEOUT seconds old + * may be stale (removable media) and thus are discarded. Copy the block out + * if successful and return zero, or return nonzero on failure. + */ +static int +bcache_lookup(caddr_t buf, daddr_t blkno) +{ + time_t now; + u_int i; + + time(&now); + + for (i = 0; i < bcache_nblks; i++) + /* cache hit? */ + if ((bcache_ctl[i].bc_blkno == blkno) && ((bcache_ctl[i].bc_stamp + BCACHE_TIMEOUT) >= now)) { + bcopy(bcache_data + (bcache_blksize * i), buf, bcache_blksize); + DEBUG("hit blk %d <- %d (now %d then %d)", blkno, i, now, bcache_ctl[i].bc_stamp); + return(0); + } + return(ENOENT); +} + +/* + * Invalidate a block from the cache. + */ +static void +bcache_invalidate(daddr_t blkno) +{ + u_int i; + + for (i = 0; i < bcache_nblks; i++) { + if (bcache_ctl[i].bc_blkno == blkno) { + bcache_ctl[i].bc_count = -1; + bcache_ctl[i].bc_blkno = -1; + DEBUG("invalidate blk %d", blkno); + break; + } + } +} + +COMMAND_SET(bcachestat, "bcachestat", "get disk block cache stats", command_bcache); + +static int +command_bcache(int argc, char *argv[]) +{ + u_int i; + + for (i = 0; i < bcache_nblks; i++) { + printf("%08x %04x %04x|", bcache_ctl[i].bc_blkno, (unsigned int)bcache_ctl[i].bc_stamp & 0xffff, bcache_ctl[i].bc_count & 0xffff); + if (((i + 1) % 4) == 0) + printf("\n"); + } + printf("\n%d ops %d bypasses %d hits %d misses %d flushes\n", bcache_ops, bcache_bypasses, bcache_hits, bcache_misses, bcache_flushes); + return(CMD_OK); +} + diff --git a/sys/boot/common/boot.c b/sys/boot/common/boot.c new file mode 100644 index 0000000..766a8cf --- /dev/null +++ b/sys/boot/common/boot.c @@ -0,0 +1,353 @@ +/*- + * Copyright (c) 1998 Michael Smith <msmith@freebsd.org> + * 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 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 AUTHOR 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. + */ + +#include <sys/cdefs.h> +__FBSDID("$FreeBSD$"); + +/* + * Loading modules, booting the system + */ + +#include <stand.h> +#include <string.h> + +#include "bootstrap.h" + +static char *getbootfile(int try); +static int loadakernel(int try, int argc, char* argv[]); + +/* List of kernel names to try (may be overwritten by boot.config) XXX should move from here? */ +static const char *default_bootfiles = "kernel"; + +static int autoboot_tried; + +/* + * The user wants us to boot. + */ +COMMAND_SET(boot, "boot", "boot a file or loaded kernel", command_boot); + +static int +command_boot(int argc, char *argv[]) +{ + struct preloaded_file *fp; + + /* + * See if the user has specified an explicit kernel to boot. + */ + if ((argc > 1) && (argv[1][0] != '-')) { + + /* XXX maybe we should discard everything and start again? */ + if (file_findfile(NULL, NULL) != NULL) { + sprintf(command_errbuf, "can't boot '%s', kernel module already loaded", argv[1]); + return(CMD_ERROR); + } + + /* find/load the kernel module */ + if (mod_loadkld(argv[1], argc - 2, argv + 2) != 0) + return(CMD_ERROR); + /* we have consumed all arguments */ + argc = 1; + } + + /* + * See if there is a kernel module already loaded + */ + if (file_findfile(NULL, NULL) == NULL) + if (loadakernel(0, argc - 1, argv + 1)) + /* we have consumed all arguments */ + argc = 1; + + /* + * Loaded anything yet? + */ + if ((fp = file_findfile(NULL, NULL)) == NULL) { + command_errmsg = "no bootable kernel"; + return(CMD_ERROR); + } + + /* + * If we were given arguments, discard any previous. + * XXX should we merge arguments? Hard to DWIM. + */ + if (argc > 1) { + if (fp->f_args != NULL) + free(fp->f_args); + fp->f_args = unargv(argc - 1, argv + 1); + } + + /* Hook for platform-specific autoloading of modules */ + if (archsw.arch_autoload() != 0) + return(CMD_ERROR); + + /* Call the exec handler from the loader matching the kernel */ + file_formats[fp->f_loader]->l_exec(fp); + return(CMD_ERROR); +} + + +/* + * Autoboot after a delay + */ + +COMMAND_SET(autoboot, "autoboot", "boot automatically after a delay", command_autoboot); + +static int +command_autoboot(int argc, char *argv[]) +{ + int howlong; + char *cp, *prompt; + + prompt = NULL; + howlong = -1; + switch(argc) { + case 3: + prompt = argv[2]; + /* FALLTHROUGH */ + case 2: + howlong = strtol(argv[1], &cp, 0); + if (*cp != 0) { + sprintf(command_errbuf, "bad delay '%s'", argv[1]); + return(CMD_ERROR); + } + /* FALLTHROUGH */ + case 1: + return(autoboot(howlong, prompt)); + } + + command_errmsg = "too many arguments"; + return(CMD_ERROR); +} + +/* + * Called before we go interactive. If we think we can autoboot, and + * we haven't tried already, try now. + */ +void +autoboot_maybe() +{ + char *cp; + + cp = getenv("autoboot_delay"); + if ((autoboot_tried == 0) && ((cp == NULL) || strcasecmp(cp, "NO"))) + autoboot(-1, NULL); /* try to boot automatically */ +} + +int +autoboot(int timeout, char *prompt) +{ + time_t when, otime, ntime; + int c, yes; + char *argv[2], *cp, *ep; + char *kernelname; + + autoboot_tried = 1; + + if (timeout == -1) { + /* try to get a delay from the environment */ + if ((cp = getenv("autoboot_delay"))) { + timeout = strtol(cp, &ep, 0); + if (cp == ep) + timeout = -1; + } + } + if (timeout == -1) /* all else fails */ + timeout = 10; + + kernelname = getenv("kernelname"); + if (kernelname == NULL) { + argv[0] = NULL; + loadakernel(0, 0, argv); + kernelname = getenv("kernelname"); + if (kernelname == NULL) { + command_errmsg = "no valid kernel found"; + return(CMD_ERROR); + } + } + + otime = time(NULL); + when = otime + timeout; /* when to boot */ + yes = 0; + + printf("%s\n", (prompt == NULL) ? "Hit [Enter] to boot immediately, or any other key for command prompt." : prompt); + + for (;;) { + if (ischar()) { + c = getchar(); + if ((c == '\r') || (c == '\n')) + yes = 1; + break; + } + ntime = time(NULL); + if (ntime >= when) { + yes = 1; + break; + } + + if (ntime != otime) { + printf("\rBooting [%s] in %d second%s... ", + kernelname, (int)(when - ntime), + (when-ntime)==1?"":"s"); + otime = ntime; + } + } + if (yes) + printf("\rBooting [%s]... ", kernelname); + putchar('\n'); + if (yes) { + argv[0] = "boot"; + argv[1] = NULL; + return(command_boot(1, argv)); + } + return(CMD_OK); +} + +/* + * Scrounge for the name of the (try)'th file we will try to boot. + */ +static char * +getbootfile(int try) +{ + static char *name = NULL; + const char *spec, *ep; + size_t len; + + /* we use dynamic storage */ + if (name != NULL) { + free(name); + name = NULL; + } + + /* + * Try $bootfile, then try our builtin default + */ + if ((spec = getenv("bootfile")) == NULL) + spec = default_bootfiles; + + while ((try > 0) && (spec != NULL)) { + spec = strchr(spec, ';'); + if (spec) + spec++; /* skip over the leading ';' */ + try--; + } + if (spec != NULL) { + if ((ep = strchr(spec, ';')) != NULL) { + len = ep - spec; + } else { + len = strlen(spec); + } + name = malloc(len + 1); + strncpy(name, spec, len); + name[len] = 0; + } + if (name && name[0] == 0) { + free(name); + name = NULL; + } + return(name); +} + +/* + * Try to find the /etc/fstab file on the filesystem (rootdev), + * which should be be the root filesystem, and parse it to find + * out what the kernel ought to think the root filesystem is. + * + * If we're successful, set vfs.root.mountfrom to <vfstype>:<path> + * so that the kernel can tell both which VFS and which node to use + * to mount the device. If this variable's already set, don't + * overwrite it. + */ +int +getrootmount(char *rootdev) +{ + char lbuf[128], *cp, *ep, *dev, *fstyp; + int fd, error; + + if (getenv("vfs.root.mountfrom") != NULL) + return(0); + + sprintf(lbuf, "%s/etc/fstab", rootdev); + if ((fd = open(lbuf, O_RDONLY)) < 0) + return(1); + + /* loop reading lines from /etc/fstab What was that about sscanf again? */ + error = 1; + while (fgetstr(lbuf, sizeof(lbuf), fd) >= 0) { + if ((lbuf[0] == 0) || (lbuf[0] == '#')) + continue; + + /* skip device name */ + for (cp = lbuf; (*cp != 0) && !isspace(*cp); cp++) + ; + if (*cp == 0) /* misformatted */ + continue; + /* delimit and save */ + *cp++ = 0; + dev = strdup(lbuf); + + /* skip whitespace up to mountpoint */ + while ((*cp != 0) && isspace(*cp)) + cp++; + /* must have /<space> to be root */ + if ((*cp == 0) || (*cp != '/') || !isspace(*(cp + 1))) + continue; + /* skip whitespace up to fstype */ + cp += 2; + while ((*cp != 0) && isspace(*cp)) + cp++; + if (*cp == 0) /* misformatted */ + continue; + /* skip text to end of fstype and delimit */ + ep = cp; + while ((*cp != 0) && !isspace(*cp)) + cp++; + *cp = 0; + fstyp = strdup(ep); + + /* build the final result and save it */ + sprintf(lbuf, "%s:%s", fstyp, dev); + free(dev); + free(fstyp); + setenv("vfs.root.mountfrom", lbuf, 0); + error = 0; + break; + } + close(fd); + return(error); +} + +static int +loadakernel(int try, int argc, char* argv[]) +{ + char *cp; + + for (try = 0; (cp = getbootfile(try)) != NULL; try++) + if (mod_loadkld(cp, argc - 1, argv + 1) != 0) + printf("can't load '%s'\n", cp); + else + return 1; + return 0; +} + diff --git a/sys/boot/common/bootstrap.h b/sys/boot/common/bootstrap.h new file mode 100644 index 0000000..6e17be7 --- /dev/null +++ b/sys/boot/common/bootstrap.h @@ -0,0 +1,287 @@ +/*- + * Copyright (c) 1998 Michael Smith <msmith@freebsd.org> + * 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 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 AUTHOR 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. + * + * $FreeBSD$ + */ + +#include <sys/types.h> +#include <sys/queue.h> +#include <sys/linker_set.h> + +/* + * Generic device specifier; architecture-dependant + * versions may be larger, but should be allowed to + * overlap. + */ +struct devdesc +{ + struct devsw *d_dev; + int d_type; +#define DEVT_NONE 0 +#define DEVT_DISK 1 +#define DEVT_NET 2 +#define DEVT_CD 3 +}; + +/* Commands and return values; nonzero return sets command_errmsg != NULL */ +typedef int (bootblk_cmd_t)(int argc, char *argv[]); +extern char *command_errmsg; +extern char command_errbuf[]; /* XXX blah, length */ +#define CMD_OK 0 +#define CMD_ERROR 1 + +/* interp.c */ +void interact(void); +int include(const char *filename); + +/* interp_backslash.c */ +char *backslash(char *str); + +/* interp_parse.c */ +int parse(int *argc, char ***argv, char *str); + +/* interp_forth.c */ +void bf_init(void); +int bf_run(char *line); + +/* boot.c */ +int autoboot(int timeout, char *prompt); +void autoboot_maybe(void); +int getrootmount(char *rootdev); + +/* misc.c */ +char *unargv(int argc, char *argv[]); +void hexdump(caddr_t region, size_t len); +size_t strlenout(vm_offset_t str); +char *strdupout(vm_offset_t str); + +/* bcache.c */ +int bcache_init(u_int nblks, size_t bsize); +void bcache_flush(void); +int bcache_strategy(void *devdata, int unit, int rw, daddr_t blk, + size_t size, char *buf, size_t *rsize); + +/* + * Disk block cache + */ +struct bcache_devdata +{ + int (*dv_strategy)(void *devdata, int rw, daddr_t blk, size_t size, char *buf, size_t *rsize); + void *dv_devdata; +}; + +/* + * Modular console support. + */ +struct console +{ + const char *c_name; + const char *c_desc; + int c_flags; +#define C_PRESENTIN (1<<0) +#define C_PRESENTOUT (1<<1) +#define C_ACTIVEIN (1<<2) +#define C_ACTIVEOUT (1<<3) + void (* c_probe)(struct console *cp); /* set c_flags to match hardware */ + int (* c_init)(int arg); /* reinit XXX may need more args */ + void (* c_out)(int c); /* emit c */ + int (* c_in)(void); /* wait for and return input */ + int (* c_ready)(void); /* return nonzer if input waiting */ +}; +extern struct console *consoles[]; +void cons_probe(void); + +/* + * Plug-and-play enumerator/configurator interface. + */ +struct pnphandler +{ + const char *pp_name; /* handler/bus name */ + void (* pp_enumerate)(void); /* enumerate PnP devices, add to chain */ +}; + +struct pnpident +{ + char *id_ident; /* ASCII identifier, actual format varies with bus/handler */ + STAILQ_ENTRY(pnpident) id_link; +}; + +struct pnpinfo +{ + char *pi_desc; /* ASCII description, optional */ + int pi_revision; /* optional revision (or -1) if not supported */ + char *pi_module; /* module/args nominated to handle device */ + int pi_argc; /* module arguments */ + char **pi_argv; + struct pnphandler *pi_handler; /* handler which detected this device */ + STAILQ_HEAD(,pnpident) pi_ident; /* list of identifiers */ + STAILQ_ENTRY(pnpinfo) pi_link; +}; + +STAILQ_HEAD(pnpinfo_stql, pnpinfo); + +extern struct pnpinfo_stql pnp_devices; + +extern struct pnphandler *pnphandlers[]; /* provided by MD code */ + +void pnp_addident(struct pnpinfo *pi, char *ident); +struct pnpinfo *pnp_allocinfo(void); +void pnp_freeinfo(struct pnpinfo *pi); +void pnp_addinfo(struct pnpinfo *pi); +char *pnp_eisaformat(u_int8_t *data); + +/* + * < 0 - No ISA in system + * == 0 - Maybe ISA, search for read data port + * > 0 - ISA in system, value is read data port address + */ +extern int isapnp_readport; + +/* + * Preloaded file metadata header. + * + * Metadata are allocated on our heap, and copied into kernel space + * before executing the kernel. + */ +struct file_metadata +{ + size_t md_size; + u_int16_t md_type; + struct file_metadata *md_next; + char md_data[1]; /* data are immediately appended */ +}; + +struct preloaded_file; +struct mod_depend; + +struct kernel_module +{ + char *m_name; /* module name */ + int m_version; /* module version */ +/* char *m_args;*/ /* arguments for the module */ + struct preloaded_file *m_fp; + struct kernel_module *m_next; +}; + +/* + * Preloaded file information. Depending on type, file can contain + * additional units called 'modules'. + * + * At least one file (the kernel) must be loaded in order to boot. + * The kernel is always loaded first. + * + * String fields (m_name, m_type) should be dynamically allocated. + */ +struct preloaded_file +{ + char *f_name; /* file name */ + char *f_type; /* verbose file type, eg 'ELF kernel', 'pnptable', etc. */ + char *f_args; /* arguments for the file */ + struct file_metadata *f_metadata; /* metadata that will be placed in the module directory */ + int f_loader; /* index of the loader that read the file */ + vm_offset_t f_addr; /* load address */ + size_t f_size; /* file size */ + struct kernel_module *f_modules; /* list of modules if any */ + struct preloaded_file *f_next; /* next file */ +}; + +struct file_format +{ + /* Load function must return EFTYPE if it can't handle the module supplied */ + int (* l_load)(char *filename, u_int64_t dest, struct preloaded_file **result); + /* Only a loader that will load a kernel (first module) should have an exec handler */ + int (* l_exec)(struct preloaded_file *mp); +}; + +extern struct file_format *file_formats[]; /* supplied by consumer */ +extern struct preloaded_file *preloaded_files; + +int mod_load(char *name, struct mod_depend *verinfo, int argc, char *argv[]); +int mod_loadkld(const char *name, int argc, char *argv[]); + +struct preloaded_file *file_alloc(void); +struct preloaded_file *file_findfile(char *name, char *type); +struct file_metadata *file_findmetadata(struct preloaded_file *fp, int type); +void file_discard(struct preloaded_file *fp); +void file_addmetadata(struct preloaded_file *fp, int type, size_t size, void *p); +int file_addmodule(struct preloaded_file *fp, char *modname, int version, + struct kernel_module **newmp); + + +/* MI module loaders */ +#ifdef __elfN +int __elfN(loadfile)(char *filename, u_int64_t dest, struct preloaded_file **result); +#endif + +/* + * Support for commands + */ +struct bootblk_command +{ + const char *c_name; + const char *c_desc; + bootblk_cmd_t *c_fn; +}; + +#define COMMAND_SET(tag, key, desc, func) \ + static bootblk_cmd_t func; \ + static struct bootblk_command _cmd_ ## tag = { key, desc, func }; \ + DATA_SET(Xcommand_set, _cmd_ ## tag) + +SET_DECLARE(Xcommand_set, struct bootblk_command); + +/* + * The intention of the architecture switch is to provide a convenient + * encapsulation of the interface between the bootstrap MI and MD code. + * MD code may selectively populate the switch at runtime based on the + * actual configuration of the target system. + */ +struct arch_switch +{ + /* Automatically load modules as required by detected hardware */ + int (*arch_autoload)(void); + /* Locate the device for (name), return pointer to tail in (*path) */ + int (*arch_getdev)(void **dev, const char *name, const char **path); + /* Copy from local address space to module address space, similar to bcopy() */ + ssize_t (*arch_copyin)(const void *src, vm_offset_t dest, + const size_t len); + /* Copy to local address space from module address space, similar to bcopy() */ + ssize_t (*arch_copyout)(const vm_offset_t src, void *dest, + const size_t len); + /* Read from file to module address space, same semantics as read() */ + ssize_t (*arch_readin)(const int fd, vm_offset_t dest, + const size_t len); + /* Perform ISA byte port I/O (only for systems with ISA) */ + int (*arch_isainb)(int port); + void (*arch_isaoutb)(int port, int value); +}; +extern struct arch_switch archsw; + +/* This must be provided by the MD code, but should it be in the archsw? */ +void delay(int delay); + +void dev_cleanup(void); + +time_t time(time_t *tloc); diff --git a/sys/boot/common/commands.c b/sys/boot/common/commands.c new file mode 100644 index 0000000..38aa0a3 --- /dev/null +++ b/sys/boot/common/commands.c @@ -0,0 +1,485 @@ +/*- + * Copyright (c) 1998 Michael Smith <msmith@freebsd.org> + * 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 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 AUTHOR 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. + */ + +#include <sys/cdefs.h> +__FBSDID("$FreeBSD$"); + +#include <stand.h> +#include <string.h> + +#include "bootstrap.h" + +char *command_errmsg; +char command_errbuf[256]; /* XXX should have procedural interface for setting, size limit? */ + +static int page_file(char *filename); + +/* + * Help is read from a formatted text file. + * + * Entries in the file are formatted as + +# Ttopic [Ssubtopic] Ddescription +help +text +here +# + + * + * Note that for code simplicity's sake, the above format must be followed + * exactly. + * + * Subtopic entries must immediately follow the topic (this is used to + * produce the listing of subtopics). + * + * If no argument(s) are supplied by the user, the help for 'help' is displayed. + */ +COMMAND_SET(help, "help", "detailed help", command_help); + +static int +help_getnext(int fd, char **topic, char **subtopic, char **desc) +{ + char line[81], *cp, *ep; + + for (;;) { + if (fgetstr(line, 80, fd) < 0) + return(0); + + if ((strlen(line) < 3) || (line[0] != '#') || (line[1] != ' ')) + continue; + + *topic = *subtopic = *desc = NULL; + cp = line + 2; + while((cp != NULL) && (*cp != 0)) { + ep = strchr(cp, ' '); + if ((*cp == 'T') && (*topic == NULL)) { + if (ep != NULL) + *ep++ = 0; + *topic = strdup(cp + 1); + } else if ((*cp == 'S') && (*subtopic == NULL)) { + if (ep != NULL) + *ep++ = 0; + *subtopic = strdup(cp + 1); + } else if (*cp == 'D') { + *desc = strdup(cp + 1); + ep = NULL; + } + cp = ep; + } + if (*topic == NULL) { + if (*subtopic != NULL) + free(*subtopic); + if (*desc != NULL) + free(*desc); + continue; + } + return(1); + } +} + +static void +help_emitsummary(char *topic, char *subtopic, char *desc) +{ + int i; + + pager_output(" "); + pager_output(topic); + i = strlen(topic); + if (subtopic != NULL) { + pager_output(" "); + pager_output(subtopic); + i += strlen(subtopic) + 1; + } + if (desc != NULL) { + do { + pager_output(" "); + } while (i++ < 30); + pager_output(desc); + } + pager_output("\n"); +} + + +static int +command_help(int argc, char *argv[]) +{ + char buf[81]; /* XXX buffer size? */ + int hfd, matched, doindex; + char *topic, *subtopic, *t, *s, *d; + + /* page the help text from our load path */ + sprintf(buf, "%s/boot/loader.help", getenv("loaddev")); + if ((hfd = open(buf, O_RDONLY)) < 0) { + printf("Verbose help not available, use '?' to list commands\n"); + return(CMD_OK); + } + + /* pick up request from arguments */ + topic = subtopic = NULL; + switch(argc) { + case 3: + subtopic = strdup(argv[2]); + case 2: + topic = strdup(argv[1]); + break; + case 1: + topic = strdup("help"); + break; + default: + command_errmsg = "usage is 'help <topic> [<subtopic>]"; + return(CMD_ERROR); + } + + /* magic "index" keyword */ + doindex = !strcmp(topic, "index"); + matched = doindex; + + /* Scan the helpfile looking for help matching the request */ + pager_open(); + while(help_getnext(hfd, &t, &s, &d)) { + + if (doindex) { /* dink around formatting */ + help_emitsummary(t, s, d); + + } else if (strcmp(topic, t)) { + /* topic mismatch */ + if(matched) /* nothing more on this topic, stop scanning */ + break; + + } else { + /* topic matched */ + matched = 1; + if (((subtopic == NULL) && (s == NULL)) || + ((subtopic != NULL) && (s != NULL) && !strcmp(subtopic, s))) { + /* exact match, print text */ + while((fgetstr(buf, 80, hfd) >= 0) && (buf[0] != '#')) { + if (pager_output(buf)) + break; + if (pager_output("\n")) + break; + } + } else if ((subtopic == NULL) && (s != NULL)) { + /* topic match, list subtopics */ + help_emitsummary(t, s, d); + } + } + free(t); + free(s); + free(d); + } + pager_close(); + close(hfd); + if (!matched) { + sprintf(command_errbuf, "no help available for '%s'", topic); + free(topic); + if (subtopic) + free(subtopic); + return(CMD_ERROR); + } + free(topic); + if (subtopic) + free(subtopic); + return(CMD_OK); +} + + +COMMAND_SET(commandlist, "?", "list commands", command_commandlist); + +static int +command_commandlist(int argc, char *argv[]) +{ + struct bootblk_command **cmdp; + + printf("Available commands:\n"); + SET_FOREACH(cmdp, Xcommand_set) { + if (((*cmdp)->c_name != NULL) && ((*cmdp)->c_desc != NULL)) + printf(" %-15s %s\n", (*cmdp)->c_name, (*cmdp)->c_desc); + } + return(CMD_OK); +} + +/* + * XXX set/show should become set/echo if we have variable + * substitution happening. + */ + +COMMAND_SET(show, "show", "show variable(s)", command_show); + +static int +command_show(int argc, char *argv[]) +{ + struct env_var *ev; + char *cp; + + if (argc < 2) { + /* + * With no arguments, print everything. + */ + pager_open(); + for (ev = environ; ev != NULL; ev = ev->ev_next) { + pager_output(ev->ev_name); + cp = getenv(ev->ev_name); + if (cp != NULL) { + pager_output("="); + pager_output(cp); + } + if (pager_output("\n")) + break; + } + pager_close(); + } else { + if ((cp = getenv(argv[1])) != NULL) { + printf("%s\n", cp); + } else { + sprintf(command_errbuf, "variable '%s' not found", argv[1]); + return(CMD_ERROR); + } + } + return(CMD_OK); +} + +COMMAND_SET(set, "set", "set a variable", command_set); + +static int +command_set(int argc, char *argv[]) +{ + int err; + + if (argc != 2) { + command_errmsg = "wrong number of arguments"; + return(CMD_ERROR); + } else { + if ((err = putenv(argv[1])) != 0) { + command_errmsg = strerror(err); + return(CMD_ERROR); + } + } + return(CMD_OK); +} + +COMMAND_SET(unset, "unset", "unset a variable", command_unset); + +static int +command_unset(int argc, char *argv[]) +{ + int err; + + if (argc != 2) { + command_errmsg = "wrong number of arguments"; + return(CMD_ERROR); + } else { + if ((err = unsetenv(argv[1])) != 0) { + command_errmsg = strerror(err); + return(CMD_ERROR); + } + } + return(CMD_OK); +} + +COMMAND_SET(echo, "echo", NULL, command_echo); + +static int +command_echo(int argc, char *argv[]) +{ + char *s; + int nl, ch; + + nl = 0; + optind = 1; + optreset = 1; + while ((ch = getopt(argc, argv, "n")) != -1) { + switch(ch) { + case 'n': + nl = 1; + break; + case '?': + default: + /* getopt has already reported an error */ + return(CMD_OK); + } + } + argv += (optind); + argc -= (optind); + + s = unargv(argc, argv); + if (s != NULL) { + printf("%s", s); + free(s); + } + if (!nl) + printf("\n"); + return(CMD_OK); +} + +/* + * A passable emulation of the sh(1) command of the same name. + */ + +COMMAND_SET(read, "read", NULL, command_read); + +static int +command_read(int argc, char *argv[]) +{ + char *prompt; + int timeout; + time_t when; + char *cp; + char *name; + char buf[256]; /* XXX size? */ + int c; + + timeout = -1; + prompt = NULL; + optind = 1; + optreset = 1; + while ((c = getopt(argc, argv, "p:t:")) != -1) { + switch(c) { + + case 'p': + prompt = optarg; + break; + case 't': + timeout = strtol(optarg, &cp, 0); + if (cp == optarg) { + sprintf(command_errbuf, "bad timeout '%s'", optarg); + return(CMD_ERROR); + } + break; + default: + return(CMD_OK); + } + } + + argv += (optind); + argc -= (optind); + name = (argc > 0) ? argv[0]: NULL; + + if (prompt != NULL) + printf("%s", prompt); + if (timeout >= 0) { + when = time(NULL) + timeout; + while (!ischar()) + if (time(NULL) >= when) + return(CMD_OK); /* is timeout an error? */ + } + + ngets(buf, sizeof(buf)); + + if (name != NULL) + setenv(name, buf, 1); + return(CMD_OK); +} + +/* + * File pager + */ +COMMAND_SET(more, "more", "show contents of a file", command_more); + +static int +command_more(int argc, char *argv[]) +{ + int i; + int res; + char line[80]; + + res=0; + pager_open(); + for (i = 1; (i < argc) && (res == 0); i++) { + sprintf(line, "*** FILE %s BEGIN ***\n", argv[i]); + if (pager_output(line)) + break; + res = page_file(argv[i]); + if (!res) { + sprintf(line, "*** FILE %s END ***\n", argv[i]); + res = pager_output(line); + } + } + pager_close(); + + if (res == 0) + return CMD_OK; + else + return CMD_ERROR; +} + +static int +page_file(char *filename) +{ + int result; + + result = pager_file(filename); + + if (result == -1) + sprintf(command_errbuf, "error showing %s", filename); + + return result; +} + +/* + * List all disk-like devices + */ +COMMAND_SET(lsdev, "lsdev", "list all devices", command_lsdev); + +static int +command_lsdev(int argc, char *argv[]) +{ + int verbose, ch, i; + char line[80]; + + verbose = 0; + optind = 1; + optreset = 1; + while ((ch = getopt(argc, argv, "v")) != -1) { + switch(ch) { + case 'v': + verbose = 1; + break; + case '?': + default: + /* getopt has already reported an error */ + return(CMD_OK); + } + } + argv += (optind); + argc -= (optind); + + pager_open(); + for (i = 0; devsw[i] != NULL; i++) { + if (devsw[i]->dv_print != NULL){ + sprintf(line, "%s devices:\n", devsw[i]->dv_name); + if (pager_output(line)) + break; + devsw[i]->dv_print(verbose); + } else { + sprintf(line, "%s: (unknown)\n", devsw[i]->dv_name); + if (pager_output(line)) + break; + } + } + pager_close(); + return(CMD_OK); +} + diff --git a/sys/boot/common/console.c b/sys/boot/common/console.c new file mode 100644 index 0000000..0fc138c --- /dev/null +++ b/sys/boot/common/console.c @@ -0,0 +1,173 @@ +/*- + * Copyright (c) 1998 Michael Smith <msmith@freebsd.org> + * 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 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 AUTHOR 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. + */ + +#include <sys/cdefs.h> +__FBSDID("$FreeBSD$"); + +#include <stand.h> +#include <string.h> + +#include "bootstrap.h" +/* + * Core console support + */ + +static int cons_set(struct env_var *ev, int flags, void *value); +static int cons_find(char *name); + +/* + * Detect possible console(s) to use. The first probed console + * is marked active. Also create the console variable. + * + * XXX Add logic for multiple console support. + */ +void +cons_probe(void) +{ + int cons; + int active; + char *prefconsole; + + /* Do all console probes */ + for (cons = 0; consoles[cons] != NULL; cons++) { + consoles[cons]->c_flags = 0; + consoles[cons]->c_probe(consoles[cons]); + } + /* Now find the first working one */ + active = -1; + for (cons = 0; consoles[cons] != NULL && active == -1; cons++) { + consoles[cons]->c_flags = 0; + consoles[cons]->c_probe(consoles[cons]); + if (consoles[cons]->c_flags == (C_PRESENTIN | C_PRESENTOUT)) + active = cons; + } + + /* Check to see if a console preference has already been registered */ + prefconsole = getenv("console"); + if (prefconsole != NULL) + prefconsole = strdup(prefconsole); + if (prefconsole != NULL) { + unsetenv("console"); /* we want to replace this */ + for (cons = 0; consoles[cons] != NULL; cons++) + /* look for the nominated console, use it if it's functional */ + if (!strcmp(prefconsole, consoles[cons]->c_name) && + (consoles[cons]->c_flags == (C_PRESENTIN | C_PRESENTOUT))) + active = cons; + free(prefconsole); + } + if (active == -1) + active = 0; + consoles[active]->c_flags |= (C_ACTIVEIN | C_ACTIVEOUT); + consoles[active]->c_init(0); + + printf("Console: %s\n", consoles[active]->c_desc); + env_setenv("console", EV_VOLATILE, consoles[active]->c_name, cons_set, + env_nounset); +} + +int +getchar(void) +{ + int cons; + int rv; + + /* Loop forever polling all active consoles */ + for(;;) + for (cons = 0; consoles[cons] != NULL; cons++) + if ((consoles[cons]->c_flags & C_ACTIVEIN) && + ((rv = consoles[cons]->c_in()) != -1)) + return(rv); +} + +int +ischar(void) +{ + int cons; + + for (cons = 0; consoles[cons] != NULL; cons++) + if ((consoles[cons]->c_flags & C_ACTIVEIN) && + (consoles[cons]->c_ready() != 0)) + return(1); + return(0); +} + +void +putchar(int c) +{ + int cons; + + /* Expand newlines */ + if (c == '\n') + putchar('\r'); + + for (cons = 0; consoles[cons] != NULL; cons++) + if (consoles[cons]->c_flags & C_ACTIVEOUT) + consoles[cons]->c_out(c); +} + +static int +cons_find(char *name) +{ + int cons; + + for (cons = 0; consoles[cons] != NULL; cons++) + if (!strcmp(consoles[cons]->c_name, name)) + return(cons); + return(-1); +} + + +/* + * Select a console. + * + * XXX Note that the console system design allows for some extension + * here (eg. multiple consoles, input/output only, etc.) + */ +static int +cons_set(struct env_var *ev, int flags, void *value) +{ + int cons, active; + + if ((value == NULL) || ((active = cons_find(value)) == -1)) { + if (value != NULL) + printf("no such console '%s'\n", (char *)value); + printf("Available consoles:\n"); + for (cons = 0; consoles[cons] != NULL; cons++) + printf(" %s\n", consoles[cons]->c_name); + return(CMD_ERROR); + } + + /* disable all current consoles */ + for (cons = 0; consoles[cons] != NULL; cons++) + consoles[cons]->c_flags &= ~(C_ACTIVEIN | C_ACTIVEOUT); + + /* enable selected console */ + consoles[active]->c_flags |= C_ACTIVEIN | C_ACTIVEOUT; + consoles[active]->c_init(0); + + env_setenv(ev->ev_name, flags | EV_NOHOOK, value, NULL, NULL); + return(CMD_OK); +} diff --git a/sys/boot/common/dev_net.c b/sys/boot/common/dev_net.c new file mode 100644 index 0000000..b7ccbca --- /dev/null +++ b/sys/boot/common/dev_net.c @@ -0,0 +1,307 @@ +/* + * $NetBSD: dev_net.c,v 1.12 1997/12/10 20:38:37 gwr Exp $ + */ + +/*- + * Copyright (c) 1997 The NetBSD Foundation, Inc. + * All rights reserved. + * + * This code is derived from software contributed to The NetBSD Foundation + * by Gordon W. Ross. + * + * 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 the NetBSD + * Foundation, Inc. and its contributors. + * 4. Neither the name of The NetBSD Foundation 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 NETBSD FOUNDATION, INC. 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 FOUNDATION 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. + */ + +#include <sys/cdefs.h> +__FBSDID("$FreeBSD$"); + +/*- + * This module implements a "raw device" interface suitable for + * use by the stand-alone I/O library NFS code. This interface + * does not support any "block" access, and exists only for the + * purpose of initializing the network interface, getting boot + * parameters, and performing the NFS mount. + * + * At open time, this does: + * + * find interface - netif_open() + * RARP for IP address - rarp_getipaddress() + * RPC/bootparams - callrpc(d, RPC_BOOTPARAMS, ...) + * RPC/mountd - nfs_mount(sock, ip, path) + * + * the root file handle from mountd is saved in a global + * for use by the NFS open code (NFS/lookup). + */ + +#include <machine/stdarg.h> +#include <sys/param.h> +#include <sys/socket.h> +#include <net/if.h> +#include <netinet/in.h> +#include <netinet/in_systm.h> + +#include <stand.h> +#include <string.h> +#include <net.h> +#include <netif.h> +#include <bootp.h> +#include <bootparam.h> + +#include "dev_net.h" +#include "bootstrap.h" + +int debug = 0; + +static int netdev_sock = -1; +static int netdev_opens; + +static int net_init(void); +static int net_open(struct open_file *, ...); +static int net_close(struct open_file *); +static int net_strategy(); +static void net_print(int); + +static int net_getparams(int sock); + +struct devsw netdev = { + "net", + DEVT_NET, + net_init, + net_strategy, + net_open, + net_close, + noioctl, + net_print +}; + +int +net_init(void) +{ + return 0; +} + +/* + * Called by devopen after it sets f->f_dev to our devsw entry. + * This opens the low-level device and sets f->f_devdata. + * This is declared with variable arguments... + */ +int +net_open(struct open_file *f, ...) +{ + va_list args; + char *devname; /* Device part of file name (or NULL). */ + int error = 0; + + va_start(args, f); + devname = va_arg(args, char*); + va_end(args); + + /* On first open, do netif open, mount, etc. */ + if (netdev_opens == 0) { + /* Find network interface. */ + if (netdev_sock < 0) { + netdev_sock = netif_open(devname); + if (netdev_sock < 0) { + printf("net_open: netif_open() failed\n"); + return (ENXIO); + } + if (debug) + printf("net_open: netif_open() succeeded\n"); + } + if (rootip.s_addr == 0) { + /* Get root IP address, and path, etc. */ + error = net_getparams(netdev_sock); + if (error) { + /* getparams makes its own noise */ + netif_close(netdev_sock); + netdev_sock = -1; + return (error); + } + } + netdev_opens++; + } + netdev_opens++; + f->f_devdata = &netdev_sock; + return (error); +} + +int +net_close(f) + struct open_file *f; +{ + +#ifdef NETIF_DEBUG + if (debug) + printf("net_close: opens=%d\n", netdev_opens); +#endif + + /* On last close, do netif close, etc. */ + f->f_devdata = NULL; + /* Extra close call? */ + if (netdev_opens <= 0) + return (0); + netdev_opens--; + /* Not last close? */ + if (netdev_opens > 0) + return(0); + rootip.s_addr = 0; + if (netdev_sock >= 0) { + if (debug) + printf("net_close: calling netif_close()\n"); + netif_close(netdev_sock); + netdev_sock = -1; + } + return (0); +} + +int +net_strategy() +{ + return EIO; +} + +#define SUPPORT_BOOTP + +/* + * Get info for NFS boot: our IP address, our hostname, + * server IP address, and our root path on the server. + * There are two ways to do this: The old, Sun way, + * and the more modern, BOOTP way. (RFC951, RFC1048) + * + * The default is to use the Sun bootparams RPC + * (because that is what the kernel will do). + * MD code can make try_bootp initialied data, + * which will override this common definition. + */ +#ifdef SUPPORT_BOOTP +int try_bootp = 1; +#endif + +extern n_long ip_convertaddr(char *p); + +static int +net_getparams(sock) + int sock; +{ + char buf[MAXHOSTNAMELEN]; + char temp[FNAME_SIZE]; + struct iodesc *d; + int i; + n_long smask; + +#ifdef SUPPORT_BOOTP + /* + * Try to get boot info using BOOTP. If we succeed, then + * the server IP address, gateway, and root path will all + * be initialized. If any remain uninitialized, we will + * use RARP and RPC/bootparam (the Sun way) to get them. + */ + if (try_bootp) + bootp(sock, BOOTP_NONE); + if (myip.s_addr != 0) + goto exit; + if (debug) + printf("net_open: BOOTP failed, trying RARP/RPC...\n"); +#endif + + /* + * Use RARP to get our IP address. This also sets our + * netmask to the "natural" default for our address. + */ + if (rarp_getipaddress(sock)) { + printf("net_open: RARP failed\n"); + return (EIO); + } + printf("net_open: client addr: %s\n", inet_ntoa(myip)); + + /* Get our hostname, server IP address, gateway. */ + if (bp_whoami(sock)) { + printf("net_open: bootparam/whoami RPC failed\n"); + return (EIO); + } + printf("net_open: client name: %s\n", hostname); + + /* + * Ignore the gateway from whoami (unreliable). + * Use the "gateway" parameter instead. + */ + smask = 0; + gateip.s_addr = 0; + if (bp_getfile(sock, "gateway", &gateip, buf) == 0) { + /* Got it! Parse the netmask. */ + smask = ip_convertaddr(buf); + } + if (smask) { + netmask = smask; + printf("net_open: subnet mask: %s\n", intoa(netmask)); + } + if (gateip.s_addr) + printf("net_open: net gateway: %s\n", inet_ntoa(gateip)); + + /* Get the root server and pathname. */ + if (bp_getfile(sock, "root", &rootip, rootpath)) { + printf("net_open: bootparam/getfile RPC failed\n"); + return (EIO); + } + exit: + /* + * If present, strip the server's address off of the rootpath + * before passing it along. This allows us to be compatible with + * the kernel's diskless (BOOTP_NFSROOT) booting conventions + */ + for (i = 0; rootpath[i] != '\0' && i < FNAME_SIZE; i++) + if (rootpath[i] == ':') + break; + if (i && i != FNAME_SIZE && rootpath[i] == ':') { + rootpath[i++] = '\0'; + if (inet_addr(&rootpath[0]) != INADDR_NONE) + rootip.s_addr = inet_addr(&rootpath[0]); + bcopy(&rootpath[i], &temp[0], strlen(&rootpath[i])+1); + bcopy(&temp[0], &rootpath[0], strlen(&rootpath[i])+1); + } + printf("net_open: server addr: %s\n", inet_ntoa(rootip)); + printf("net_open: server path: %s\n", rootpath); + + d = socktodesc(sock); + sprintf(temp, "%6D", d->myea, ":"); + setenv("boot.netif.ip", inet_ntoa(myip), 1); + setenv("boot.netif.netmask", intoa(netmask), 1); + setenv("boot.netif.gateway", inet_ntoa(gateip), 1); + setenv("boot.netif.hwaddr", temp, 1); + setenv("boot.nfsroot.server", inet_ntoa(rootip), 1); + setenv("boot.nfsroot.path", rootpath, 1); + + return (0); +} + +static void +net_print(int verbose) +{ + return; +} diff --git a/sys/boot/common/dev_net.h b/sys/boot/common/dev_net.h new file mode 100644 index 0000000..3b3d107 --- /dev/null +++ b/sys/boot/common/dev_net.h @@ -0,0 +1,30 @@ +/*- + * Copyright (c) 1998 Doug Rabson <dfr@freebsd.org> + * 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 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 AUTHOR 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. + * + * $FreeBSD$ + */ + +extern struct devsw netdev; + diff --git a/sys/boot/common/devopen.c b/sys/boot/common/devopen.c new file mode 100644 index 0000000..28a2fab --- /dev/null +++ b/sys/boot/common/devopen.c @@ -0,0 +1,61 @@ +/*- + * Copyright (c) 1998 Michael Smith <msmith@freebsd.org> + * 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 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 AUTHOR 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. + */ + +#include <sys/cdefs.h> +__FBSDID("$FreeBSD$"); + +#include <stand.h> +#include <string.h> + +#include "bootstrap.h" + +int +devopen(struct open_file *f, const char *fname, const char **file) +{ + struct devdesc *dev; + int result; + + if ((result = archsw.arch_getdev((void *)&dev, fname, file)) == 0) { /* get the device */ + /* point to device-specific data so that device open can use it */ + f->f_devdata = dev; + if ((result = dev->d_dev->dv_open(f, dev)) == 0) { /* try to open it */ + /* reference the devsw entry from the open_file structure */ + f->f_dev = dev->d_dev; + } else { + free(dev); /* release the device descriptor */ + } + } + return(result); +} + +int +devclose(struct open_file *f) +{ + if (f->f_devdata != NULL) { + free(f->f_devdata); + } + return(0); +} diff --git a/sys/boot/common/help.common b/sys/boot/common/help.common new file mode 100644 index 0000000..a6fd748 --- /dev/null +++ b/sys/boot/common/help.common @@ -0,0 +1,341 @@ +################################################################################ +# Thelp DDisplay command help + + help [topic [subtopic]] + ? + + The help command displays help on commands and their usage. + + In command help, a term enclosed with <...> indicates a value as + described by the term. A term enclosed with [...] is optional, + and may not be required by all forms of the command. + + Some commands may not be available. Use the '?' command to list + most available commands. + + If needed, disable the use of ACPI with: + + unset acpi_load + $hint.acpi.0.disabled="1" + +################################################################################ +# Tautoboot DBoot after a delay + + autoboot [<delay> [<prompt>]] + + Displays <prompt> or a default prompt, and counts down <delay> seconds + before attempting to boot. If <delay> is not specified, the default + value is 10. + +################################################################################ +# Tboot DBoot immediately + + boot [<kernelname>] [-<arg> ...] + + Boot the system. If arguments are specified, they are added to the + arguments for the kernel. If <kernelname> is specified, and a kernel + has not already been loaded, it will be booted instead of the default + kernel. + +################################################################################ +# Techo DEcho arguments + + echo [-n] [<message>] + + Emits <message>, with no trailing newline if -n is specified. This is + most useful in conjunction with scripts and the '@' line prefix. + + Variables are substituted by prefixing them with $, eg. + + echo Current device is $currdev + + will print the current device. + +################################################################################ +# Tload DLoad a kernel or module + + load [-t <type>] <filename> + + Loads the module contained in <filename> into memory. If no other + modules are loaded, <filename> must be a kernel or the command will + fail. + + If -t is specified, the module is loaded as raw data of <type>, for + later use by the kernel or other modules. <type> may be any string. + +################################################################################ +# Tls DList files + + ls [-l] [<path>] + + Displays a listing of files in the directory <path>, or the root + directory of the current device if <path> is not specified. + + The -l argument displays file sizes as well; the process of obtaining + file sizes on some media may be very slow. + +################################################################################ +# Tlsdev DList devices + + lsdev [-v] + + List all of the devices from which it may be possible to load modules. + If -v is specified, print more details. + +################################################################################ +# Tlsmod DList modules + + lsmod [-v] + + List loaded modules. If [-v] is specified, print more details. + +################################################################################ +# Tmore DPage files + + more <filename> [<filename> ...] + + Show contents of text files. When displaying the contents of more, + than one file, if the user elects to quit displaying a file, the + remaining files will not be shown. + +################################################################################ +# Tpnpscan DScan for PnP devices + + pnpscan [-v] + + Scan for Plug-and-Play devices. This command is normally automatically + run as part of the boot process, in order to dynamically load modules + required for system operation. + + If the -v argument is specified, details on the devices found will + be printed. + +################################################################################ +# Tset DSet a variable + + set <variable name> + set <variable name>=<value> + + The set command is used to set variables. + +################################################################################ +# Tset Sautoboot_delay DSet the default autoboot delay + + set autoboot_delay=<value> + + Sets the default delay for the autoboot command to <value> seconds. + +################################################################################ +# Tset Sbootfile DSet the default boot file set + + set bootfile=<filename>[;<filename>...] + + Sets the default set of kernel boot filename(s). It may be overridden + by setting the bootfile variable to a semicolon-separated list of + filenames, each of which will be searched for in the module_path + directories. The default bootfile set is "kernel". + +################################################################################ +# Tset Sboot_askname DPrompt for root device + + set boot_askname + + Instructs the kernel to prompt the user for the name of the root device + when the kernel is booted. + +################################################################################ +# Tset Sboot_ddb DDrop to the kernel debugger (DDB) + + set boot_ddb + + Instructs the kernel to start in the DDB debugger, rather than + proceeding to initialise when booted. + +################################################################################ +# Tset Sboot_gdb DSelect gdb-remote mode + + set boot_gdb + + Selects gdb-remote mode for the kernel debugger by default. + +################################################################################ +# Tset Sboot_single DStart system in single-user mode + + set boot_single + + Prevents the kernel from initiating a multi-user startup, single-user + mode will be entered when the kernel has finished device probes. + +################################################################################ +# Tset Sboot_verbose DVerbose boot messages + + set boot_verbose + + Setting this variable causes extra debugging information to be printed + by the kernel during the boot phase. + +################################################################################ +# Tset Sconsole DSet the current console + + set console[=<value>] + + Sets the current console. If <value> is omitted, a list of valid + consoles will be displayed. + +################################################################################ +# Tset Scurrdev DSet the current device + + set currdev=<device> + + Selects the default device. Syntax for devices is odd. + +################################################################################ +# Tset Sinit_path DSet the list of init candidates + + set init_path=<path>[:<path>...] + + Sets the list of binaries which the kernel will try to run as initial + process. + + +################################################################################ +# Tset Smodule_path DSet the module search path + + set module_path=<path>[;<path>...] + + Sets the list of directories which will be searched in for modules + named in a load command or implicitly required by a dependency. The + default module_path is "/boot/kernel;/boot/modules". + +################################################################################ +# Tset Sprompt DSet the command prompt + + set prompt=<value> + + The command prompt is displayed when the loader is waiting for input. + Variable substitution is performed on the prompt. The default + prompt can be set with: + + set prompt=\$currdev> + +################################################################################ +# Tset Srootdev DSet the root filesystem + + set rootdev=<path> + + By default the value of $currdev is used to set the root filesystem + when the kernel is booted. This can be overridden by setting + $rootdev explicitly. + +################################################################################ +# Tset Stunables DSet kernel tunable values + + Various kernel tunable parameters can be overridden by specifying new + values in the environment. + + set kern.ipc.nmbclusters=<value> NMBCLUSTERS + + Set the number of mbuf clusters to be allocated. The value + cannot be set below the default determined when the kernel + was compiled. + + set kern.ipc.nsfbufs=<value> NSFBUFS + + Set the number of sendfile buffers to be allocated. This + overrides the value determined when the kernel was compiled. + + set kern.vm.kmem.size=<value> VM_KMEM_SIZE + + Sets the size of kernel memory (bytes). This overrides + the value determined when the kernel was compiled. + + set machdep.disable_mtrrs=1 + + Disable the use of i686 MTRRs (i386 only) + + set net.inet.tcp.tcbhashsize=<value> TCBHASHSIZE + + Overrides the compile-time set value of TCBHASHSIZE or + the preset default of 512. Must be a power of 2. + + hw.syscons.sc_no_suspend_vtswitch=<value> + + Disable VT switching on suspend. + + value is 0 (default) or non-zero to enable. + + set hw.physmem=<value> MAXMEM (i386 only) + + Limits the amount of physical memory space available to + the system to <value> bytes. <value> may have a k, M or G + suffix to indicate kilobytes, megabytes and gigabytes + respectively. Note that the current i386 architecture + limits this value to 4GB. + + On systems where memory cannot be accurately probed, + this option provides a hint as to the actual size of + system memory (which will be tested before use). + + set hw.pci.allow_unsupported_io_range=<value> + + Allow the PCI Bridge to pass through an unsupported + memory range assigned by the BIOS. + + value is 0 (default) or non-zero to enable. + + set hw.pci.enable_io_modes=<value> + + Enable PCI resources which are left off by some BIOSes + or are not enabled correctly by the device driver. + + value is 1 (default), but this may cause problems with + some peripherals. Set to 0 to disable. + +################################################################################ +# Tshow DShow the values of variables + + show [<variable>] + + Displays the value of <variable>, or all variables if not specified. + Multiple paths can be separated with a semicolon. + +################################################################################ +# Tinclude DRead commands from a script file + + include <filename> [<filename> ...] + + The entire contents of <filename> are read into memory before executing + commands, so it is safe to source a file from removable media. + +################################################################################ +# Tread DRead input from the terminal + + read [-t <value>] [-p <prompt>] [<variable name>] + + The read command reads a line of input from the terminal. If the + -t argument is specified, it will return nothing if no input has been + received after <value> seconds. (Any keypress will cancel the + timeout). + + If -p is specified, <prompt> is printed before reading input. No + newline is emitted after the prompt. + + If a variable name is supplied, the variable is set to the value read, + less any terminating newline. + +################################################################################ +# Tunload DRemove all modules from memory + + unload + + This command removes any kernel and all loaded modules from memory. + +################################################################################ +# Tunset DUnset a variable + + unset <variable name> + + If allowed, the named variable's value is discarded and the variable + is removed. + +################################################################################ diff --git a/sys/boot/common/interp.c b/sys/boot/common/interp.c new file mode 100644 index 0000000..33f48e6 --- /dev/null +++ b/sys/boot/common/interp.c @@ -0,0 +1,346 @@ +/*- + * Copyright (c) 1998 Michael Smith <msmith@freebsd.org> + * 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 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 AUTHOR 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. + */ + +#include <sys/cdefs.h> +__FBSDID("$FreeBSD$"); + +/* + * Simple commandline interpreter, toplevel and misc. + * + * XXX may be obsoleted by BootFORTH or some other, better, interpreter. + */ + +#include <stand.h> +#include <string.h> +#include "bootstrap.h" + +#ifdef BOOT_FORTH +#include "ficl.h" +#define RETURN(x) stackPushINT(bf_vm->pStack,!x); return(x) + +extern FICL_VM *bf_vm; +#else +#define RETURN(x) return(x) +#endif + +#define MAXARGS 20 /* maximum number of arguments allowed */ + +static void prompt(void); + +#ifndef BOOT_FORTH +static int perform(int argc, char *argv[]); + +/* + * Perform the command + */ +int +perform(int argc, char *argv[]) +{ + int result; + struct bootblk_command **cmdp; + bootblk_cmd_t *cmd; + + if (argc < 1) + return(CMD_OK); + + /* set return defaults; a successful command will override these */ + command_errmsg = command_errbuf; + strcpy(command_errbuf, "no error message"); + cmd = NULL; + result = CMD_ERROR; + + /* search the command set for the command */ + SET_FOREACH(cmdp, Xcommand_set) { + if (((*cmdp)->c_name != NULL) && !strcmp(argv[0], (*cmdp)->c_name)) + cmd = (*cmdp)->c_fn; + } + if (cmd != NULL) { + result = (cmd)(argc, argv); + } else { + command_errmsg = "unknown command"; + } + RETURN(result); +} +#endif /* ! BOOT_FORTH */ + +/* + * Interactive mode + */ +void +interact(void) +{ + char input[256]; /* big enough? */ +#ifndef BOOT_FORTH + int argc; + char **argv; +#endif + +#ifdef BOOT_FORTH + bf_init(); +#endif + + /* + * Read our default configuration + */ + if(include("/boot/loader.rc")!=CMD_OK) + include("/boot/boot.conf"); + printf("\n"); + /* + * Before interacting, we might want to autoboot. + */ + autoboot_maybe(); + + /* + * Not autobooting, go manual + */ + printf("\nType '?' for a list of commands, 'help' for more detailed help.\n"); + if (getenv("prompt") == NULL) + setenv("prompt", "${interpret}", 1); + if (getenv("interpret") == NULL) + setenv("interpret", "OK", 1); + + + for (;;) { + input[0] = '\0'; + prompt(); + ngets(input, sizeof(input)); +#ifdef BOOT_FORTH + bf_vm->sourceID.i = 0; + bf_run(input); +#else + if (!parse(&argc, &argv, input)) { + if (perform(argc, argv)) + printf("%s: %s\n", argv[0], command_errmsg); + free(argv); + } else { + printf("parse error\n"); + } +#endif + } +} + +/* + * Read commands from a file, then execute them. + * + * We store the commands in memory and close the source file so that the media + * holding it can safely go away while we are executing. + * + * Commands may be prefixed with '@' (so they aren't displayed) or '-' (so + * that the script won't stop if they fail). + */ +COMMAND_SET(include, "include", "read commands from a file", command_include); + +static int +command_include(int argc, char *argv[]) +{ + int i; + int res; + char **argvbuf; + + /* + * Since argv is static, we need to save it here. + */ + argvbuf = (char**) calloc((u_int)argc, sizeof(char*)); + for (i = 0; i < argc; i++) + argvbuf[i] = strdup(argv[i]); + + res=CMD_OK; + for (i = 1; (i < argc) && (res == CMD_OK); i++) + res = include(argvbuf[i]); + + for (i = 0; i < argc; i++) + free(argvbuf[i]); + free(argvbuf); + + return(res); +} + +struct includeline +{ + char *text; + int flags; + int line; +#define SL_QUIET (1<<0) +#define SL_IGNOREERR (1<<1) + struct includeline *next; +}; + +int +include(const char *filename) +{ + struct includeline *script, *se, *sp; + char input[256]; /* big enough? */ +#ifdef BOOT_FORTH + int res; + char *cp; + int prevsrcid, fd, line; +#else + int argc,res; + char **argv, *cp; + int fd, flags, line; +#endif + + if (((fd = open(filename, O_RDONLY)) == -1)) { + sprintf(command_errbuf,"can't open '%s': %s\n", filename, strerror(errno)); + return(CMD_ERROR); + } + + /* + * Read the script into memory. + */ + script = se = NULL; + line = 0; + + while (fgetstr(input, sizeof(input), fd) >= 0) { + line++; +#ifdef BOOT_FORTH + cp = input; +#else + flags = 0; + /* Discard comments */ + if (strncmp(input+strspn(input, " "), "\\ ", 2) == 0) + continue; + cp = input; + /* Echo? */ + if (input[0] == '@') { + cp++; + flags |= SL_QUIET; + } + /* Error OK? */ + if (input[0] == '-') { + cp++; + flags |= SL_IGNOREERR; + } +#endif + /* Allocate script line structure and copy line, flags */ + sp = malloc(sizeof(struct includeline) + strlen(cp) + 1); + sp->text = (char *)sp + sizeof(struct includeline); + strcpy(sp->text, cp); +#ifndef BOOT_FORTH + sp->flags = flags; +#endif + sp->line = line; + sp->next = NULL; + + if (script == NULL) { + script = sp; + } else { + se->next = sp; + } + se = sp; + } + close(fd); + + /* + * Execute the script + */ +#ifndef BOOT_FORTH + argv = NULL; +#else + prevsrcid = bf_vm->sourceID.i; + bf_vm->sourceID.i = fd; +#endif + res = CMD_OK; + for (sp = script; sp != NULL; sp = sp->next) { + +#ifdef BOOT_FORTH + res = bf_run(sp->text); + if (res != VM_OUTOFTEXT) { + sprintf(command_errbuf, "Error while including %s, in the line:\n%s", filename, sp->text); + res = CMD_ERROR; + break; + } else + res = CMD_OK; +#else + /* print if not being quiet */ + if (!(sp->flags & SL_QUIET)) { + prompt(); + printf("%s\n", sp->text); + } + + /* Parse the command */ + if (!parse(&argc, &argv, sp->text)) { + if ((argc > 0) && (perform(argc, argv) != 0)) { + /* normal command */ + printf("%s: %s\n", argv[0], command_errmsg); + if (!(sp->flags & SL_IGNOREERR)) { + res=CMD_ERROR; + break; + } + } + free(argv); + argv = NULL; + } else { + printf("%s line %d: parse error\n", filename, sp->line); + res=CMD_ERROR; + break; + } +#endif + } +#ifndef BOOT_FORTH + if (argv != NULL) + free(argv); +#else + bf_vm->sourceID.i = prevsrcid; +#endif + while(script != NULL) { + se = script; + script = script->next; + free(se); + } + return(res); +} + +/* + * Emit the current prompt; use the same syntax as the parser + * for embedding environment variables. + */ +static void +prompt(void) +{ + char *pr, *p, *cp, *ev; + + if ((cp = getenv("prompt")) == NULL) + cp = ">"; + pr = p = strdup(cp); + + while (*p != 0) { + if ((*p == '$') && (*(p+1) == '{')) { + for (cp = p + 2; (*cp != 0) && (*cp != '}'); cp++) + ; + *cp = 0; + ev = getenv(p + 2); + + if (ev != NULL) + printf("%s", ev); + p = cp + 1; + continue; + } + putchar(*p++); + } + putchar(' '); + free(pr); +} diff --git a/sys/boot/common/interp_backslash.c b/sys/boot/common/interp_backslash.c new file mode 100644 index 0000000..3cbdd5b --- /dev/null +++ b/sys/boot/common/interp_backslash.c @@ -0,0 +1,167 @@ +/*- + * 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. + * + * Jordan K. Hubbard + * 29 August 1998 + * + * Routine for doing backslash elimination. + */ + +#include <sys/cdefs.h> +__FBSDID("$FreeBSD$"); + +#include <stand.h> +#include <string.h> +#include "bootstrap.h" + +#define DIGIT(x) (isdigit(x) ? (x) - '0' : islower(x) ? (x) + 10 - 'a' : (x) + 10 - 'A') + +/* + * backslash: Return malloc'd copy of str with all standard "backslash + * processing" done on it. Original can be free'd if desired. + */ +char * +backslash(char *str) +{ + /* + * Remove backslashes from the strings. Turn \040 etc. into a single + * character (we allow eight bit values). Currently NUL is not + * allowed. + * + * Turn "\n" and "\t" into '\n' and '\t' characters. Etc. + * + */ + char *new_str; + int seenbs = 0; + int i = 0; + + if ((new_str = strdup(str)) == NULL) + return NULL; + + while (*str) { + if (seenbs) { + seenbs = 0; + switch (*str) { + case '\\': + new_str[i++] = '\\'; + str++; + break; + + /* preserve backslashed quotes, dollar signs */ + case '\'': + case '"': + case '$': + new_str[i++] = '\\'; + new_str[i++] = *str++; + break; + + case 'b': + new_str[i++] = '\b'; + str++; + break; + + case 'f': + new_str[i++] = '\f'; + str++; + break; + + case 'r': + new_str[i++] = '\r'; + str++; + break; + + case 'n': + new_str[i++] = '\n'; + str++; + break; + + case 's': + new_str[i++] = ' '; + str++; + break; + + case 't': + new_str[i++] = '\t'; + str++; + break; + + case 'v': + new_str[i++] = '\13'; + str++; + break; + + case 'z': + str++; + break; + + case '0': case '1': case '2': case '3': case '4': + case '5': case '6': case '7': case '8': case '9': { + char val; + + /* Three digit octal constant? */ + if (*str >= '0' && *str <= '3' && + *(str + 1) >= '0' && *(str + 1) <= '7' && + *(str + 2) >= '0' && *(str + 2) <= '7') { + + val = (DIGIT(*str) << 6) + (DIGIT(*(str + 1)) << 3) + + DIGIT(*(str + 2)); + + /* Allow null value if user really wants to shoot + at feet, but beware! */ + new_str[i++] = val; + str += 3; + break; + } + + /* One or two digit hex constant? + * If two are there they will both be taken. + * Use \z to split them up if this is not wanted. + */ + if (*str == '0' && + (*(str + 1) == 'x' || *(str + 1) == 'X') && + isxdigit(*(str + 2))) { + val = DIGIT(*(str + 2)); + if (isxdigit(*(str + 3))) { + val = (val << 4) + DIGIT(*(str + 3)); + str += 4; + } + else + str += 3; + /* Yep, allow null value here too */ + new_str[i++] = val; + break; + } + } + break; + + default: + new_str[i++] = *str++; + break; + } + } + else { + if (*str == '\\') { + seenbs = 1; + str++; + } + else + new_str[i++] = *str++; + } + } + + if (seenbs) { + /* + * The final character was a '\'. Put it in as a single backslash. + */ + new_str[i++] = '\\'; + } + new_str[i] = '\0'; + return new_str; +} diff --git a/sys/boot/common/interp_forth.c b/sys/boot/common/interp_forth.c new file mode 100644 index 0000000..cc66c4d --- /dev/null +++ b/sys/boot/common/interp_forth.c @@ -0,0 +1,307 @@ +/*- + * Copyright (c) 1998 Michael Smith <msmith@freebsd.org> + * 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 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 AUTHOR 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. + */ + +#include <sys/cdefs.h> +__FBSDID("$FreeBSD$"); + +#include <sys/param.h> /* to pick up __FreeBSD_version */ +#include <string.h> +#include <stand.h> +#include "bootstrap.h" +#include "ficl.h" + +extern char bootprog_rev[]; + +/* #define BFORTH_DEBUG */ + +#ifdef BFORTH_DEBUG +# define DEBUG(fmt, args...) printf("%s: " fmt "\n" , __func__ , ## args) +#else +# define DEBUG(fmt, args...) +#endif + +/* + * Eventually, all builtin commands throw codes must be defined + * elsewhere, possibly bootstrap.h. For now, just this code, used + * just in this file, it is getting defined. + */ +#define BF_PARSE 100 + +/* + * BootForth Interface to Ficl Forth interpreter. + */ + +FICL_SYSTEM *bf_sys; +FICL_VM *bf_vm; +FICL_WORD *pInterp; + +/* + * Shim for taking commands from BF and passing them out to 'standard' + * argv/argc command functions. + */ +static void +bf_command(FICL_VM *vm) +{ + char *name, *line, *tail, *cp; + size_t len; + struct bootblk_command **cmdp; + bootblk_cmd_t *cmd; + int nstrings, i; + int argc, result; + char **argv; + + /* Get the name of the current word */ + name = vm->runningWord->name; + + /* Find our command structure */ + cmd = NULL; + SET_FOREACH(cmdp, Xcommand_set) { + if (((*cmdp)->c_name != NULL) && !strcmp(name, (*cmdp)->c_name)) + cmd = (*cmdp)->c_fn; + } + if (cmd == NULL) + panic("callout for unknown command '%s'", name); + + /* Check whether we have been compiled or are being interpreted */ + if (stackPopINT(vm->pStack)) { + /* + * Get parameters from stack, in the format: + * an un ... a2 u2 a1 u1 n -- + * Where n is the number of strings, a/u are pairs of + * address/size for strings, and they will be concatenated + * in LIFO order. + */ + nstrings = stackPopINT(vm->pStack); + for (i = 0, len = 0; i < nstrings; i++) + len += stackFetch(vm->pStack, i * 2).i + 1; + line = malloc(strlen(name) + len + 1); + strcpy(line, name); + + if (nstrings) + for (i = 0; i < nstrings; i++) { + len = stackPopINT(vm->pStack); + cp = stackPopPtr(vm->pStack); + strcat(line, " "); + strncat(line, cp, len); + } + } else { + /* Get remainder of invocation */ + tail = vmGetInBuf(vm); + for (cp = tail, len = 0; cp != vm->tib.end && *cp != 0 && *cp != '\n'; cp++, len++) + ; + + line = malloc(strlen(name) + len + 2); + strcpy(line, name); + if (len > 0) { + strcat(line, " "); + strncat(line, tail, len); + vmUpdateTib(vm, tail + len); + } + } + DEBUG("cmd '%s'", line); + + command_errmsg = command_errbuf; + command_errbuf[0] = 0; + if (!parse(&argc, &argv, line)) { + result = (cmd)(argc, argv); + free(argv); + } else { + result=BF_PARSE; + } + free(line); + /* This is going to be thrown!!! */ + stackPushINT(vm->pStack,result); +} + +/* + * Replace a word definition (a builtin command) with another + * one that: + * + * - Throw error results instead of returning them on the stack + * - Pass a flag indicating whether the word was compiled or is + * being interpreted. + * + * There is one major problem with builtins that cannot be overcome + * in anyway, except by outlawing it. We want builtins to behave + * differently depending on whether they have been compiled or they + * are being interpreted. Notice that this is *not* the interpreter's + * current state. For example: + * + * : example ls ; immediate + * : problem example ; \ "ls" gets executed while compiling + * example \ "ls" gets executed while interpreting + * + * Notice that, though the current state is different in the two + * invocations of "example", in both cases "ls" has been + * *compiled in*, which is what we really want. + * + * The problem arises when you tick the builtin. For example: + * + * : example-1 ['] ls postpone literal ; immediate + * : example-2 example-1 execute ; immediate + * : problem example-2 ; + * example-2 + * + * We have no way, when we get EXECUTEd, of knowing what our behavior + * should be. Thus, our only alternative is to "outlaw" this. See RFI + * 0007, and ANS Forth Standard's appendix D, item 6.7 for a related + * problem, concerning compile semantics. + * + * The problem is compounded by the fact that "' builtin CATCH" is valid + * and desirable. The only solution is to create an intermediary word. + * For example: + * + * : my-ls ls ; + * : example ['] my-ls catch ; + * + * So, with the below implementation, here is a summary of the behavior + * of builtins: + * + * ls -l \ "interpret" behavior, ie, + * \ takes parameters from TIB + * : ex-1 s" -l" 1 ls ; \ "compile" behavior, ie, + * \ takes parameters from the stack + * : ex-2 ['] ls catch ; immediate \ undefined behavior + * : ex-3 ['] ls catch ; \ undefined behavior + * ex-2 ex-3 \ "interpret" behavior, + * \ catch works + * : ex-4 ex-2 ; \ "compile" behavior, + * \ catch does not work + * : ex-5 ex-3 ; immediate \ same as ex-2 + * : ex-6 ex-3 ; \ same as ex-3 + * : ex-7 ['] ex-1 catch ; \ "compile" behavior, + * \ catch works + * : ex-8 postpone ls ; immediate \ same as ex-2 + * : ex-9 postpone ls ; \ same as ex-3 + * + * As the definition below is particularly tricky, and it's side effects + * must be well understood by those playing with it, I'll be heavy on + * the comments. + * + * (if you edit this definition, pay attention to trailing spaces after + * each word -- I warned you! :-) ) + */ +#define BUILTIN_CONSTRUCTOR \ +": builtin: " \ + ">in @ " /* save the tib index pointer */ \ + "' " /* get next word's xt */ \ + "swap >in ! " /* point again to next word */ \ + "create " /* create a new definition of the next word */ \ + ", " /* save previous definition's xt */ \ + "immediate " /* make the new definition an immediate word */ \ + \ + "does> " /* Now, the *new* definition will: */ \ + "state @ if " /* if in compiling state: */ \ + "1 postpone literal " /* pass 1 flag to indicate compile */ \ + "@ compile, " /* compile in previous definition */ \ + "postpone throw " /* throw stack-returned result */ \ + "else " /* if in interpreting state: */ \ + "0 swap " /* pass 0 flag to indicate interpret */ \ + "@ execute " /* call previous definition */ \ + "throw " /* throw stack-returned result */ \ + "then ; " + +/* + * Initialise the Forth interpreter, create all our commands as words. + */ +void +bf_init(void) +{ + struct bootblk_command **cmdp; + char create_buf[41]; /* 31 characters-long builtins */ + int fd; + + bf_sys = ficlInitSystem(10000); /* Default dictionary ~4000 cells */ + bf_vm = ficlNewVM(bf_sys); + + /* Put all private definitions in a "builtins" vocabulary */ + ficlExec(bf_vm, "vocabulary builtins also builtins definitions"); + + /* Builtin constructor word */ + ficlExec(bf_vm, BUILTIN_CONSTRUCTOR); + + /* make all commands appear as Forth words */ + SET_FOREACH(cmdp, Xcommand_set) { + ficlBuild(bf_sys, (char *)(*cmdp)->c_name, bf_command, FW_DEFAULT); + ficlExec(bf_vm, "forth definitions builtins"); + sprintf(create_buf, "builtin: %s", (*cmdp)->c_name); + ficlExec(bf_vm, create_buf); + ficlExec(bf_vm, "builtins definitions"); + } + ficlExec(bf_vm, "only forth definitions"); + + /* Export some version numbers so that code can detect the loader/host version */ + ficlSetEnv(bf_sys, "FreeBSD_version", __FreeBSD_version); + ficlSetEnv(bf_sys, "loader_version", + (bootprog_rev[0] - '0') * 10 + (bootprog_rev[2] - '0')); + + /* try to load and run init file if present */ + if ((fd = open("/boot/boot.4th", O_RDONLY)) != -1) { + (void)ficlExecFD(bf_vm, fd); + close(fd); + } + + /* Do this last, so /boot/boot.4th can change it */ + pInterp = ficlLookup(bf_sys, "interpret"); +} + +/* + * Feed a line of user input to the Forth interpreter + */ +int +bf_run(char *line) +{ + int result; + + result = ficlExec(bf_vm, line); + + DEBUG("ficlExec '%s' = %d", line, result); + switch (result) { + case VM_OUTOFTEXT: + case VM_ABORTQ: + case VM_QUIT: + case VM_ERREXIT: + break; + case VM_USEREXIT: + printf("No where to leave to!\n"); + break; + case VM_ABORT: + printf("Aborted!\n"); + break; + case BF_PARSE: + printf("Parse error!\n"); + break; + default: + /* Hopefully, all other codes filled this buffer */ + printf("%s\n", command_errmsg); + } + + if (result == VM_USEREXIT) + panic("interpreter exit"); + setenv("interpret", bf_vm->state ? "" : "OK", 1); + + return result; +} diff --git a/sys/boot/common/interp_parse.c b/sys/boot/common/interp_parse.c new file mode 100644 index 0000000..32b01c8 --- /dev/null +++ b/sys/boot/common/interp_parse.c @@ -0,0 +1,204 @@ +/*- + * 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. + * + * Jordan K. Hubbard + * 29 August 1998 + * + * The meat of the simple parser. + */ + +#include <sys/cdefs.h> +__FBSDID("$FreeBSD$"); + +#include <stand.h> +#include <string.h> +#include "bootstrap.h" + +static void clean(void); +static int insert(int *argcp, char *buf); +static char *variable_lookup(char *name); + +#define PARSE_BUFSIZE 1024 /* maximum size of one element */ +#define MAXARGS 20 /* maximum number of elements */ +static char *args[MAXARGS]; + +/* + * parse: accept a string of input and "parse" it for backslash + * substitutions and environment variable expansions (${var}), + * returning an argc/argv style vector of whitespace separated + * arguments. Returns 0 on success, 1 on failure (ok, ok, so I + * wimped-out on the error codes! :). + * + * Note that the argv array returned must be freed by the caller, but + * we own the space allocated for arguments and will free that on next + * invocation. This allows argv consumers to modify the array if + * required. + * + * NB: environment variables that expand to more than one whitespace + * separated token will be returned as a single argv[] element, not + * split in turn. Expanded text is also immune to further backslash + * elimination or expansion since this is a one-pass, non-recursive + * parser. You didn't specify more than this so if you want more, ask + * me. - jkh + */ + +#define PARSE_FAIL(expr) \ +if (expr) { \ + printf("fail at line %d\n", __LINE__); \ + clean(); \ + free(copy); \ + free(buf); \ + return 1; \ +} + +/* Accept the usual delimiters for a variable, returning counterpart */ +static char +isdelim(int ch) +{ + if (ch == '{') + return '}'; + else if (ch == '(') + return ')'; + return '\0'; +} + +static int +isquote(int ch) +{ + return (ch == '\'' || ch == '"'); +} + +int +parse(int *argc, char ***argv, char *str) +{ + int ac; + char *val, *p, *q, *copy = NULL; + size_t i = 0; + char token, tmp, quote, *buf; + enum { STR, VAR, WHITE } state; + + ac = *argc = 0; + quote = 0; + if (!str || (p = copy = backslash(str)) == NULL) + return 1; + + /* Initialize vector and state */ + clean(); + state = STR; + buf = (char *)malloc(PARSE_BUFSIZE); + token = 0; + + /* And awaaaaaaaaay we go! */ + while (*p) { + switch (state) { + case STR: + if ((*p == '\\') && p[1]) { + p++; + PARSE_FAIL(i == (PARSE_BUFSIZE - 1)); + buf[i++] = *p++; + } else if (isquote(*p)) { + quote = quote ? 0 : *p; + ++p; + } + else if (isspace(*p) && !quote) { + state = WHITE; + if (i) { + buf[i] = '\0'; + PARSE_FAIL(insert(&ac, buf)); + i = 0; + } + ++p; + } else if (*p == '$') { + token = isdelim(*(p + 1)); + if (token) + p += 2; + else + ++p; + state = VAR; + } else { + PARSE_FAIL(i == (PARSE_BUFSIZE - 1)); + buf[i++] = *p++; + } + break; + + case WHITE: + if (isspace(*p)) + ++p; + else + state = STR; + break; + + case VAR: + if (token) { + PARSE_FAIL((q = index(p, token)) == NULL); + } else { + q = p; + while (*q && !isspace(*q)) + ++q; + } + tmp = *q; + *q = '\0'; + if ((val = variable_lookup(p)) != NULL) { + size_t len = strlen(val); + + strncpy(buf + i, val, PARSE_BUFSIZE - (i + 1)); + i += min(len, PARSE_BUFSIZE - 1); + } + *q = tmp; /* restore value */ + p = q + (token ? 1 : 0); + state = STR; + break; + } + } + /* If at end of token, add it */ + if (i && state == STR) { + buf[i] = '\0'; + PARSE_FAIL(insert(&ac, buf)); + } + args[ac] = NULL; + *argc = ac; + *argv = (char **)malloc((sizeof(char *) * ac + 1)); + bcopy(args, *argv, sizeof(char *) * ac + 1); + free(buf); + free(copy); + return 0; +} + +#define MAXARGS 20 + +/* Clean vector space */ +static void +clean(void) +{ + int i; + + for (i = 0; i < MAXARGS; i++) { + if (args[i] != NULL) { + free(args[i]); + args[i] = NULL; + } + } +} + +static int +insert(int *argcp, char *buf) +{ + if (*argcp >= MAXARGS) + return 1; + args[(*argcp)++] = strdup(buf); + return 0; +} + +static char * +variable_lookup(char *name) +{ + /* XXX search "special variable" space first? */ + return (char *)getenv(name); +} diff --git a/sys/boot/common/isapnp.c b/sys/boot/common/isapnp.c new file mode 100644 index 0000000..a8efb28 --- /dev/null +++ b/sys/boot/common/isapnp.c @@ -0,0 +1,313 @@ +/*- + * Copyright (c) 1998, Michael Smith + * Copyright (c) 1996, Sujal M. Patel + * 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 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 AUTHOR 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. + */ + +#include <sys/cdefs.h> +__FBSDID("$FreeBSD$"); + +/* + * Machine-independant ISA PnP enumerator implementing a subset of the + * ISA PnP specification. + */ +#include <stand.h> +#include <string.h> +#include <bootstrap.h> +#include <isapnp.h> + +#define inb(x) (archsw.arch_isainb((x))) +#define outb(x,y) (archsw.arch_isaoutb((x),(y))) + +static void isapnp_write(int d, int r); +static void isapnp_send_Initiation_LFSR(void); +static int isapnp_get_serial(u_int8_t *p); +static int isapnp_isolation_protocol(void); +static void isapnp_enumerate(void); + +/* PnP read data port */ +int isapnp_readport = 0; + +#define _PNP_ID_LEN 9 + +struct pnphandler isapnphandler = +{ + "ISA bus", + isapnp_enumerate +}; + +static void +isapnp_write(int d, int r) +{ + outb (_PNP_ADDRESS, d); + outb (_PNP_WRITE_DATA, r); +} + +/* + * Send Initiation LFSR as described in "Plug and Play ISA Specification", + * Intel May 94. + */ +static void +isapnp_send_Initiation_LFSR(void) +{ + int cur, i; + + /* Reset the LSFR */ + outb(_PNP_ADDRESS, 0); + outb(_PNP_ADDRESS, 0); /* yes, we do need it twice! */ + + cur = 0x6a; + outb(_PNP_ADDRESS, cur); + + for (i = 1; i < 32; i++) { + cur = (cur >> 1) | (((cur ^ (cur >> 1)) << 7) & 0xff); + outb(_PNP_ADDRESS, cur); + } +} + +/* + * Get the device's serial number. Returns 1 if the serial is valid. + */ +static int +isapnp_get_serial(u_int8_t *data) +{ + int i, bit, valid = 0, sum = 0x6a; + + bzero(data, _PNP_ID_LEN); + outb(_PNP_ADDRESS, SERIAL_ISOLATION); + for (i = 0; i < 72; i++) { + bit = inb(isapnp_readport) == 0x55; + delay(250); /* Delay 250 usec */ + + /* Can't Short Circuit the next evaluation, so 'and' is last */ + bit = (inb(isapnp_readport) == 0xaa) && bit; + delay(250); /* Delay 250 usec */ + + valid = valid || bit; + + if (i < 64) + sum = (sum >> 1) | + (((sum ^ (sum >> 1) ^ bit) << 7) & 0xff); + + data[i / 8] = (data[i / 8] >> 1) | (bit ? 0x80 : 0); + } + + valid = valid && (data[8] == sum); + + return valid; +} + +/* + * Fills the buffer with resource info from the device. + * Returns nonzero if the device fails to report + */ +static int +isapnp_get_resource_info(u_int8_t *buffer, int len) +{ + int i, j; + u_char temp; + + for (i = 0; i < len; i++) { + outb(_PNP_ADDRESS, STATUS); + for (j = 0; j < 100; j++) { + if ((inb(isapnp_readport)) & 0x1) + break; + delay(1); + } + if (j == 100) { + printf("PnP device failed to report resource data\n"); + return(1); + } + outb(_PNP_ADDRESS, RESOURCE_DATA); + temp = inb(isapnp_readport); + if (buffer != NULL) + buffer[i] = temp; + } + return(0); +} + +/* + * Scan Resource Data for useful information. + * + * We scan the resource data for compatible device IDs and + * identifier strings; we only take the first identifier string + * and assume it's for the card as a whole. + * + * Returns 0 if the scan completed OK, nonzero on error. + */ +static int +isapnp_scan_resdata(struct pnpinfo *pi) +{ + u_char tag, resinfo[8]; + u_int limit; + size_t large_len; + u_char *str; + + limit = 1000; + while ((limit-- > 0) && !isapnp_get_resource_info(&tag, 1)) { + if (PNP_RES_TYPE(tag) == 0) { + /* Small resource */ + switch (PNP_SRES_NUM(tag)) { + + case COMP_DEVICE_ID: + /* Got a compatible device id resource */ + if (isapnp_get_resource_info(resinfo, PNP_SRES_LEN(tag))) + return(1); + pnp_addident(pi, pnp_eisaformat(resinfo)); + + case END_TAG: + return(0); + break; + + default: + /* Skip this resource */ + if (isapnp_get_resource_info(NULL, PNP_SRES_LEN(tag))) + return(1); + break; + } + } else { + /* Large resource */ + if (isapnp_get_resource_info(resinfo, 2)) + return(1); + + large_len = resinfo[1]; + large_len = (large_len << 8) + resinfo[0]; + + switch(PNP_LRES_NUM(tag)) { + + case ID_STRING_ANSI: + str = malloc(large_len + 1); + if (isapnp_get_resource_info(str, (ssize_t)large_len)) { + free(str); + return(1); + } + str[large_len] = 0; + if (pi->pi_desc == NULL) { + pi->pi_desc = (char *)str; + } else { + free(str); + } + break; + + default: + /* Large resource, skip it */ + if (isapnp_get_resource_info(NULL, (ssize_t)large_len)) + return(1); + } + } + } + return(1); +} + +/* + * Run the isolation protocol. Upon exiting, all cards are aware that + * they should use isapnp_readport as the READ_DATA port. + */ +static int +isapnp_isolation_protocol(void) +{ + int csn; + struct pnpinfo *pi; + u_int8_t cardid[_PNP_ID_LEN]; + int ndevs; + + isapnp_send_Initiation_LFSR(); + ndevs = 0; + + isapnp_write(CONFIG_CONTROL, 0x04); /* Reset CSN for All Cards */ + + for (csn = 1; ; csn++) { + /* Wake up cards without a CSN (ie. all of them) */ + isapnp_write(WAKE, 0); + isapnp_write(SET_RD_DATA, (isapnp_readport >> 2)); + outb(_PNP_ADDRESS, SERIAL_ISOLATION); + delay(1000); /* Delay 1 msec */ + + if (isapnp_get_serial(cardid)) { + isapnp_write(SET_CSN, csn); + pi = pnp_allocinfo(); + ndevs++; + pnp_addident(pi, pnp_eisaformat(cardid)); + /* scan the card obtaining all the identifiers it holds */ + if (isapnp_scan_resdata(pi)) { + pnp_freeinfo(pi); /* error getting data, ignore */ + } else { + pnp_addinfo(pi); + } + } else { + break; + } + } + /* Move all cards to wait-for-key state */ + while (--csn > 0) { + isapnp_send_Initiation_LFSR(); + isapnp_write(WAKE, csn); + isapnp_write(CONFIG_CONTROL, 0x02); + delay(1000); /* XXX is it really necessary ? */ + csn--; + } + return(ndevs); +} + +/* + * Locate ISA-PnP devices and populate the supplied list. + */ +static void +isapnp_enumerate(void) +{ + int pnp_rd_port; + + /* Check for I/O port access */ + if ((archsw.arch_isainb == NULL) || (archsw.arch_isaoutb == NULL)) + return; + + /* + * Validate a possibly-suggested read port value. If the autoscan failed + * last time, this will return us to autoscan mode again. + */ + if ((isapnp_readport > 0) && + (((isapnp_readport < 0x203) || + (isapnp_readport > 0x3ff) || + (isapnp_readport & 0x3) != 0x3))) + /* invalid, go look for ourselves */ + isapnp_readport = 0; + + if (isapnp_readport < 0) { + /* someone is telling us there is no ISA in the system */ + return; + + } else if (isapnp_readport > 0) { + /* Someone has told us where the port is/should be, or we found one last time */ + isapnp_isolation_protocol(); + + } else { + /* No clues, look for it ourselves */ + for (pnp_rd_port = 0x80; pnp_rd_port < 0xff; pnp_rd_port += 0x10) { + /* Look for something, quit when we find it */ + isapnp_readport = (pnp_rd_port << 2) | 0x3; + if (isapnp_isolation_protocol() > 0) + break; + } + } +} diff --git a/sys/boot/common/isapnp.h b/sys/boot/common/isapnp.h new file mode 100644 index 0000000..0f9956c --- /dev/null +++ b/sys/boot/common/isapnp.h @@ -0,0 +1,313 @@ +/* + * Copyright (c) 1996, Sujal M. Patel + * 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 Sujal M. Patel + * 4. Neither the name of the author nor the names of any co-contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR 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 AUTHOR 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. + * + * $FreeBSD$ + */ + +#ifndef _I386_ISA_PNP_H_ +#define _I386_ISA_PNP_H_ + +/* Maximum Number of PnP Devices. 8 should be plenty */ +#define MAX_PNP_CARDS 8 +/* + * the following is the maximum number of PnP Logical devices that + * userconfig can handle. + */ +#define MAX_PNP_LDN 20 + +/* Static ports to access PnP state machine */ +#ifndef _KERNEL +#ifdef PC98 +/* pnp.h is included from pnpinfo.c. */ +#define _PNP_ADDRESS 0x259 +#define _PNP_WRITE_DATA 0xa59 +#else +#define _PNP_ADDRESS 0x279 +#define _PNP_WRITE_DATA 0xa79 +#endif +#endif + +/* PnP Registers. Write to ADDRESS and then use WRITE/READ_DATA */ +#define SET_RD_DATA 0x00 + /*** + Writing to this location modifies the address of the port used for + reading from the Plug and Play ISA cards. Bits[7:0] become I/O + read port address bits[9:2]. Reads from this register are ignored. + ***/ + +#define SERIAL_ISOLATION 0x01 + /*** + A read to this register causes a Plug and Play cards in the Isolation + state to compare one bit of the boards ID. + This register is read only. + ***/ + +#define CONFIG_CONTROL 0x02 + /*** + Bit[2] Reset CSN to 0 + Bit[1] Return to the Wait for Key state + Bit[0] Reset all logical devices and restore configuration + registers to their power-up values. + + A write to bit[0] of this register performs a reset function on + all logical devices. This resets the contents of configuration + registers to their default state. All card's logical devices + enter their default state and the CSN is preserved. + + A write to bit[1] of this register causes all cards to enter the + Wait for Key state but all CSNs are preserved and logical devices + are not affected. + + A write to bit[2] of this register causes all cards to reset their + CSN to zero . + + This register is write-only. The values are not sticky, that is, + hardware will automatically clear them and there is no need for + software to clear the bits. + ***/ + +#define WAKE 0x03 + /*** + A write to this port will cause all cards that have a CSN that + matches the write data[7:0] to go from the Sleep state to the either + the Isolation state if the write data for this command is zero or + the Config state if the write data is not zero. Additionally, the + pointer to the byte-serial device is reset. This register is + writeonly. + ***/ + +#define RESOURCE_DATA 0x04 + /*** + A read from this address reads the next byte of resource information. + The Status register must be polled until bit[0] is set before this + register may be read. This register is read only. + ***/ + +#define STATUS 0x05 + /*** + Bit[0] when set indicates it is okay to read the next data byte + from the Resource Data register. This register is readonly. + ***/ + +#define SET_CSN 0x06 + /*** + A write to this port sets a card's CSN. The CSN is a value uniquely + assigned to each ISA card after the serial identification process + so that each card may be individually selected during a Wake[CSN] + command. This register is read/write. + ***/ + +#define SET_LDN 0x07 + /*** + Selects the current logical device. All reads and writes of memory, + I/O, interrupt and DMA configuration information access the registers + of the logical device written here. In addition, the I/O Range + Check and Activate commands operate only on the selected logical + device. This register is read/write. If a card has only 1 logical + device, this location should be a read-only value of 0x00. + ***/ + +/*** addresses 0x08 - 0x1F Card Level Reserved for future use ***/ +/*** addresses 0x20 - 0x2F Card Level, Vendor Defined ***/ + +#define ACTIVATE 0x30 + /*** + For each logical device there is one activate register that controls + whether or not the logical device is active on the ISA bus. Bit[0], + if set, activates the logical device. Bits[7:1] are reserved and + must return 0 on reads. This is a read/write register. Before a + logical device is activated, I/O range check must be disabled. + ***/ + +#define IO_RANGE_CHECK 0x31 + /*** + This register is used to perform a conflict check on the I/O port + range programmed for use by a logical device. + + Bit[7:2] Reserved and must return 0 on reads + Bit[1] Enable I/O Range check, if set then I/O Range Check + is enabled. I/O range check is only valid when the logical + device is inactive. + + Bit[0], if set, forces the logical device to respond to I/O reads + of the logical device's assigned I/O range with a 0x55 when I/O + range check is in operation. If clear, the logical device drives + 0xAA. This register is read/write. + ***/ + +/*** addr 0x32 - 0x37 Logical Device Control Reserved for future use ***/ +/*** addr 0x38 - 0x3F Logical Device Control Vendor Define ***/ + +#define MEM_CONFIG 0x40 + /*** + Four memory resource registers per range, four ranges. + Fill with 0 if no ranges are enabled. + + Offset 0: RW Memory base address bits[23:16] + Offset 1: RW Memory base address bits[15:8] + Offset 2: Memory control + Bit[1] specifies 8/16-bit control. This bit is set to indicate + 16-bit memory, and cleared to indicate 8-bit memory. + Bit[0], if cleared, indicates the next field can be used as a range + length for decode (implies range length and base alignment of memory + descriptor are equal). + Bit[0], if set, indicates the next field is the upper limit for + the address. - - Bit[0] is read-only. + Offset 3: RW upper limit or range len, bits[23:16] + Offset 4: RW upper limit or range len, bits[15:8] + Offset 5-Offset 7: filler, unused. + ***/ + +#define IO_CONFIG_BASE 0x60 + /*** + Eight ranges, two bytes per range. + Offset 0: I/O port base address bits[15:8] + Offset 1: I/O port base address bits[7:0] + ***/ + +#define IRQ_CONFIG 0x70 + /*** + Two entries, two bytes per entry. + Offset 0: RW interrupt level (1..15, 0=unused). + Offset 1: Bit[1]: level(1:hi, 0:low), + Bit[0]: type (1:level, 0:edge) + byte 1 can be readonly if 1 type of int is used. + ***/ + +#define DRQ_CONFIG 0x74 + /*** + Two entries, one byte per entry. Bits[2:0] select + which DMA channel is in use for DMA 0. Zero selects DMA channel + 0, seven selects DMA channel 7. DMA channel 4, the cascade channel + is used to indicate no DMA channel is active. + ***/ + +/*** 32-bit memory accesses are at 0x76 ***/ + +/* Macros to parse Resource IDs */ +#define PNP_RES_TYPE(a) (a >> 7) +#define PNP_SRES_NUM(a) (a >> 3) +#define PNP_SRES_LEN(a) (a & 0x07) +#define PNP_LRES_NUM(a) (a & 0x7f) + +/* Small Resource Item names */ +#define PNP_VERSION 0x1 +#define LOG_DEVICE_ID 0x2 +#define COMP_DEVICE_ID 0x3 +#define IRQ_FORMAT 0x4 +#define DMA_FORMAT 0x5 +#define START_DEPEND_FUNC 0x6 +#define END_DEPEND_FUNC 0x7 +#define IO_PORT_DESC 0x8 +#define FIXED_IO_PORT_DESC 0x9 +#define SM_RES_RESERVED 0xa-0xd +#define SM_VENDOR_DEFINED 0xe +#define END_TAG 0xf + +/* Large Resource Item names */ +#define MEMORY_RANGE_DESC 0x1 +#define ID_STRING_ANSI 0x2 +#define ID_STRING_UNICODE 0x3 +#define LG_VENDOR_DEFINED 0x4 +#define _32BIT_MEM_RANGE_DESC 0x5 +#define _32BIT_FIXED_LOC_DESC 0x6 +#define LG_RES_RESERVED 0x7-0x7f + +/* + * pnp_cinfo contains Configuration Information. They are used + * to communicate to the device driver the actual configuration + * of the device, and also by the userconfig menu to let the + * operating system override any configuration set by the bios. + * + */ +struct pnp_cinfo { + u_int vendor_id; /* board id */ + u_int serial; /* Board's Serial Number */ + u_long flags; /* OS-reserved flags */ + u_char csn; /* assigned Card Select Number */ + u_char ldn; /* Logical Device Number */ + u_char enable; /* pnp enable */ + u_char override; /* override bios parms (in userconfig) */ + u_char irq[2]; /* IRQ Number */ + u_char irq_type[2]; /* IRQ Type */ + u_char drq[2]; + u_short port[8]; /* The Base Address of the Port */ + struct { + u_long base; /* Memory Base Address */ + int control; /* Memory Control Register */ + u_long range; /* Memory Range *OR* Upper Limit */ + } mem[4]; +}; + +#ifdef _KERNEL + +struct pnp_device { + char *pd_name; + char * (*pd_probe ) (u_long csn, u_long vendor_id); + void (*pd_attach ) (u_long csn, u_long vend_id, char * name, + struct isa_device *dev); + u_long *pd_count; + u_int *imask ; +}; + +struct _pnp_id { + u_long vendor_id; + u_long serial; + u_char checksum; +} ; + +struct pnp_dlist_node { + struct pnp_device *pnp; + struct isa_device dev; + struct pnp_dlist_node *next; +}; + +typedef struct _pnp_id pnp_id; +extern struct pnp_dlist_node *pnp_device_list; +extern pnp_id pnp_devices[MAX_PNP_CARDS]; +extern struct pnp_cinfo pnp_ldn_overrides[MAX_PNP_LDN]; +extern int pnp_overrides_valid; + +/* + * these two functions are for use in drivers + */ +int read_pnp_parms(struct pnp_cinfo *d, int ldn); +int write_pnp_parms(struct pnp_cinfo *d, int ldn); +int enable_pnp_card(void); + +/* + * used by autoconfigure to actually probe and attach drivers + */ +void pnp_configure(void); + +#endif /* _KERNEL */ + +#endif /* !_I386_ISA_PNP_H_ */ diff --git a/sys/boot/common/load.c b/sys/boot/common/load.c new file mode 100644 index 0000000..e00487d --- /dev/null +++ b/sys/boot/common/load.c @@ -0,0 +1,102 @@ +/*- + * Copyright (c) 1998 Michael Smith <msmith@freebsd.org> + * 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 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 AUTHOR 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. + */ + +#include <sys/cdefs.h> +__FBSDID("$FreeBSD$"); + +#include <stand.h> + +#define LOAD_TINYBUF 2048 + +/* + * Attempt to load the file at (path) into an allocated + * area on the heap, return a pointer to it or NULL + * on failure. + * + * Because in many cases it is impossible to determine the + * true size of a file without reading it, we do just that. + */ +char * +filedup(const char *path, int flags) +{ + char *buf; + int fd; + size_t size, result; + + if ((fd = open(path, F_READ | flags)) == -1) + return(NULL); + + printf("%s open, flags 0x%x\n", path, files[fd].f_flags); + buf = alloc(LOAD_TINYBUF); + + /* Read the first buffer-full */ + size = read(fd, buf, LOAD_TINYBUF); + if (size < 1) { + free(buf, LOAD_TINYBUF); + close(fd); + return(NULL); + } + /* If it all fitted, then just return the buffer straight out */ + if (size < LOAD_TINYBUF) { + close(fd); + buf[size] = 0; + return(buf); + } + + printf("tinybuf loaded, size %d\n", size); + getchar(); + + + /* Read everything until we know how big it is */ + for (;;) { + result = read(fd, buf, LOAD_TINYBUF); + if (size == -1) { + free(buf, LOAD_TINYBUF); + close(fd); + return(NULL); + } + if (result == 0) + break; + size += result; + } + + /* discard the old buffer, close the file */ + free(buf, LOAD_TINYBUF); + close(fd); + + /* reopen the file, realloc the buffer */ + if ((fd = open(path, F_READ | flags)) == -1) + return(NULL); + buf = alloc(size); + result = read(fd, buf, size); + close(fd); + if (result != size) { + free(buf, size); + return(NULL); + } + return(buf); +} + diff --git a/sys/boot/common/load_elf.c b/sys/boot/common/load_elf.c new file mode 100644 index 0000000..06c6897 --- /dev/null +++ b/sys/boot/common/load_elf.c @@ -0,0 +1,741 @@ +/*- + * Copyright (c) 1998 Michael Smith <msmith@freebsd.org> + * Copyright (c) 1998 Peter Wemm <peter@freebsd.org> + * 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 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 AUTHOR 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. + */ + +#include <sys/cdefs.h> +__FBSDID("$FreeBSD$"); + +#include <sys/param.h> +#include <sys/exec.h> +#include <sys/linker.h> +#include <sys/module.h> +#include <string.h> +#include <machine/elf.h> +#include <stand.h> +#define FREEBSD_ELF +#include <link.h> + +#include "bootstrap.h" + +#define COPYOUT(s,d,l) archsw.arch_copyout((vm_offset_t)(s), d, l) + +#if defined(__i386__) && __ELF_WORD_SIZE == 64 +#undef ELF_TARG_CLASS +#undef ELF_TARG_MACH +#define ELF_TARG_CLASS ELFCLASS64 +#define ELF_TARG_MACH EM_X86_64 +#endif + +typedef struct elf_file { + Elf_Phdr *ph; + Elf_Ehdr *ehdr; + Elf_Sym *symtab; + Elf_Hashelt *hashtab; + Elf_Hashelt nbuckets; + Elf_Hashelt nchains; + Elf_Hashelt *buckets; + Elf_Hashelt *chains; + Elf_Rela *rela; + size_t relasz; + char *strtab; + size_t strsz; + int fd; + caddr_t firstpage; + size_t firstlen; + int kernel; + u_int64_t off; +} *elf_file_t; + +static int __elfN(loadimage)(struct preloaded_file *mp, elf_file_t ef, u_int64_t loadaddr); +static int __elfN(lookup_symbol)(struct preloaded_file *mp, elf_file_t ef, const char* name, Elf_Sym* sym); +#ifdef __sparc__ +static void __elfN(reloc_ptr)(struct preloaded_file *mp, elf_file_t ef, + void *p, void *val, size_t len); +#endif +static int __elfN(parse_modmetadata)(struct preloaded_file *mp, elf_file_t ef); +static char *fake_modname(const char *name); + +const char *__elfN(kerneltype) = "elf kernel"; +const char *__elfN(moduletype) = "elf module"; + +/* + * Attempt to load the file (file) as an ELF module. It will be stored at + * (dest), and a pointer to a module structure describing the loaded object + * will be saved in (result). + */ +int +__elfN(loadfile)(char *filename, u_int64_t dest, struct preloaded_file **result) +{ + struct preloaded_file *fp, *kfp; + struct elf_file ef; + Elf_Ehdr *ehdr; + int err; + u_int pad; + ssize_t bytes_read; + + fp = NULL; + bzero(&ef, sizeof(struct elf_file)); + + /* + * Open the image, read and validate the ELF header + */ + if (filename == NULL) /* can't handle nameless */ + return(EFTYPE); + if ((ef.fd = open(filename, O_RDONLY)) == -1) + return(errno); + ef.firstpage = malloc(PAGE_SIZE); + if (ef.firstpage == NULL) { + close(ef.fd); + return(ENOMEM); + } + bytes_read = read(ef.fd, ef.firstpage, PAGE_SIZE); + ef.firstlen = (size_t)bytes_read; + if (bytes_read < 0 || ef.firstlen <= sizeof(Elf_Ehdr)) { + err = EFTYPE; /* could be EIO, but may be small file */ + goto oerr; + } + ehdr = ef.ehdr = (Elf_Ehdr *)ef.firstpage; + + /* Is it ELF? */ + if (!IS_ELF(*ehdr)) { + err = EFTYPE; + goto oerr; + } + if (ehdr->e_ident[EI_CLASS] != ELF_TARG_CLASS || /* Layout ? */ + ehdr->e_ident[EI_DATA] != ELF_TARG_DATA || + ehdr->e_ident[EI_VERSION] != EV_CURRENT || /* Version ? */ + ehdr->e_version != EV_CURRENT || + ehdr->e_machine != ELF_TARG_MACH) { /* Machine ? */ + err = EFTYPE; + goto oerr; + } + + + /* + * Check to see what sort of module we are. + */ + kfp = file_findfile(NULL, NULL); + if (ehdr->e_type == ET_DYN) { + /* Looks like a kld module */ + if (kfp == NULL) { + printf("elf" __XSTRING(__ELF_WORD_SIZE) "_loadfile: can't load module before kernel\n"); + err = EPERM; + goto oerr; + } + if (strcmp(__elfN(kerneltype), kfp->f_type)) { + printf("elf" __XSTRING(__ELF_WORD_SIZE) "_loadfile: can't load module with kernel type '%s'\n", kfp->f_type); + err = EPERM; + goto oerr; + } + /* Looks OK, got ahead */ + ef.kernel = 0; + + /* Page-align the load address */ + pad = (u_int)dest & PAGE_MASK; + if (pad != 0) { + pad = PAGE_SIZE - pad; + dest += pad; + } + } else if (ehdr->e_type == ET_EXEC) { + /* Looks like a kernel */ + if (kfp != NULL) { + printf("elf" __XSTRING(__ELF_WORD_SIZE) "_loadfile: kernel already loaded\n"); + err = EPERM; + goto oerr; + } + /* + * Calculate destination address based on kernel entrypoint + */ + dest = ehdr->e_entry; + if (dest == 0) { + printf("elf" __XSTRING(__ELF_WORD_SIZE) "_loadfile: not a kernel (maybe static binary?)\n"); + err = EPERM; + goto oerr; + } + ef.kernel = 1; + + } else { + err = EFTYPE; + goto oerr; + } + + /* + * Ok, we think we should handle this. + */ + fp = file_alloc(); + if (fp == NULL) { + printf("elf" __XSTRING(__ELF_WORD_SIZE) "_loadfile: cannot allocate module info\n"); + err = EPERM; + goto out; + } + if (ef.kernel) + setenv("kernelname", filename, 1); + fp->f_name = strdup(filename); + fp->f_type = strdup(ef.kernel ? __elfN(kerneltype) : __elfN(moduletype)); + +#ifdef ELF_VERBOSE + if (ef.kernel) + printf("%s entry at 0x%jx\n", filename, (uintmax_t)dest); +#else + printf("%s ", filename); +#endif + + fp->f_size = __elfN(loadimage)(fp, &ef, dest); + if (fp->f_size == 0 || fp->f_addr == 0) + goto ioerr; + + /* save exec header as metadata */ + file_addmetadata(fp, MODINFOMD_ELFHDR, sizeof(*ehdr), ehdr); + + /* Load OK, return module pointer */ + *result = (struct preloaded_file *)fp; + err = 0; + goto out; + + ioerr: + err = EIO; + oerr: + file_discard(fp); + out: + if (ef.firstpage) + free(ef.firstpage); + close(ef.fd); + return(err); +} + +/* + * With the file (fd) open on the image, and (ehdr) containing + * the Elf header, load the image at (off) + */ +static int +__elfN(loadimage)(struct preloaded_file *fp, elf_file_t ef, u_int64_t off) +{ + int i; + u_int j; + Elf_Ehdr *ehdr; + Elf_Phdr *phdr, *php; + Elf_Shdr *shdr; + int ret; + vm_offset_t firstaddr; + vm_offset_t lastaddr; + void *buf; + size_t resid, chunk; + ssize_t result; + vm_offset_t dest; + Elf_Addr ssym, esym; + Elf_Dyn *dp; + Elf_Addr adp; + int ndp; + int symstrindex; + int symtabindex; + Elf_Size size; + u_int fpcopy; + + dp = NULL; + shdr = NULL; + ret = 0; + firstaddr = lastaddr = 0; + ehdr = ef->ehdr; + if (ef->kernel) { +#ifdef __i386__ +#if __ELF_WORD_SIZE == 64 + off = - (off & 0xffffffffff000000ull);/* x86_64 relocates after locore */ +#else + off = - (off & 0xff000000u); /* i386 relocates after locore */ +#endif +#else + off = 0; /* alpha is direct mapped for kernels */ +#endif + } + ef->off = off; + + if ((ehdr->e_phoff + ehdr->e_phnum * sizeof(*phdr)) > ef->firstlen) { + printf("elf" __XSTRING(__ELF_WORD_SIZE) "_loadimage: program header not within first page\n"); + goto out; + } + phdr = (Elf_Phdr *)(ef->firstpage + ehdr->e_phoff); + + for (i = 0; i < ehdr->e_phnum; i++) { + /* We want to load PT_LOAD segments only.. */ + if (phdr[i].p_type != PT_LOAD) + continue; + +#ifdef ELF_VERBOSE + printf("Segment: 0x%lx@0x%lx -> 0x%lx-0x%lx", + (long)phdr[i].p_filesz, (long)phdr[i].p_offset, + (long)(phdr[i].p_vaddr + off), + (long)(phdr[i].p_vaddr + off + phdr[i].p_memsz - 1)); +#else + if ((phdr[i].p_flags & PF_W) == 0) { + printf("text=0x%lx ", (long)phdr[i].p_filesz); + } else { + printf("data=0x%lx", (long)phdr[i].p_filesz); + if (phdr[i].p_filesz < phdr[i].p_memsz) + printf("+0x%lx", (long)(phdr[i].p_memsz -phdr[i].p_filesz)); + printf(" "); + } +#endif + fpcopy = 0; + if (ef->firstlen > phdr[i].p_offset) { + fpcopy = ef->firstlen - phdr[i].p_offset; + archsw.arch_copyin(ef->firstpage + phdr[i].p_offset, + phdr[i].p_vaddr + off, fpcopy); + } + if (phdr[i].p_filesz > fpcopy) { + if (lseek(ef->fd, (off_t)(phdr[i].p_offset + fpcopy), + SEEK_SET) == -1) { + printf("\nelf" __XSTRING(__ELF_WORD_SIZE) "_loadexec: cannot seek\n"); + goto out; + } + if (archsw.arch_readin(ef->fd, phdr[i].p_vaddr + off + fpcopy, + phdr[i].p_filesz - fpcopy) != (ssize_t)(phdr[i].p_filesz - fpcopy)) { + printf("\nelf" __XSTRING(__ELF_WORD_SIZE) "_loadexec: archsw.readin failed\n"); + goto out; + } + } + /* clear space from oversized segments; eg: bss */ + if (phdr[i].p_filesz < phdr[i].p_memsz) { +#ifdef ELF_VERBOSE + printf(" (bss: 0x%lx-0x%lx)", + (long)(phdr[i].p_vaddr + off + phdr[i].p_filesz), + (long)(phdr[i].p_vaddr + off + phdr[i].p_memsz - 1)); +#endif + + /* no archsw.arch_bzero */ + buf = malloc(PAGE_SIZE); + if (buf == NULL) { + printf("\nelf" __XSTRING(__ELF_WORD_SIZE) "_loadimage: malloc() failed\n"); + goto out; + } + bzero(buf, PAGE_SIZE); + resid = phdr[i].p_memsz - phdr[i].p_filesz; + dest = phdr[i].p_vaddr + off + phdr[i].p_filesz; + while (resid > 0) { + chunk = min(PAGE_SIZE, resid); + archsw.arch_copyin(buf, dest, chunk); + resid -= chunk; + dest += chunk; + } + free(buf); + } +#ifdef ELF_VERBOSE + printf("\n"); +#endif + + if (firstaddr == 0 || firstaddr > (phdr[i].p_vaddr + off)) + firstaddr = phdr[i].p_vaddr + off; + if (lastaddr == 0 || lastaddr < (phdr[i].p_vaddr + off + phdr[i].p_memsz)) + lastaddr = phdr[i].p_vaddr + off + phdr[i].p_memsz; + } + lastaddr = roundup(lastaddr, sizeof(long)); + + /* + * Now grab the symbol tables. This isn't easy if we're reading a + * .gz file. I think the rule is going to have to be that you must + * strip a file to remove symbols before gzipping it so that we do not + * try to lseek() on it. + */ + chunk = ehdr->e_shnum * ehdr->e_shentsize; + if (chunk == 0 || ehdr->e_shoff == 0) + goto nosyms; + shdr = malloc(chunk); + if (shdr == NULL) + goto nosyms; + if (lseek(ef->fd, (off_t)ehdr->e_shoff, SEEK_SET) == -1) { + printf("\nelf" __XSTRING(__ELF_WORD_SIZE) "_loadimage: cannot lseek() to section headers"); + goto nosyms; + } + result = read(ef->fd, shdr, chunk); + if (result < 0 || (size_t)result != chunk) { + printf("\nelf" __XSTRING(__ELF_WORD_SIZE) "_loadimage: read section headers failed"); + goto nosyms; + } + symtabindex = -1; + symstrindex = -1; + for (i = 0; i < ehdr->e_shnum; i++) { + if (shdr[i].sh_type != SHT_SYMTAB) + continue; + for (j = 0; j < ehdr->e_phnum; j++) { + if (phdr[j].p_type != PT_LOAD) + continue; + if (shdr[i].sh_offset >= phdr[j].p_offset && + (shdr[i].sh_offset + shdr[i].sh_size <= + phdr[j].p_offset + phdr[j].p_filesz)) { + shdr[i].sh_offset = 0; + shdr[i].sh_size = 0; + break; + } + } + if (shdr[i].sh_offset == 0 || shdr[i].sh_size == 0) + continue; /* alread loaded in a PT_LOAD above */ + /* Save it for loading below */ + symtabindex = i; + symstrindex = shdr[i].sh_link; + } + if (symtabindex < 0 || symstrindex < 0) + goto nosyms; + + /* Ok, committed to a load. */ +#ifndef ELF_VERBOSE + printf("syms=["); +#endif + ssym = lastaddr; + for (i = symtabindex; i >= 0; i = symstrindex) { +#ifdef ELF_VERBOSE + char *secname; + + switch(shdr[i].sh_type) { + case SHT_SYMTAB: /* Symbol table */ + secname = "symtab"; + break; + case SHT_STRTAB: /* String table */ + secname = "strtab"; + break; + default: + secname = "WHOA!!"; + break; + } +#endif + + size = shdr[i].sh_size; + archsw.arch_copyin(&size, lastaddr, sizeof(size)); + lastaddr += sizeof(size); + +#ifdef ELF_VERBOSE + printf("\n%s: 0x%lx@0x%lx -> 0x%lx-0x%lx", secname, + shdr[i].sh_size, shdr[i].sh_offset, + lastaddr, lastaddr + shdr[i].sh_size); +#else + if (i == symstrindex) + printf("+"); + printf("0x%lx+0x%lx", (long)sizeof(size), (long)size); +#endif + + if (lseek(ef->fd, (off_t)shdr[i].sh_offset, SEEK_SET) == -1) { + printf("\nelf" __XSTRING(__ELF_WORD_SIZE) "_loadimage: could not seek for symbols - skipped!"); + lastaddr = ssym; + ssym = 0; + goto nosyms; + } + result = archsw.arch_readin(ef->fd, lastaddr, shdr[i].sh_size); + if (result < 0 || (size_t)result != shdr[i].sh_size) { + printf("\nelf" __XSTRING(__ELF_WORD_SIZE) "_loadimage: could not read symbols - skipped!"); + lastaddr = ssym; + ssym = 0; + goto nosyms; + } + /* Reset offsets relative to ssym */ + lastaddr += shdr[i].sh_size; + lastaddr = roundup(lastaddr, sizeof(size)); + if (i == symtabindex) + symtabindex = -1; + else if (i == symstrindex) + symstrindex = -1; + } + esym = lastaddr; +#ifndef ELF_VERBOSE + printf("]"); +#endif + + file_addmetadata(fp, MODINFOMD_SSYM, sizeof(ssym), &ssym); + file_addmetadata(fp, MODINFOMD_ESYM, sizeof(esym), &esym); + +nosyms: + printf("\n"); + + ret = lastaddr - firstaddr; + fp->f_addr = firstaddr; + + php = NULL; + for (i = 0; i < ehdr->e_phnum; i++) { + if (phdr[i].p_type == PT_DYNAMIC) { + php = phdr + i; + adp = php->p_vaddr; + file_addmetadata(fp, MODINFOMD_DYNAMIC, sizeof(adp), &adp); + break; + } + } + + if (php == NULL) /* this is bad, we cannot get to symbols or _DYNAMIC */ + goto out; + + ndp = php->p_filesz / sizeof(Elf_Dyn); + if (ndp == 0) + goto out; + dp = malloc(php->p_filesz); + if (dp == NULL) + goto out; + archsw.arch_copyout(php->p_vaddr + off, dp, php->p_filesz); + + ef->strsz = 0; + for (i = 0; i < ndp; i++) { + if (dp[i].d_tag == NULL) + break; + switch (dp[i].d_tag) { + case DT_HASH: + ef->hashtab = (Elf_Hashelt*)(uintptr_t)(dp[i].d_un.d_ptr + off); + break; + case DT_STRTAB: + ef->strtab = (char *)(uintptr_t)(dp[i].d_un.d_ptr + off); + break; + case DT_STRSZ: + ef->strsz = dp[i].d_un.d_val; + break; + case DT_SYMTAB: + ef->symtab = (Elf_Sym*)(uintptr_t)(dp[i].d_un.d_ptr + off); + break; + case DT_RELA: + ef->rela = (Elf_Rela *)(uintptr_t)(dp[i].d_un.d_ptr + off); + break; + case DT_RELASZ: + ef->relasz = dp[i].d_un.d_val; + break; + default: + break; + } + } + if (ef->hashtab == NULL || ef->symtab == NULL || + ef->strtab == NULL || ef->strsz == 0) + goto out; + COPYOUT(ef->hashtab, &ef->nbuckets, sizeof(ef->nbuckets)); + COPYOUT(ef->hashtab + 1, &ef->nchains, sizeof(ef->nchains)); + ef->buckets = ef->hashtab + 2; + ef->chains = ef->buckets + ef->nbuckets; + if (__elfN(parse_modmetadata)(fp, ef) == 0) + goto out; + + if (ef->kernel) /* kernel must not depend on anything */ + goto out; + +out: + if (dp) + free(dp); + if (shdr) + free(shdr); + return ret; +} + +static char invalid_name[] = "bad"; + +char * +fake_modname(const char *name) +{ + const char *sp, *ep; + char *fp; + size_t len; + + sp = strrchr(name, '/'); + if (sp) + sp++; + else + sp = name; + ep = strrchr(name, '.'); + if (ep) { + if (ep == name) { + sp = invalid_name; + ep = invalid_name + sizeof(invalid_name) - 1; + } + } else + ep = name + strlen(name); + len = ep - sp; + fp = malloc(len + 1); + if (fp == NULL) + return NULL; + memcpy(fp, sp, len); + fp[len] = '\0'; + return fp; +} + +#if defined(__i386__) && __ELF_WORD_SIZE == 64 +struct mod_metadata64 { + int md_version; /* structure version MDTV_* */ + int md_type; /* type of entry MDT_* */ + u_int64_t md_data; /* specific data */ + u_int64_t md_cval; /* common string label */ +}; +#endif + +int +__elfN(parse_modmetadata)(struct preloaded_file *fp, elf_file_t ef) +{ + struct mod_metadata md; +#if defined(__i386__) && __ELF_WORD_SIZE == 64 + struct mod_metadata64 md64; +#endif + struct mod_depend *mdepend; + struct mod_version mver; + Elf_Sym sym; + char *s; + int modcnt, minfolen; + Elf_Addr v, p, p_stop; + + if (__elfN(lookup_symbol)(fp, ef, "__start_set_modmetadata_set", &sym) != 0) + return ENOENT; + p = sym.st_value + ef->off; + if (__elfN(lookup_symbol)(fp, ef, "__stop_set_modmetadata_set", &sym) != 0) + return ENOENT; + p_stop = sym.st_value + ef->off; + + modcnt = 0; + while (p < p_stop) { + COPYOUT(p, &v, sizeof(v)); +#ifdef __sparc64__ + __elfN(reloc_ptr)(fp, ef, p, &v, sizeof(v)); +#else + v += ef->off; +#endif +#if defined(__i386__) && __ELF_WORD_SIZE == 64 + COPYOUT(v, &md64, sizeof(md64)); + md.md_version = md64.md_version; + md.md_type = md64.md_type; + md.md_cval = (const char *)(uintptr_t)(md64.md_cval + ef->off); + md.md_data = (void *)(uintptr_t)(md64.md_data + ef->off); +#else + COPYOUT(v, &md, sizeof(md)); +#ifdef __sparc64__ + __elfN(reloc_ptr)(fp, ef, v, &md, sizeof(md)); +#else + md.md_cval += ef->off; + md.md_data += ef->off; +#endif +#endif + p += sizeof(Elf_Addr); + switch(md.md_type) { + case MDT_DEPEND: + if (ef->kernel) /* kernel must not depend on anything */ + break; + s = strdupout((vm_offset_t)md.md_cval); + minfolen = sizeof(*mdepend) + strlen(s) + 1; + mdepend = malloc(minfolen); + if (mdepend == NULL) + return ENOMEM; + COPYOUT((vm_offset_t)md.md_data, mdepend, sizeof(*mdepend)); + strcpy((char*)(mdepend + 1), s); + free(s); + file_addmetadata(fp, MODINFOMD_DEPLIST, minfolen, mdepend); + free(mdepend); + break; + case MDT_VERSION: + s = strdupout((vm_offset_t)md.md_cval); + COPYOUT((vm_offset_t)md.md_data, &mver, sizeof(mver)); + file_addmodule(fp, s, mver.mv_version, NULL); + free(s); + modcnt++; + break; + } + } + if (modcnt == 0) { + s = fake_modname(fp->f_name); + file_addmodule(fp, s, 1, NULL); + free(s); + } + return 0; +} + +static unsigned long +elf_hash(const char *name) +{ + const unsigned char *p = (const unsigned char *) name; + unsigned long h = 0; + unsigned long g; + + while (*p != '\0') { + h = (h << 4) + *p++; + if ((g = h & 0xf0000000) != 0) + h ^= g >> 24; + h &= ~g; + } + return h; +} + +static const char __elfN(bad_symtable)[] = "elf" __XSTRING(__ELF_WORD_SIZE) "_lookup_symbol: corrupt symbol table\n"; +int +__elfN(lookup_symbol)(struct preloaded_file *fp, elf_file_t ef, const char* name, + Elf_Sym *symp) +{ + Elf_Hashelt symnum; + Elf_Sym sym; + char *strp; + unsigned long hash; + + hash = elf_hash(name); + COPYOUT(&ef->buckets[hash % ef->nbuckets], &symnum, sizeof(symnum)); + + while (symnum != STN_UNDEF) { + if (symnum >= ef->nchains) { + printf(__elfN(bad_symtable)); + return ENOENT; + } + + COPYOUT(ef->symtab + symnum, &sym, sizeof(sym)); + if (sym.st_name == 0) { + printf(__elfN(bad_symtable)); + return ENOENT; + } + + strp = strdupout((vm_offset_t)(ef->strtab + sym.st_name)); + if (strcmp(name, strp) == 0) { + free(strp); + if (sym.st_shndx != SHN_UNDEF || + (sym.st_value != 0 && + ELF_ST_TYPE(sym.st_info) == STT_FUNC)) { + *symp = sym; + return 0; + } + return ENOENT; + } + free(strp); + COPYOUT(&ef->chains[symnum], &symnum, sizeof(symnum)); + } + return ENOENT; +} + +#ifdef __sparc__ +/* + * Apply any intra-module relocations to the value. *p is the load address + * of the value and val/len is the value to be modified. This does NOT modify + * the image in-place, because this is done by kern_linker later on. + */ +static void +__elfN(reloc_ptr)(struct preloaded_file *mp, elf_file_t ef, + void *p, void *val, size_t len) +{ + Elf_Addr off = (Elf_Addr)p - ef->off, word; + size_t n; + Elf_Rela r; + + for (n = 0; n < ef->relasz / sizeof(r); n++) { + COPYOUT(ef->rela + n, &r, sizeof(r)); + + if (r.r_offset >= off && r.r_offset < off + len && + ELF_R_TYPE(r.r_info) == R_SPARC_RELATIVE) { + word = ef->off + r.r_addend; + bcopy(&word, (char *)val + (r.r_offset - off), + sizeof(word)); + } + } +} +#endif diff --git a/sys/boot/common/load_elf32.c b/sys/boot/common/load_elf32.c new file mode 100644 index 0000000..1de5dc1 --- /dev/null +++ b/sys/boot/common/load_elf32.c @@ -0,0 +1,6 @@ +#include <sys/cdefs.h> +__FBSDID("$FreeBSD$"); + +#define __ELF_WORD_SIZE 32 + +#include "load_elf.c" diff --git a/sys/boot/common/load_elf64.c b/sys/boot/common/load_elf64.c new file mode 100644 index 0000000..c29e8e3 --- /dev/null +++ b/sys/boot/common/load_elf64.c @@ -0,0 +1,6 @@ +#include <sys/cdefs.h> +__FBSDID("$FreeBSD$"); + +#define __ELF_WORD_SIZE 64 + +#include "load_elf.c" diff --git a/sys/boot/common/loader.8 b/sys/boot/common/loader.8 new file mode 100644 index 0000000..2712da1 --- /dev/null +++ b/sys/boot/common/loader.8 @@ -0,0 +1,900 @@ +.\" Copyright (c) 1999 Daniel C. Sobral +.\" 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 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 AUTHOR 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. +.\" +.\" $FreeBSD$ +.\" +.\" Note: The date here should be updated whenever a non-trivial +.\" change is made to the manual page. +.Dd March 14, 1999 +.Dt LOADER 8 +.Os +.Sh NAME +.Nm loader +.Nd kernel bootstrapping final stage +.Sh DESCRIPTION +The program called +.Nm +is the final stage of +.Fx Ns 's +kernel bootstrapping process. +On IA32 (i386) architectures, it is a +.Pa BTX +client. +It is linked statically to +.Xr libstand 3 +and usually located in the directory +.Pa /boot . +.Pp +It provides a scripting language that can be used to +automate tasks, do pre-configuration or assist in recovery +procedures. +This scripting language is roughly divided in +two main components. +The smaller one is a set of commands +designed for direct use by the casual user, called "builtin +commands" for historical reasons. +The main drive behind these commands is user-friendliness. +The bigger component is an +.Tn ANS +Forth compatible Forth interpreter based on FICL, by +.An John Sadler . +.Pp +During initialization, +.Nm +will probe for a console and set the +.Va console +variable, or set it to serial console +.Pq Dq comconsole +if the previous boot stage used that. +Then, devices are probed, +.Va currdev +and +.Va loaddev +are set, and +.Va LINES +is set to 24. +Next, +.Tn FICL +is initialized, the builtin words are added to its vocabulary, and +.Pa /boot/boot.4th +is processed if it exists. +No disk switching is possible while that file is being read. +The inner interpreter +.Nm +will use with +.Tn FICL +is then set to +.Ic interpret , +which is +.Tn FICL Ns 's +default. +After that, +.Pa /boot/loader.rc +is processed if available, and, failing that, +.Pa /boot/boot.conf +is read for historical reasons. +These files are processed through the +.Ic include +command, which reads all of them into memory before processing them, +making disk changes possible. +.Pp +At this point, if an +.Ic autoboot +has not been tried, and if +.Va autoboot_delay +is not set to +.Dq NO +(not case sensitive), then an +.Ic autoboot +will be tried. +If the system gets past this point, +.Va prompt +will be set and +.Nm +will engage interactive mode. +.Sh BUILTIN COMMANDS +In +.Nm , +builtin commands take parameters from the command line. +Presently, +the only way to call them from a script is by using +.Pa evaluate +on a string. +If an error condition occurs, an exception will be generated, +which can be intercepted using +.Tn ANS +Forth exception handling +words. +If not intercepted, an error message will be displayed and +the interpreter's state will be reset, emptying the stack and restoring +interpreting mode. +.Pp +The builtin commands available are: +.Pp +.Bl -tag -width Ds -compact +.It Ic autoboot Op Ar seconds +Proceeds to bootstrap the system after a number of seconds, if not +interrupted by the user. +Displays a countdown prompt +warning the user the system is about to be booted, +unless interrupted by a key press. +The kernel will be loaded first if necessary. +Defaults to 10 seconds. +.Pp +.It Ic bcachestat +Displays statistics about disk cache usage. +For depuration only. +.Pp +.It Ic boot +.It Ic boot Ar kernelname Op Cm ... +.It Ic boot Fl flag Cm ... +Immediately proceeds to bootstrap the system, loading the kernel +if necessary. +Any flags or arguments are passed to the kernel, but they +must precede the kernel name, if a kernel name is provided. +.Pp +.Em WARNING : +The behavior of this builtin is changed if +.Xr loader.4th 8 +is loaded. +.Pp +.It Ic echo Xo +.Op Fl n +.Op Aq message +.Xc +Displays text on the screen. +A new line will be printed unless +.Fl n +is specified. +.Pp +.It Ic heap +Displays memory usage statistics. +For debugging purposes only. +.Pp +.It Ic help Op topic Op subtopic +Shows help messages read from +.Pa /boot/loader.help . +The special topic +.Em index +will list the topics available. +.Pp +.It Ic include Ar file Op Ar +Process script files. +Each file, in turn, is completely read into memory, +and then each of its lines is passed to the command line interpreter. +If any error is returned by the interpreter, the include +command aborts immediately, without reading any other files, and +returns an error itself (see +.Sx ERRORS ) . +.Pp +.It Ic load Xo +.Op Fl t Ar type +.Ar file Cm ... +.Xc +Loads a kernel, kernel loadable module (kld), or file of opaque +contents tagged as being of the type +.Ar type . +Kernel and modules can be either in a.out or ELF format. +Any arguments passed after the name of the file to be loaded +will be passed as arguments to that file. +Currently, argument passing does not work for the kernel. +.Pp +.It Ic ls Xo +.Op Fl l +.Op Ar path +.Xc +Displays a listing of files in the directory +.Ar path , +or the root directory if +.Ar path +is not specified. +If +.Fl l +is specified, file sizes will be shown too. +.Pp +.It Ic lsdev Op Fl v +Lists all of the devices from which it may be possible to load modules. +If +.Fl v +is specified, more details are printed. +.Pp +.It Ic lsmod Op Fl v +Displays loaded modules. +If +.Fl v +is specified, more details are shown. +.Pp +.It Ic more Ar file Op Ar +Display the files specified, with a pause at each +.Va LINES +displayed. +.Pp +.It Ic pnpscan Op Fl v +Scans for Plug-and-Play devices. +This is not functional at present. +.Pp +.It Ic read Xo +.Op Fl t Ar seconds +.Op Fl p Ar prompt +.Op Va variable +.Xc +Reads a line of input from the terminal, storing it in +.Va variable +if specified. +A timeout can be specified with +.Fl t , +though it will be canceled at the first key pressed. +A prompt may also be displayed through the +.Fl p +flag. +.Pp +.It Ic reboot +Immediately reboots the system. +.Pp +.It Ic set Ar variable +.It Ic set Ar variable Ns = Ns Ar value +Set loader's environment variables. +.Pp +.It Ic show Op Va variable +Displays the specified variable's value, or all variables and their +values if +.Va variable +is not specified. +.Pp +.It Ic unload +Remove all modules from memory. +.Pp +.It Ic unset Va variable +Removes +.Va variable +from the environment. +.Pp +.It Ic \&? +Same as +.Dq help index . +.Pp +.El +.Ss BUILTIN ENVIRONMENT VARIABLES +The +.Nm +has actually two different kinds of +.Sq environment +variables. +There are ANS Forth's +.Em environmental queries , +and a separate space of environment variables used by builtins, which +are not directly available to Forth words. +It is the latter type that this section covers. +.Pp +Environment variables can be set and unset through the +.Ic set +and +.Ic unset +builtins, and can have their values interactively examined through the +use of the +.Ic show +builtin. +Their values can also be accessed as described in +.Sx BUILTIN PARSER . +.Pp +Notice that these environment variables are not inherited by any shell +after the system has been booted. +.Pp +A few variables are set automatically by +.Nm . +Others can affect the behavior of either +.Nm +or the kernel at boot. +Some options may require a value, +while others define behavior just by being set. +Both types of builtin variables are described below. +.Bl -tag -width bootfile +.It Va acpi_load +Unset this to disable automatic loading of the ACPI module. +See also +.Va hint.acpi.0.disabled +in +.Xr device.hints 5 . +.It Va autoboot_delay +Number of seconds +.Ic autoboot +will wait before booting. +If this variable is not defined, +.Ic autoboot +will default to 10 seconds. +.Pp +If set to +.Dq NO , +no +.Ic autoboot +will be automatically attempted after processing +.Pa /boot/loader.rc , +though explicit +.Ic autoboot Ns 's +will be processed normally, defaulting to 10 seconds delay. +.It Va boot_askname +Instructs the kernel to prompt the user for the name of the root device +when the kernel is booted. +.It Va boot_ddb +Instructs the kernel to start in the DDB debugger, rather than +proceeding to initialize when booted. +.It Va boot_gdb +Selects gdb-remote mode for the kernel debugger by default. +.It Va boot_single +Prevents the kernel from initiating a multi-user startup; instead single-user +mode will be entered when the kernel has finished device probing. +.It Va boot_userconfig +Requests that the kernel's interactive device configuration program +be run when the kernel is booted. +.It Va boot_verbose +Setting this variable causes extra debugging information to be printed +by the kernel during the boot phase. +.It Va bootfile +List of semicolon-separated search path for bootable kernels. +The default is +.Dq Li kernel;kernel.old . +.It Va console +Defines the current console. +.It Va currdev +Selects the default device. +Syntax for devices is odd. +.It Va init_path +Sets the list of binaries which the kernel will try to run as the initial +process. +The first matching binary is used. +The default list is +.Dq Li /sbin/init:/sbin/oinit:/sbin/init.bak:/stand/sysinstall . +.It Va interpret +Has the value +.Dq Li ok +if the Forth's current state is interpreting. +.It Va LINES +Define the number of lines on the screen, to be used by the pager. +.It Va module_path +Sets the list of directories which will be searched for modules +named in a load command or implicitly required by a dependency. +The default value for this variable is +.Dq Li /boot/kernel;/boot/modules . +.It Va num_ide_disks +Sets the number of IDE disks as a workaround for some problems in +finding the root disk at boot. +This has been deprecated in favor of +.Va root_disk_unit . +.It Va prompt +Value of +.Nm Ns 's +prompt. +Defaults to +.Dq Li "${currdev}>" . +.It Va root_disk_unit +If the code which detects the disk unit number for the root disk is +confused, e.g. by a mix of SCSI and IDE disks, or IDE disks with +gaps in the sequence (e.g. no primary slave), the unit number can +be forced by setting this variable. +.It Va rootdev +By default the value of +.Va currdev +is used to set the root file system +when the kernel is booted. +This can be overridden by setting +.Va rootdev +explicitly. +.It Va dumpdev +The name of a device where the kernel can save a crash dump in case +of a panic. +This automatically sets the +.Va kern.dumpdev +.Xr sysctl 3 +MIB variable. +.El +.Pp +Other variables are used to override kernel tunable parameters. +The following tunables are available: +.Bl -tag -width Va +.It Va hw.physmem +Limit the amount of physical memory the system will use. +By default the size is in bytes, but the +.Cm k , K , m , M , g +and +.Cm G +suffixes +are also accepted and indicate kilobytes, megabytes and gigabytes +respectively. +An invalid suffix will result in the variable being ignored by the +kernel. +.It Va hw.pci.enable_io_modes +Enable PCI resources which are left off by some BIOSes or are not +enabled correctly by the device driver. +Tunable value set to ON (1) by default, but this may cause problems +with some peripherals. +.It Va hw.pci.allow_unsupported_io_range +Allow the PCI bridge to pass through an unsupported memory range +assigned by the BIOS. +Tunable value set to OFF (0) by default. +.It Va kern.maxusers +Set the size of a number of statically allocated system tables; see +.Xr tuning 7 +for a description of how to select an appropriate value for this +tunable. +When set, this tunable replaces the value declared in the kernel +compile-time configuration file. +.It Va kern.ipc.nmbclusters +Set the number of mbuf clusters to be allocated. +The value cannot be set below the default +determined when the kernel was compiled. +Modifies +.Va NMBCLUSTERS . +.It Va kern.ipc.nsfbufs +Set the number of +.Xr sendfile 2 +buffers to be allocated. +Overrides +.Dv NSFBUFS . +.It Va kern.vm.kmem.size +Sets the size of kernel memory (bytes). +This overrides the value determined when the kernel was compiled. +Modifies +.Va VM_KMEM_SIZE . +.It Va kern.maxswzone +Limits the amount of KVM to be used to hold swap +meta information, which directly governs the +maximum amount of swap the system can support. +This value is specified in bytes of KVA space +and defaults to around 70MBytes. +Care should be taken +to not reduce this value such that the actual +amount of configured swap exceeds 1/2 the +kernel-supported swap. +The default 70MB allows +the kernel to support a maximum of (approximately) +14GB of configured swap. +Only mess around with +this parameter if you need to greatly extend the +KVM reservation for other resources such as the +buffer cache or +.Va NMBCLUSTERS . +Modifies +.Va VM_SWZONE_SIZE_MAX . +.It Va kern.maxbcache +Limits the amount of KVM reserved for use by the +buffer cache, specified in bytes. +The default maximum is 200MB. +This parameter is used to +prevent the buffer cache from eating too much +KVM in large-memory machine configurations. +Only mess around with this parameter if you need to +greatly extend the KVM reservation for other resources +such as the swap zone or +.Va NMBCLUSTERS . +Note that +the NBUF parameter will override this limit. +Modifies +.Va VM_BCACHE_SIZE_MAX . +.It Va machdep.disable_mtrrs +Disable the use of i686 MTRRs (x86 only). +.It Va net.inet.tcp.tcbhashsize +Overrides the compile-time set value of +.Va TCBHASHSIZE +or the preset default of 512. +Must be a power of 2. +.El +.Ss BUILTIN PARSER +When a builtin command is executed, the rest of the line is taken +by it as arguments, and it is processed by a special parser which +is not used for regular Forth commands. +.Pp +This special parser applies the following rules to the parsed text: +.Pp +.Bl -enum +.It +All backslash characters are preprocessed. +.Bl -bullet +.It +\eb , \ef , \er , \en and \et are processed as in C. +.It +\es is converted to a space. +.It +\ev is converted to +.Tn ASCII +11. +.It +\ez is just skipped. +Useful for things like +.Dq \e0xf\ez\e0xf . +.It +\e0xN and \e0xNN are replaced by the hex N or NN. +.It +\eNNN is replaced by the octal NNN +.Tn ASCII +character. +.It +\e" , \e' and \e$ will escape these characters, preventing them from +receiving special treatment in Step 2, described below. +.It +\e\e will be replaced with a single \e . +.It +In any other occurrence, backslash will just be removed. +.El +.It +Every string between non-escaped quotes or double-quotes will be treated +as a single word for the purposes of the remaining steps. +.It +Replace any +.Li $VARIABLE +or +.Li ${VARIABLE} +with the value of the environment variable +.Va VARIABLE . +.It +Space-delimited arguments are passed to the called builtin command. +Spaces can also be escaped through the use of \e\e . +.El +.Pp +An exception to this parsing rule exists, and is described in +.Sx BUILTINS AND FORTH . +.Ss BUILTINS AND FORTH +All builtin words are state-smart, immediate words. +If interpreted, they behave exactly as described previously. +If they are compiled, though, +they extract their arguments from the stack instead of the command line. +.Pp +If compiled, the builtin words expect to find, at execution time, the +following parameters on the stack: +.D1 Ar addrN lenN ... addr2 len2 addr1 len1 N +where +.Ar addrX lenX +are strings which will compose the command line that will be parsed +into the builtin's arguments. +Internally, these strings are concatenated in from 1 to N, +with a space put between each one. +.Pp +If no arguments are passed, a 0 +.Em must +be passed, even if the builtin accepts no arguments. +.Pp +While this behavior has benefits, it has its trade-offs. +If the execution token of a builtin is acquired (through +.Ic ' +or +.Ic ['] ) , +and then passed to +.Ic catch +or +.Ic execute , +the builtin behavior will depend on the system state +.Bf Em +at the time +.Ic catch +or +.Ic execute +is processed +.Ef +\&! This is particularly annoying for programs that want or need to +handle exceptions. +In this case, the use of a proxy is recommended. +For example: +.Dl : (boot) boot ; +.Sh FICL +.Tn FICL +is a Forth interpreter written in C, in the form of a forth +virtual machine library that can be called by C functions and vice +versa. +.Pp +In +.Nm , +each line read interactively is then fed to +.Tn FICL , +which may call +.Nm +back to execute the builtin words. +The builtin +.Ic include +will also feed +.Tn FICL , +one line at a time. +.Pp +The words available to +.Tn FICL +can be classified into four groups. +The +.Tn ANS +Forth standard words, extra +.Tn FICL +words, extra +.Fx +words, and the builtin commands; +the latter were already described. +The +.Tn ANS +Forth standard words are listed in the +.Sx STANDARDS +section. +The words falling in the two other groups are described in the +following subsections. +.Ss FICL EXTRA WORDS +.Bl -tag -width wid-set-super +.It Ic .env +.It Ic .ver +.It Ic -roll +.It Ic 2constant +.It Ic >name +.It Ic body> +.It Ic compare +This is the STRING word set's +.Ic compare . +.It Ic compile-only +.It Ic endif +.It Ic forget-wid +.It Ic parse-word +.It Ic sliteral +This is the STRING word set's +.Ic sliteral . +.It Ic wid-set-super +.It Ic w@ +.It Ic w! +.It Ic x. +.It Ic empty +.It Ic cell- +.It Ic -rot +.El +.Ss FREEBSD EXTRA WORDS +.Bl -tag -width XXXXXXXX +.It Ic \&$ Pq -- +Evaluates the remainder of the input buffer, after having printed it first. +.It Ic \&% Pq -- +Evaluates the remainder of the input buffer under a +.Ic catch +exception guard. +.It Ic .# +Works like +.Ic . +but without outputting a trailing space. +.It Ic fclose Pq Ar fd -- +Closes a file. +.It Ic fkey Pq Ar fd -- char +Reads a single character from a file. +.It Ic fload Pq Ar fd -- +Processes a file +.Em fd . +.It Ic fopen Pq Ar addr len mode Li -- Ar fd +Opens a file. +Returns a file descriptor, or \-1 in case of failure. +The +.Ar mode +parameter selects whether the file is to be opened for read access, write +access, or both. +The constants +.Dv O_RDONLY , O_WRONLY , +and +.Dv O_RDWR +are defined in +.Pa /boot/support.4th , +indicating read only, write only, and read-write access, respectively. +.It Xo +.Ic fread +.Pq Ar fd addr len -- len' +.Xc +Tries to read +.Em len +bytes from file +.Em fd +into buffer +.Em addr . +Returns the actual number of bytes read, or -1 in case of error or end of +file. +.It Ic heap? Pq -- Ar cells +Return the space remaining in the dictionary heap, in cells. +This is not related to the heap used by dynamic memory allocation words. +.It Ic inb Pq Ar port -- char +Reads a byte from a port. +.It Ic key Pq -- Ar char +Reads a single character from the console. +.It Ic key? Pq -- Ar flag +Returns +.Ic true +if there is a character available to be read from the console. +.It Ic ms Pq Ar u -- +Waits +.Em u +microseconds. +.It Ic outb Pq Ar port char -- +Writes a byte to a port. +.It Ic seconds Pq -- Ar u +Returns the number of seconds since midnight. +.It Ic tib> Pq -- Ar addr len +Returns the remainder of the input buffer as a string on the stack. +.It Ic trace! Pq Ar flag -- +Activates or deactivates tracing. +Does not work with +.Ic catch . +.El +.Ss FREEBSD DEFINED ENVIRONMENTAL QUERIES +.Bl -tag -width Ds +.It arch-i386 +.Ic TRUE +if the architecture is IA32. +.It arch-alpha +.Ic TRUE +if the architecture is AXP. +.It FreeBSD_version +.Fx +version at compile time. +.It loader_version +.Nm +version. +.El +.Ss SYSTEM DOCUMENTATION +.Sh FILES +.Bl -tag -width /boot/defaults/loader.conf -compact +.It Pa /boot/loader +.Nm +itself. +.It Pa /boot/boot.4th +Additional +.Tn FICL +initialization. +.It Pa /boot/boot.conf +.Nm +bootstrapping script. +Deprecated. +.It Pa /boot/defaults/loader.conf +.It Pa /boot/loader.conf +.It Pa /boot/loader.conf.local +.Nm +configuration files, as described in +.Xr loader.conf 5 . +.It Pa /boot/loader.rc +.Nm +bootstrapping script. +.It Pa /boot/loader.help +Loaded by +.Ic help . +Contains the help messages. +.El +.Sh EXAMPLES +Boot in single user mode: +.Pp +.Dl boot -s +.Pp +Load kernel's user configuration file. +Notice that a kernel must be loaded before any other +.Ic load +command is attempted. +.Bd -literal -offset indent +load kernel +load -t userconfig_script /boot/kernel.conf +.Ed +.Pp +Load the kernel, a splash screen, and then autoboot in five seconds. +.Bd -literal -offset indent +load kernel +load splash_bmp +load -t splash_image_data /boot/chuckrulez.bmp +autoboot 5 +.Ed +.Pp +Set the disk unit of the root device to 2, and then boot. +This would be needed in a system with two IDE disks, +with the second IDE disk hardwired to wd2 instead of wd1. +.Bd -literal -offset indent +set root_disk_unit=2 +boot /kernel +.Ed +.Pp +See also: +.Bl -tag -width /usr/share/examples/bootforth/X +.It Pa /boot/loader.4th +Extra builtin-like words. +.It Pa /boot/support.4th +.Pa loader.conf +processing words. +.It Pa /usr/share/examples/bootforth/ +Assorted examples. +.El +.Sh ERRORS +The following values are thrown by +.Nm : +.Bl -tag -width XXXXX -offset indent +.It 100 +Any type of error in the processing of a builtin. +.It -1 +.Ic Abort +executed. +.It -2 +.Ic Abort" +executed. +.It -56 +.Ic Quit +executed. +.It -256 +Out of interpreting text. +.It -257 +Need more text to succeed -- will finish on next run. +.It -258 +.Ic Bye +executed. +.It -259 +Unspecified error. +.El +.Sh SEE ALSO +.Xr libstand 3 , +.Xr loader.conf 5 , +.Xr tuning 7 , +.Xr boot 8 , +.Xr btxld 8 +.Sh STANDARDS +For the purposes of ANS Forth compliance, loader is an +.Bf Em +ANS Forth System with Environmental Restrictions, Providing +.Ef +.Bf Li +.No .( , +.No :noname , +.No ?do , +parse, pick, roll, refill, to, value, \e, false, true, +.No <> , +.No 0<> , +compile\&, , erase, nip, tuck +.Ef +.Em and +.Li marker +.Bf Em +from the Core Extensions word set, Providing the Exception Extensions +word set, Providing the Locals Extensions word set, Providing the +Memory-Allocation Extensions word set, Providing +.Ef +.Bf Li +\&.s, +bye, forget, see, words, +\&[if], +\&[else] +.Ef +.Em and +.Li [then] +.Bf Em +from the Programming-Tools extension word set, Providing the +Search-Order extensions word set. +.Ef +.Sh HISTORY +The +.Nm +first appeared in +.Fx 3.1 . +.Sh AUTHORS +.An -nosplit +The +.Nm +was written by +.An Michael Smith Aq msmith@FreeBSD.org . +.Pp +.Tn FICL +was written by +.An John Sadler Aq john_sadler@alum.mit.edu . +.Sh BUGS +The +.Ic expect +and +.Ic accept +words will read from the input buffer instead of the console. +The latter will be fixed, but the former will not. diff --git a/sys/boot/common/ls.c b/sys/boot/common/ls.c new file mode 100644 index 0000000..86f83c3 --- /dev/null +++ b/sys/boot/common/ls.c @@ -0,0 +1,184 @@ +/* + * $NetBSD: ls.c,v 1.3 1997/06/13 13:48:47 drochner Exp $ + */ + +/*- + * Copyright (c) 1993 + * The Regents of the University of California. All rights reserved. + * Copyright (c) 1996 + * Matthias Drochner. 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 the University of + * California, Berkeley and its contributors. + * 4. 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. + */ + +#include <sys/cdefs.h> +__FBSDID("$FreeBSD$"); + +#include <sys/param.h> +#include <ufs/ufs/dinode.h> +#include <ufs/ufs/dir.h> + +#include <stand.h> +#include <string.h> + +#include "bootstrap.h" + +static char typestr[] = "?fc?d?b? ?l?s?w"; + +static int ls_getdir(char **pathp); + +COMMAND_SET(ls, "ls", "list files", command_ls); + +static int +command_ls(int argc, char *argv[]) +{ + int fd; + struct stat sb; + struct dirent *d; + char *buf, *path; + char lbuf[128]; /* one line */ + int result, ch; + int verbose; + + result = CMD_OK; + fd = -1; + verbose = 0; + optind = 1; + optreset = 1; + while ((ch = getopt(argc, argv, "l")) != -1) { + switch(ch) { + case 'l': + verbose = 1; + break; + case '?': + default: + /* getopt has already reported an error */ + return(CMD_OK); + } + } + argv += (optind - 1); + argc -= (optind - 1); + + if (argc < 2) { + path = ""; + } else { + path = argv[1]; + } + + fd = ls_getdir(&path); + if (fd == -1) { + result = CMD_ERROR; + goto out; + } + pager_open(); + pager_output(path); + pager_output("\n"); + + while ((d = readdirfd(fd)) != NULL) { + if (strcmp(d->d_name, ".") && strcmp(d->d_name, "..")) { + if (verbose) { + /* stat the file, if possible */ + sb.st_size = 0; + buf = malloc(strlen(path) + strlen(d->d_name) + 2); + sprintf(buf, "%s/%s", path, d->d_name); + /* ignore return, could be symlink, etc. */ + if (stat(buf, &sb)) + sb.st_size = 0; + free(buf); + sprintf(lbuf, " %c %8d %s\n", typestr[d->d_type], + (int)sb.st_size, d->d_name); + } else { + sprintf(lbuf, " %c %s\n", typestr[d->d_type], d->d_name); + } + if (pager_output(lbuf)) + goto out; + } + } + out: + pager_close(); + if (fd != -1) + close(fd); + if (path != NULL) + free(path); + return(result); +} + +/* + * Given (path) containing a vaguely reasonable path specification, return an fd + * on the directory, and an allocated copy of the path to the directory. + */ +static int +ls_getdir(char **pathp) +{ + struct stat sb; + int fd; + const char *cp; + char *path, *tail; + + tail = NULL; + fd = -1; + + /* one extra byte for a possible trailing slash required */ + path = malloc(strlen(*pathp) + 2); + strcpy(path, *pathp); + + /* Make sure the path is respectable to begin with */ + if (archsw.arch_getdev(NULL, path, &cp)) { + sprintf(command_errbuf, "bad path '%s'", path); + goto out; + } + + /* If there's no path on the device, assume '/' */ + if (*cp == 0) + strcat(path, "/"); + + fd = open(path, O_RDONLY); + if (fd < 0) { + sprintf(command_errbuf, "open '%s' failed: %s", path, strerror(errno)); + goto out; + } + if (fstat(fd, &sb) < 0) { + sprintf(command_errbuf, "stat failed: %s", strerror(errno)); + goto out; + } + if (!S_ISDIR(sb.st_mode)) { + sprintf(command_errbuf, "%s: %s", path, strerror(ENOTDIR)); + goto out; + } + + *pathp = path; + return(fd); + + out: + free(path); + *pathp = NULL; + if (fd != -1) + close(fd); + return(-1); +} diff --git a/sys/boot/common/merge_help.awk b/sys/boot/common/merge_help.awk new file mode 100644 index 0000000..02fbc4a --- /dev/null +++ b/sys/boot/common/merge_help.awk @@ -0,0 +1,101 @@ +#!/usr/bin/awk -f +# +# $FreeBSD$ +# +# Merge two boot loader help files for FreeBSD 3.0 +# Joe Abley <jabley@patho.gen.nz> + +BEGIN \ +{ + state = 0; + first = -1; + ind = 0; +} + +# beginning of first command +/^###/ && (state == 0) \ +{ + state = 1; + next; +} + +# entry header +/^# T[[:graph:]]+ (S[[:graph:]]+ )*D[[:graph:]][[:print:]]*$/ && (state == 1) \ +{ + match($0, " T[[:graph:]]+"); + T = substr($0, RSTART + 2, RLENGTH - 2); + match($0, " S[[:graph:]]+"); + S = (RLENGTH == -1) ? "" : substr($0, RSTART + 2, RLENGTH - 2); + match($0, " D[[:graph:]][[:print:]]*$"); + D = substr($0, RSTART + 2); + + # find a suitable place to store this one... + ind++; + if (ind == 1) + { + first = ind; + help[ind, "T"] = T; + help[ind, "S"] = S; + help[ind, "link"] = -1; + } else { + i = first; j = -1; + while (help[i, "T"] help[i, "S"] < T S) + { + j = i; + i = help[i, "link"]; + if (i == -1) break; + } + + if (i == -1) + { + help[j, "link"] = ind; + help[ind, "link"] = -1; + } else { + help[ind, "link"] = i; + if (j == -1) + first = ind; + else + help[j, "link"] = ind; + } + } + help[ind, "T"] = T; + help[ind, "S"] = S; + help[ind, "D"] = D; + + # set our state + state = 2; + help[ind, "text"] = 0; + next; +} + +# end of last command, beginning of next one +/^###/ && (state == 2) \ +{ + state = 1; +} + +(state == 2) \ +{ + sub("[[:blank:]]+$", ""); + if (help[ind, "text"] == 0 && $0 ~ /^[[:blank:]]*$/) next; + help[ind, "text", help[ind, "text"]] = $0; + help[ind, "text"]++; + next; +} + +# show them what we have (it's already sorted in help[]) +END \ +{ + node = first; + while (node != -1) + { + printf "################################################################################\n"; + printf "# T%s ", help[node, "T"]; + if (help[node, "S"] != "") printf "S%s ", help[node, "S"]; + printf "D%s\n\n", help[node, "D"]; + for (i = 0; i < help[node, "text"]; i++) + printf "%s\n", help[node, "text", i]; + node = help[node, "link"]; + } + printf "################################################################################\n"; +} diff --git a/sys/boot/common/misc.c b/sys/boot/common/misc.c new file mode 100644 index 0000000..9f38b60 --- /dev/null +++ b/sys/boot/common/misc.c @@ -0,0 +1,145 @@ +/*- + * Copyright (c) 1998 Michael Smith <msmith@freebsd.org> + * 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 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 AUTHOR 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. + */ + +#include <sys/cdefs.h> +__FBSDID("$FreeBSD$"); + +#include <string.h> +#include <stand.h> +#include <bootstrap.h> + +/* + * Concatenate the (argc) elements of (argv) into a single string, and return + * a copy of same. + */ +char * +unargv(int argc, char *argv[]) +{ + size_t hlong; + int i; + char *cp; + + for (hlong = 0, i = 0, hlong = 0; i < argc; i++) + hlong += strlen(argv[i]) + 2; + + if(hlong == 0) + return(NULL); + + cp = malloc(hlong); + cp[0] = 0; + for (i = 0; i < argc; i++) { + strcat(cp, argv[i]); + if (i < (argc - 1)) + strcat(cp, " "); + } + + return(cp); +} + +/* + * Get the length of a string in kernel space + */ +size_t +strlenout(vm_offset_t src) +{ + char c; + size_t len; + + for (len = 0; ; len++) { + archsw.arch_copyout(src++, &c, 1); + if (c == 0) + break; + } + return(len); +} + +/* + * Make a duplicate copy of a string in kernel space + */ +char * +strdupout(vm_offset_t str) +{ + char *result, *cp; + + result = malloc(strlenout(str) + 1); + for (cp = result; ;cp++) { + archsw.arch_copyout(str++, cp, 1); + if (*cp == 0) + break; + } + return(result); +} + +/* + * Display a region in traditional hexdump format. + */ +void +hexdump(caddr_t region, size_t len) +{ + caddr_t line; + int x, c; + char lbuf[80]; +#define emit(fmt, args...) {sprintf(lbuf, fmt , ## args); pager_output(lbuf);} + + pager_open(); + for (line = region; line < (region + len); line += 16) { + emit("%08lx ", (long) line); + + for (x = 0; x < 16; x++) { + if ((line + x) < (region + len)) { + emit("%02x ", *(u_int8_t *)(line + x)); + } else { + emit("-- "); + } + if (x == 7) + emit(" "); + } + emit(" |"); + for (x = 0; x < 16; x++) { + if ((line + x) < (region + len)) { + c = *(u_int8_t *)(line + x); + if ((c < ' ') || (c > '~')) /* !isprint(c) */ + c = '.'; + emit("%c", c); + } else { + emit(" "); + } + } + emit("|\n"); + } + pager_close(); +} + +void +dev_cleanup(void) +{ + int i; + + /* Call cleanup routines */ + for (i = 0; devsw[i] != NULL; ++i) + if (devsw[i]->dv_cleanup != NULL) + (devsw[i]->dv_cleanup)(); +} diff --git a/sys/boot/common/module.c b/sys/boot/common/module.c new file mode 100644 index 0000000..ae93426 --- /dev/null +++ b/sys/boot/common/module.c @@ -0,0 +1,961 @@ +/*- + * Copyright (c) 1998 Michael Smith <msmith@freebsd.org> + * 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 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 AUTHOR 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. + */ + +#include <sys/cdefs.h> +__FBSDID("$FreeBSD$"); + +/* + * file/module function dispatcher, support, etc. + */ + +#include <stand.h> +#include <string.h> +#include <sys/param.h> +#include <sys/linker.h> +#include <sys/module.h> +#include <sys/queue.h> + +#include "bootstrap.h" + +#define MDIR_REMOVED 0x0001 +#define MDIR_NOHINTS 0x0002 + +struct moduledir { + char *d_path; /* path of modules directory */ + u_char *d_hints; /* content of linker.hints file */ + int d_hintsz; /* size of hints data */ + int d_flags; + STAILQ_ENTRY(moduledir) d_link; +}; + +static int file_load(char *filename, vm_offset_t dest, struct preloaded_file **result); +static int file_loadraw(char *type, char *name); +static int file_load_dependencies(struct preloaded_file *base_mod); +static char * file_search(const char *name, char **extlist); +static struct kernel_module * file_findmodule(struct preloaded_file *fp, char *modname, struct mod_depend *verinfo); +static int file_havepath(const char *name); +static char *mod_searchmodule(char *name, struct mod_depend *verinfo); +static void file_insert_tail(struct preloaded_file *mp); +struct file_metadata* metadata_next(struct file_metadata *base_mp, int type); +static void moduledir_readhints(struct moduledir *mdp); +static void moduledir_rebuild(void); + +/* load address should be tweaked by first module loaded (kernel) */ +static vm_offset_t loadaddr = 0; + +static const char *default_searchpath ="/boot/kernel;/boot/modules"; + +static STAILQ_HEAD(, moduledir) moduledir_list = STAILQ_HEAD_INITIALIZER(moduledir_list); + +struct preloaded_file *preloaded_files = NULL; + +static char *kld_ext_list[] = { + ".ko", + "", + NULL +}; + + +/* + * load an object, either a disk file or code module. + * + * To load a file, the syntax is: + * + * load -t <type> <path> + * + * code modules are loaded as: + * + * load <path> <options> + */ + +COMMAND_SET(load, "load", "load a kernel or module", command_load); + +static int +command_load(int argc, char *argv[]) +{ + char *typestr; + int dofile, dokld, ch, error; + + dokld = dofile = 0; + optind = 1; + optreset = 1; + typestr = NULL; + if (argc == 1) { + command_errmsg = "no filename specified"; + return(CMD_ERROR); + } + while ((ch = getopt(argc, argv, "kt:")) != -1) { + switch(ch) { + case 'k': + dokld = 1; + break; + case 't': + typestr = optarg; + dofile = 1; + break; + case '?': + default: + /* getopt has already reported an error */ + return(CMD_OK); + } + } + argv += (optind - 1); + argc -= (optind - 1); + + /* + * Request to load a raw file? + */ + if (dofile) { + if ((argc != 2) || (typestr == NULL) || (*typestr == 0)) { + command_errmsg = "invalid load type"; + return(CMD_ERROR); + } + return(file_loadraw(typestr, argv[1])); + } + /* + * Do we have explicit KLD load ? + */ + if (dokld || file_havepath(argv[1])) { + error = mod_loadkld(argv[1], argc - 2, argv + 2); + if (error == EEXIST) + sprintf(command_errbuf, "warning: KLD '%s' already loaded", argv[1]); + return (error == 0 ? CMD_OK : CMD_ERROR); + } + /* + * Looks like a request for a module. + */ + error = mod_load(argv[1], NULL, argc - 2, argv + 2); + if (error == EEXIST) + sprintf(command_errbuf, "warning: module '%s' already loaded", argv[1]); + return (error == 0 ? CMD_OK : CMD_ERROR); +} + +COMMAND_SET(unload, "unload", "unload all modules", command_unload); + +static int +command_unload(int argc, char *argv[]) +{ + struct preloaded_file *fp; + + while (preloaded_files != NULL) { + fp = preloaded_files; + preloaded_files = preloaded_files->f_next; + file_discard(fp); + } + loadaddr = 0; + unsetenv("kernelname"); + return(CMD_OK); +} + +COMMAND_SET(lsmod, "lsmod", "list loaded modules", command_lsmod); + +static int +command_lsmod(int argc, char *argv[]) +{ + struct preloaded_file *fp; + struct kernel_module *mp; + struct file_metadata *md; + char lbuf[80]; + int ch, verbose; + + verbose = 0; + optind = 1; + optreset = 1; + while ((ch = getopt(argc, argv, "v")) != -1) { + switch(ch) { + case 'v': + verbose = 1; + break; + case '?': + default: + /* getopt has already reported an error */ + return(CMD_OK); + } + } + + pager_open(); + for (fp = preloaded_files; fp; fp = fp->f_next) { + sprintf(lbuf, " %p: %s (%s, 0x%lx)\n", + (void *) fp->f_addr, fp->f_name, fp->f_type, (long) fp->f_size); + pager_output(lbuf); + if (fp->f_args != NULL) { + pager_output(" args: "); + pager_output(fp->f_args); + pager_output("\n"); + } + if (fp->f_modules) { + pager_output(" modules: "); + for (mp = fp->f_modules; mp; mp = mp->m_next) { + sprintf(lbuf, "%s.%d ", mp->m_name, mp->m_version); + pager_output(lbuf); + } + pager_output("\n"); + } + if (verbose) { + /* XXX could add some formatting smarts here to display some better */ + for (md = fp->f_metadata; md != NULL; md = md->md_next) { + sprintf(lbuf, " 0x%04x, 0x%lx\n", md->md_type, (long) md->md_size); + pager_output(lbuf); + } + } + } + pager_close(); + return(CMD_OK); +} + +/* + * File level interface, functions file_* + */ +int +file_load(char *filename, vm_offset_t dest, struct preloaded_file **result) +{ + struct preloaded_file *fp; + int error; + int i; + + error = EFTYPE; + for (i = 0, fp = NULL; file_formats[i] && fp == NULL; i++) { + error = (file_formats[i]->l_load)(filename, loadaddr, &fp); + if (error == 0) { + fp->f_loader = i; /* remember the loader */ + *result = fp; + break; + } + if (error == EFTYPE) + continue; /* Unknown to this handler? */ + if (error) { + sprintf(command_errbuf, "can't load file '%s': %s", + filename, strerror(error)); + break; + } + } + return (error); +} + +static int +file_load_dependencies(struct preloaded_file *base_file) { + struct file_metadata *md; + struct preloaded_file *fp; + struct mod_depend *verinfo; + struct kernel_module *mp; + char *dmodname; + int error; + + md = file_findmetadata(base_file, MODINFOMD_DEPLIST); + if (md == NULL) + return (0); + error = 0; + do { + verinfo = (struct mod_depend*)md->md_data; + dmodname = (char *)(verinfo + 1); + if (file_findmodule(NULL, dmodname, verinfo) == NULL) { + printf("loading required module '%s'\n", dmodname); + error = mod_load(dmodname, verinfo, 0, NULL); + if (error) + break; + /* + * If module loaded via kld name which isn't listed + * in the linker.hints file, we should check if it have + * required version. + */ + mp = file_findmodule(NULL, dmodname, verinfo); + if (mp == NULL) { + sprintf(command_errbuf, "module '%s' exists but with wrong version", + dmodname); + error = ENOENT; + break; + } + } + md = metadata_next(md, MODINFOMD_DEPLIST); + } while (md); + if (!error) + return (0); + /* Load failed; discard everything */ + while (base_file != NULL) { + fp = base_file; + base_file = base_file->f_next; + file_discard(fp); + } + return (error); +} +/* + * We've been asked to load (name) as (type), so just suck it in, + * no arguments or anything. + */ +int +file_loadraw(char *type, char *name) +{ + struct preloaded_file *fp; + char *cp; + int fd, got; + vm_offset_t laddr; + + /* We can't load first */ + if ((file_findfile(NULL, NULL)) == NULL) { + command_errmsg = "can't load file before kernel"; + return(CMD_ERROR); + } + + /* locate the file on the load path */ + cp = file_search(name, NULL); + if (cp == NULL) { + sprintf(command_errbuf, "can't find '%s'", name); + return(CMD_ERROR); + } + name = cp; + + if ((fd = open(name, O_RDONLY)) < 0) { + sprintf(command_errbuf, "can't open '%s': %s", name, strerror(errno)); + free(name); + return(CMD_ERROR); + } + + laddr = loadaddr; + for (;;) { + /* read in 4k chunks; size is not really important */ + got = archsw.arch_readin(fd, laddr, 4096); + if (got == 0) /* end of file */ + break; + if (got < 0) { /* error */ + sprintf(command_errbuf, "error reading '%s': %s", name, strerror(errno)); + free(name); + close(fd); + return(CMD_ERROR); + } + laddr += got; + } + + /* Looks OK so far; create & populate control structure */ + fp = file_alloc(); + fp->f_name = name; + fp->f_type = strdup(type); + fp->f_args = NULL; + fp->f_metadata = NULL; + fp->f_loader = -1; + fp->f_addr = loadaddr; + fp->f_size = laddr - loadaddr; + + /* recognise space consumption */ + loadaddr = laddr; + + /* Add to the list of loaded files */ + file_insert_tail(fp); + close(fd); + return(CMD_OK); +} + +/* + * Load the module (name), pass it (argc),(argv), add container file + * to the list of loaded files. + * If module is already loaded just assign new argc/argv. + */ +int +mod_load(char *modname, struct mod_depend *verinfo, int argc, char *argv[]) +{ + struct kernel_module *mp; + int err; + char *filename; + + if (file_havepath(modname)) { + printf("Warning: mod_load() called instead of mod_loadkld() for module '%s'\n", modname); + return (mod_loadkld(modname, argc, argv)); + } + /* see if module is already loaded */ + mp = file_findmodule(NULL, modname, verinfo); + if (mp) { +#ifdef moduleargs + if (mp->m_args) + free(mp->m_args); + mp->m_args = unargv(argc, argv); +#endif + sprintf(command_errbuf, "warning: module '%s' already loaded", mp->m_name); + return (0); + } + /* locate file with the module on the search path */ + filename = mod_searchmodule(modname, verinfo); + if (filename == NULL) { + sprintf(command_errbuf, "can't find '%s'", modname); + return (ENOENT); + } + err = mod_loadkld(filename, argc, argv); + return (err); +} + +/* + * Load specified KLD. If path is omitted, then try to locate it via + * search path. + */ +int +mod_loadkld(const char *kldname, int argc, char *argv[]) +{ + struct preloaded_file *fp, *last_file; + int err; + char *filename; + + /* + * Get fully qualified KLD name + */ + filename = file_search(kldname, kld_ext_list); + if (filename == NULL) { + sprintf(command_errbuf, "can't find '%s'", kldname); + return (ENOENT); + } + /* + * Check if KLD already loaded + */ + fp = file_findfile(filename, NULL); + if (fp) { + sprintf(command_errbuf, "warning: KLD '%s' already loaded", filename); + free(filename); + return (0); + } + for (last_file = preloaded_files; + last_file != NULL && last_file->f_next != NULL; + last_file = last_file->f_next) + ; + + do { + err = file_load(filename, loadaddr, &fp); + if (err) + break; + fp->f_args = unargv(argc, argv); + loadaddr = fp->f_addr + fp->f_size; + file_insert_tail(fp); /* Add to the list of loaded files */ + if (file_load_dependencies(fp) != 0) { + err = ENOENT; + last_file->f_next = NULL; + loadaddr = last_file->f_addr + last_file->f_size; + fp = NULL; + break; + } + } while(0); + if (err == EFTYPE) + sprintf(command_errbuf, "don't know how to load module '%s'", filename); + if (err && fp) + file_discard(fp); + free(filename); + return (err); +} + +/* + * Find a file matching (name) and (type). + * NULL may be passed as a wildcard to either. + */ +struct preloaded_file * +file_findfile(char *name, char *type) +{ + struct preloaded_file *fp; + + for (fp = preloaded_files; fp != NULL; fp = fp->f_next) { + if (((name == NULL) || !strcmp(name, fp->f_name)) && + ((type == NULL) || !strcmp(type, fp->f_type))) + break; + } + return (fp); +} + +/* + * Find a module matching (name) inside of given file. + * NULL may be passed as a wildcard. + */ +struct kernel_module * +file_findmodule(struct preloaded_file *fp, char *modname, + struct mod_depend *verinfo) +{ + struct kernel_module *mp, *best; + int bestver, mver; + + if (fp == NULL) { + for (fp = preloaded_files; fp; fp = fp->f_next) { + mp = file_findmodule(fp, modname, verinfo); + if (mp) + return (mp); + } + return (NULL); + } + best = NULL; + bestver = 0; + for (mp = fp->f_modules; mp; mp = mp->m_next) { + if (strcmp(modname, mp->m_name) == 0) { + if (verinfo == NULL) + return (mp); + mver = mp->m_version; + if (mver == verinfo->md_ver_preferred) + return (mp); + if (mver >= verinfo->md_ver_minimum && + mver <= verinfo->md_ver_maximum && + mver > bestver) { + best = mp; + bestver = mver; + } + } + } + return (best); +} +/* + * Make a copy of (size) bytes of data from (p), and associate them as + * metadata of (type) to the module (mp). + */ +void +file_addmetadata(struct preloaded_file *fp, int type, size_t size, void *p) +{ + struct file_metadata *md; + + md = malloc(sizeof(struct file_metadata) - sizeof(md->md_data) + size); + md->md_size = size; + md->md_type = type; + bcopy(p, md->md_data, size); + md->md_next = fp->f_metadata; + fp->f_metadata = md; +} + +/* + * Find a metadata object of (type) associated with the file (fp) + */ +struct file_metadata * +file_findmetadata(struct preloaded_file *fp, int type) +{ + struct file_metadata *md; + + for (md = fp->f_metadata; md != NULL; md = md->md_next) + if (md->md_type == type) + break; + return(md); +} + +struct file_metadata * +metadata_next(struct file_metadata *md, int type) +{ + if (md == NULL) + return (NULL); + while((md = md->md_next) != NULL) + if (md->md_type == type) + break; + return (md); +} + +static char *emptyextlist[] = { "", NULL }; + +/* + * Check if the given file is in place and return full path to it. + */ +static char * +file_lookup(const char *path, const char *name, int namelen, char **extlist) +{ + struct stat st; + char *result, *cp, **cpp; + int pathlen, extlen, len; + + pathlen = strlen(path); + extlen = 0; + if (extlist == NULL) + extlist = emptyextlist; + for (cpp = extlist; *cpp; cpp++) { + len = strlen(*cpp); + if (len > extlen) + extlen = len; + } + result = malloc(pathlen + namelen + extlen + 2); + if (result == NULL) + return (NULL); + bcopy(path, result, pathlen); + if (pathlen > 0 && result[pathlen - 1] != '/') + result[pathlen++] = '/'; + cp = result + pathlen; + bcopy(name, cp, namelen); + cp += namelen; + for (cpp = extlist; *cpp; cpp++) { + strcpy(cp, *cpp); + if (stat(result, &st) == 0 && S_ISREG(st.st_mode)) + return result; + } + free(result); + return NULL; +} + +/* + * Check if file name have any qualifiers + */ +static int +file_havepath(const char *name) +{ + const char *cp; + + archsw.arch_getdev(NULL, name, &cp); + return (cp != name || strchr(name, '/') != NULL); +} + +/* + * Attempt to find the file (name) on the module searchpath. + * If (name) is qualified in any way, we simply check it and + * return it or NULL. If it is not qualified, then we attempt + * to construct a path using entries in the environment variable + * module_path. + * + * The path we return a pointer to need never be freed, as we manage + * it internally. + */ +static char * +file_search(const char *name, char **extlist) +{ + struct moduledir *mdp; + struct stat sb; + char *result; + int namelen; + + /* Don't look for nothing */ + if (name == NULL) + return(NULL); + + if (*name == 0) + return(strdup(name)); + + if (file_havepath(name)) { + /* Qualified, so just see if it exists */ + if (stat(name, &sb) == 0) + return(strdup(name)); + return(NULL); + } + moduledir_rebuild(); + result = NULL; + namelen = strlen(name); + STAILQ_FOREACH(mdp, &moduledir_list, d_link) { + result = file_lookup(mdp->d_path, name, namelen, extlist); + if (result) + break; + } + return(result); +} + +#define INT_ALIGN(base, ptr) ptr = \ + (base) + (((ptr) - (base) + sizeof(int) - 1) & ~(sizeof(int) - 1)) + +static char * +mod_search_hints(struct moduledir *mdp, const char *modname, + struct mod_depend *verinfo) +{ + u_char *cp, *recptr, *bufend, *best; + char *result; + int *intp, bestver, blen, clen, found, ival, modnamelen, reclen; + + moduledir_readhints(mdp); + modnamelen = strlen(modname); + found = 0; + result = NULL; + bestver = 0; + if (mdp->d_hints == NULL) + goto bad; + recptr = mdp->d_hints; + bufend = recptr + mdp->d_hintsz; + clen = blen = 0; + best = cp = NULL; + while (recptr < bufend && !found) { + intp = (int*)recptr; + reclen = *intp++; + ival = *intp++; + cp = (char*)intp; + switch (ival) { + case MDT_VERSION: + clen = *cp++; + if (clen != modnamelen || bcmp(cp, modname, clen) != 0) + break; + cp += clen; + INT_ALIGN(mdp->d_hints, cp); + ival = *(int*)cp; + cp += sizeof(int); + clen = *cp++; + if (verinfo == NULL || ival == verinfo->md_ver_preferred) { + found = 1; + break; + } + if (ival >= verinfo->md_ver_minimum && + ival <= verinfo->md_ver_maximum && + ival > bestver) { + bestver = ival; + best = cp; + blen = clen; + } + break; + default: + break; + } + recptr += reclen + sizeof(int); + } + /* + * Finally check if KLD is in the place + */ + if (found) + result = file_lookup(mdp->d_path, cp, clen, NULL); + else if (best) + result = file_lookup(mdp->d_path, best, blen, NULL); +bad: + /* + * If nothing found or hints is absent - fallback to the old way + * by using "kldname[.ko]" as module name. + */ + if (!found && !bestver && result == NULL) + result = file_lookup(mdp->d_path, modname, modnamelen, kld_ext_list); + return result; +} + +/* + * Attempt to locate the file containing the module (name) + */ +static char * +mod_searchmodule(char *name, struct mod_depend *verinfo) +{ + struct moduledir *mdp; + char *result; + + moduledir_rebuild(); + /* + * Now we ready to lookup module in the given directories + */ + result = NULL; + STAILQ_FOREACH(mdp, &moduledir_list, d_link) { + result = mod_search_hints(mdp, name, verinfo); + if (result) + break; + } + + return(result); +} + +int +file_addmodule(struct preloaded_file *fp, char *modname, int version, + struct kernel_module **newmp) +{ + struct kernel_module *mp; + struct mod_depend mdepend; + + bzero(&mdepend, sizeof(mdepend)); + mdepend.md_ver_preferred = version; + mp = file_findmodule(fp, modname, &mdepend); + if (mp) + return (EEXIST); + mp = malloc(sizeof(struct kernel_module)); + if (mp == NULL) + return (ENOMEM); + bzero(mp, sizeof(struct kernel_module)); + mp->m_name = strdup(modname); + mp->m_version = version; + mp->m_fp = fp; + mp->m_next = fp->f_modules; + fp->f_modules = mp; + if (newmp) + *newmp = mp; + return (0); +} + +/* + * Throw a file away + */ +void +file_discard(struct preloaded_file *fp) +{ + struct file_metadata *md, *md1; + struct kernel_module *mp, *mp1; + if (fp == NULL) + return; + md = fp->f_metadata; + while (md) { + md1 = md; + md = md->md_next; + free(md1); + } + mp = fp->f_modules; + while (mp) { + if (mp->m_name) + free(mp->m_name); + mp1 = mp; + mp = mp->m_next; + free(mp1); + } + if (fp->f_name != NULL) + free(fp->f_name); + if (fp->f_type != NULL) + free(fp->f_type); + if (fp->f_args != NULL) + free(fp->f_args); + free(fp); +} + +/* + * Allocate a new file; must be used instead of malloc() + * to ensure safe initialisation. + */ +struct preloaded_file * +file_alloc(void) +{ + struct preloaded_file *fp; + + if ((fp = malloc(sizeof(struct preloaded_file))) != NULL) { + bzero(fp, sizeof(struct preloaded_file)); + } + return (fp); +} + +/* + * Add a module to the chain + */ +static void +file_insert_tail(struct preloaded_file *fp) +{ + struct preloaded_file *cm; + + /* Append to list of loaded file */ + fp->f_next = NULL; + if (preloaded_files == NULL) { + preloaded_files = fp; + } else { + for (cm = preloaded_files; cm->f_next != NULL; cm = cm->f_next) + ; + cm->f_next = fp; + } +} + +static char * +moduledir_fullpath(struct moduledir *mdp, const char *fname) +{ + char *cp; + + cp = malloc(strlen(mdp->d_path) + strlen(fname) + 2); + if (cp == NULL) + return NULL; + strcpy(cp, mdp->d_path); + strcat(cp, "/"); + strcat(cp, fname); + return (cp); +} + +/* + * Read linker.hints file into memory performing some sanity checks. + */ +static void +moduledir_readhints(struct moduledir *mdp) +{ + struct stat st; + char *path; + int fd, size, version; + + if (mdp->d_hints != NULL || (mdp->d_flags & MDIR_NOHINTS)) + return; + path = moduledir_fullpath(mdp, "linker.hints"); + if (stat(path, &st) != 0 || st.st_size < (sizeof(version) + sizeof(int)) || + st.st_size > 100 * 1024 || (fd = open(path, O_RDONLY)) < 0) { + free(path); + mdp->d_flags |= MDIR_NOHINTS; + return; + } + free(path); + size = read(fd, &version, sizeof(version)); + if (size != sizeof(version) || version != LINKER_HINTS_VERSION) + goto bad; + size = st.st_size - size; + mdp->d_hints = malloc(size); + if (mdp->d_hints == NULL) + goto bad; + if (read(fd, mdp->d_hints, size) != size) + goto bad; + mdp->d_hintsz = size; + close(fd); + return; +bad: + close(fd); + if (mdp->d_hints) { + free(mdp->d_hints); + mdp->d_hints = NULL; + } + mdp->d_flags |= MDIR_NOHINTS; + return; +} + +/* + * Extract directories from the ';' separated list, remove duplicates. + */ +static void +moduledir_rebuild(void) +{ + struct moduledir *mdp, *mtmp; + const char *path, *cp, *ep; + int cplen; + + path = getenv("module_path"); + if (path == NULL) + path = default_searchpath; + /* + * Rebuild list of module directories if it changed + */ + STAILQ_FOREACH(mdp, &moduledir_list, d_link) + mdp->d_flags |= MDIR_REMOVED; + + for (ep = path; *ep != 0; ep++) { + cp = ep; + for (; *ep != 0 && *ep != ';'; ep++) + ; + /* + * Ignore trailing slashes + */ + for (cplen = ep - cp; cplen > 1 && cp[cplen - 1] == '/'; cplen--) + ; + STAILQ_FOREACH(mdp, &moduledir_list, d_link) { + if (strlen(mdp->d_path) != cplen || bcmp(cp, mdp->d_path, cplen) != 0) + continue; + mdp->d_flags &= ~MDIR_REMOVED; + break; + } + if (mdp == NULL) { + mdp = malloc(sizeof(*mdp) + cplen + 1); + if (mdp == NULL) + return; + mdp->d_path = (char*)(mdp + 1); + bcopy(cp, mdp->d_path, cplen); + mdp->d_path[cplen] = 0; + mdp->d_hints = NULL; + mdp->d_flags = 0; + STAILQ_INSERT_TAIL(&moduledir_list, mdp, d_link); + } + if (*ep == 0) + break; + } + /* + * Delete unused directories if any + */ + mdp = STAILQ_FIRST(&moduledir_list); + while (mdp) { + if ((mdp->d_flags & MDIR_REMOVED) == 0) { + mdp = STAILQ_NEXT(mdp, d_link); + } else { + if (mdp->d_hints) + free(mdp->d_hints); + mtmp = mdp; + mdp = STAILQ_NEXT(mdp, d_link); + STAILQ_REMOVE(&moduledir_list, mtmp, moduledir, d_link); + free(mtmp); + } + } + return; +} diff --git a/sys/boot/common/newvers.sh b/sys/boot/common/newvers.sh new file mode 100755 index 0000000..7a4856c --- /dev/null +++ b/sys/boot/common/newvers.sh @@ -0,0 +1,47 @@ +#!/bin/sh - +# +# $FreeBSD$ +# $NetBSD: newvers.sh,v 1.1 1997/07/26 01:50:38 thorpej Exp $ +# +# Copyright (c) 1984, 1986, 1990, 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. All advertising materials mentioning features or use of this software +# must display the following acknowledgement: +# This product includes software developed by the University of +# California, Berkeley and its contributors. +# 4. 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. +# +# @(#)newvers.sh 8.1 (Berkeley) 4/20/94 + +LC_ALL=C; export LC_ALL +u=${USER-root} h=`hostname` t=`date` +#r=`head -n 6 $1 | tail -n 1 | awk -F: ' { print $1 } '` +r=`awk -F: ' /^[0-9]\.[0-9]+:/ { print $1; exit }' $1` + +echo "char bootprog_name[] = \"FreeBSD/${3} ${2}\";" > vers.c +echo "char bootprog_rev[] = \"${r}\";" >> vers.c +echo "char bootprog_date[] = \"${t}\";" >> vers.c +echo "char bootprog_maker[] = \"${u}@${h}\";" >> vers.c diff --git a/sys/boot/common/panic.c b/sys/boot/common/panic.c new file mode 100644 index 0000000..181c4c7 --- /dev/null +++ b/sys/boot/common/panic.c @@ -0,0 +1,57 @@ +/* + * $NetBSD: panic.c,v 1.2 1997/03/22 01:48:36 thorpej Exp $ + */ +/*- + * Copyright (c) 1996 + * Matthias Drochner. 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 for the NetBSD Project + * by Matthias Drochner. + * 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/cdefs.h> +__FBSDID("$FreeBSD$"); + +#include <stand.h> +#include <machine/stdarg.h> + +void +panic(const char *fmt,...) +{ + va_list ap; + + printf("panic: "); + va_start(ap, fmt); + vprintf(fmt, ap); + va_end(ap); + printf("\n"); + + printf("--> Press a key on the console to reboot <--\n"); + getchar(); + printf("Rebooting...\n"); + exit(1); +} diff --git a/sys/boot/common/pnp.c b/sys/boot/common/pnp.c new file mode 100644 index 0000000..05cce99 --- /dev/null +++ b/sys/boot/common/pnp.c @@ -0,0 +1,382 @@ +/* + * mjs copyright + * + */ + +#include <sys/cdefs.h> +__FBSDID("$FreeBSD$"); + +/* + * "Plug and Play" functionality. + * + * We use the PnP enumerators to obtain identifiers for installed hardware, + * and the contents of a database to determine modules to be loaded to support + * such hardware. + */ + +#include <stand.h> +#include <string.h> +#include <bootstrap.h> + +struct pnpinfo_stql pnp_devices; +static int pnp_devices_initted = 0; + +static void pnp_discard(void); + +/* + * Perform complete enumeration sweep + */ + +COMMAND_SET(pnpscan, "pnpscan", "scan for PnP devices", pnp_scan); + +static int +pnp_scan(int argc, char *argv[]) +{ + struct pnpinfo *pi; + int hdlr; + int verbose; + int ch; + + if (pnp_devices_initted == 0) { + STAILQ_INIT(&pnp_devices); + pnp_devices_initted = 1; + } + + verbose = 0; + optind = 1; + optreset = 1; + while ((ch = getopt(argc, argv, "v")) != -1) { + switch(ch) { + case 'v': + verbose = 1; + break; + case '?': + default: + /* getopt has already reported an error */ + return(CMD_OK); + } + } + + /* forget anything we think we knew */ + pnp_discard(); + + /* iterate over all of the handlers */ + for (hdlr = 0; pnphandlers[hdlr] != NULL; hdlr++) { + if (verbose) + printf("Probing %s...\n", pnphandlers[hdlr]->pp_name); + pnphandlers[hdlr]->pp_enumerate(); + } + if (verbose) { + pager_open(); + pager_output("PNP scan summary:\n"); + STAILQ_FOREACH(pi, &pnp_devices, pi_link) { + pager_output(STAILQ_FIRST(&pi->pi_ident)->id_ident); /* first ident should be canonical */ + if (pi->pi_desc != NULL) { + pager_output(" : "); + pager_output(pi->pi_desc); + } + pager_output("\n"); + } + pager_close(); + } + return(CMD_OK); +} + +#if 0 +/* + * Try to load outstanding modules (eg. after disk change) + */ +COMMAND_SET(pnpload, "pnpload", "load modules for PnP devices", pnp_load); + +static int +pnp_load(int argc, char *argv[]) +{ + struct pnpinfo *pi; + char *modfname; + + /* find anything? */ + if (STAILQ_FIRST(&pnp_devices) != NULL) { + + /* check for kernel, assign modules handled by static drivers there */ + if (pnp_scankernel()) { + command_errmsg = "cannot load drivers until kernel loaded"; + return(CMD_ERROR); + } + if (fname == NULL) { + /* default paths */ + pnp_readconf("/boot/pnpdata.local"); + pnp_readconf("/boot/pnpdata"); + } else { + if (pnp_readconf(fname)) { + sprintf(command_errbuf, "can't read PnP information from '%s'", fname); + return(CMD_ERROR); + } + } + + /* try to load any modules that have been nominated */ + STAILQ_FOREACH(pi, &pnp_devices, pi_link) { + /* Already loaded? */ + if ((pi->pi_module != NULL) && (file_findfile(pi->pi_module, NULL) == NULL)) { + modfname = malloc(strlen(pi->pi_module) + 4); + sprintf(modfname, "%s.ko", pi->pi_module); /* XXX implicit knowledge of KLD module filenames */ + if (mod_load(pi->pi_module, pi->pi_argc, pi->pi_argv)) + printf("Could not load module '%s' for device '%s'\n", modfname, STAILQ_FIRST(&pi->pi_ident)->id_ident); + free(modfname); + } + } + } + return(CMD_OK); +} +#endif +/* + * Throw away anything we think we know about PnP devices. + */ +static void +pnp_discard(void) +{ + struct pnpinfo *pi; + + while (STAILQ_FIRST(&pnp_devices) != NULL) { + pi = STAILQ_FIRST(&pnp_devices); + STAILQ_REMOVE_HEAD(&pnp_devices, pi_link); + pnp_freeinfo(pi); + } +} +#if 0 +/* + * The PnP configuration database consists of a flat text file with + * entries one per line. Valid lines are: + * + * # <text> + * + * This line is a comment, and ignored. + * + * [<name>] + * + * Entries following this line are for devices connected to the + * bus <name>, At least one such entry must be encountered + * before identifiers are recognised. + * + * ident=<identifier> rev=<revision> module=<module> args=<arguments> + * + * This line describes an identifier:module mapping. The 'ident' + * and 'module' fields are required; the 'rev' field is currently + * ignored (but should be used), and the 'args' field must come + * last. + * + * Comments may be appended to lines; any character including or following + * '#' on a line is ignored. + */ +static int +pnp_readconf(char *path) +{ + struct pnpinfo *pi; + struct pnpident *id; + int fd, line; + char lbuf[128], *currbus, *ident, *revision, *module, *args; + char *cp, *ep, *tp, c; + + /* try to open the file */ + if ((fd = open(path, O_RDONLY)) >= 0) { + line = 0; + currbus = NULL; + + while (fgetstr(lbuf, sizeof(lbuf), fd) > 0) { + line++; + /* Find the first non-space character on the line */ + for (cp = lbuf; (*cp != 0) && !isspace(*cp); cp++) + ; + + /* keep/discard? */ + if ((*cp == 0) || (*cp == '#')) + continue; + + /* cut trailing comment? */ + if ((ep = strchr(cp, '#')) != NULL) + *ep = 0; + + /* bus declaration? */ + if (*cp == '[') { + if (((ep = strchr(cp, ']')) == NULL) || ((ep - cp) < 2)) { + printf("%s line %d: bad bus specification\n", path, line); + } else { + if (currbus != NULL) + free(currbus); + *ep = 0; + currbus = strdup(cp + 1); + } + continue; + } + + /* XXX should we complain? */ + if (currbus == NULL) + continue; + + /* mapping */ + for (ident = module = args = revision = NULL; *cp != 0;) { + + /* discard leading whitespace */ + if (isspace(*cp)) { + cp++; + continue; + } + + /* scan for terminator, separator */ + for (ep = cp; (*ep != 0) && (*ep != '=') && !isspace(*ep); ep++) + ; + + if (*ep == '=') { + *ep = 0; + for (tp = ep + 1; (*tp != 0) && !isspace(*tp); tp++) + ; + c = *tp; + *tp = 0; + if ((ident == NULL) && !strcmp(cp, "ident")) { + ident = ep + 1; + } else if ((revision == NULL) && !strcmp(cp, "revision")) { + revision = ep + 1; + } else if ((args == NULL) && !strcmp(cp, "args")) { + *tp = c; + while (*tp != 0) /* skip to end of string */ + tp++; + args = ep + 1; + } else { + /* XXX complain? */ + } + cp = tp; + continue; + } + + /* it's garbage or a keyword - ignore it for now */ + cp = ep; + } + + /* we must have at least ident and module set to be interesting */ + if ((ident == NULL) || (module == NULL)) + continue; + + /* + * Loop looking for module/bus that might match this, but aren't already + * assigned. + * XXX no revision parse/test here yet. + */ + STAILQ_FOREACH(pi, &pnp_devices, pi_link) { + + /* no driver assigned, bus matches OK */ + if ((pi->pi_module == NULL) && + !strcmp(pi->pi_handler->pp_name, currbus)) { + + /* scan idents, take first match */ + STAILQ_FOREACH(id, &pi->pi_ident, id_link) + if (!strcmp(id->id_ident, ident)) + break; + + /* find a match? */ + if (id != NULL) { + if (args != NULL) + if (parse(&pi->pi_argc, &pi->pi_argv, args)) { + printf("%s line %d: bad arguments\n", path, line); + continue; + } + pi->pi_module = strdup(module); + printf("use module '%s' for %s:%s\n", module, pi->pi_handler->pp_name, id->id_ident); + } + } + } + } + close(fd); + } + return(CMD_OK); +} + +static int +pnp_scankernel(void) +{ + return(CMD_OK); +} +#endif +/* + * Add a unique identifier to (pi) + */ +void +pnp_addident(struct pnpinfo *pi, char *ident) +{ + struct pnpident *id; + + STAILQ_FOREACH(id, &pi->pi_ident, id_link) + if (!strcmp(id->id_ident, ident)) + return; /* already have this one */ + + id = malloc(sizeof(struct pnpident)); + id->id_ident = strdup(ident); + STAILQ_INSERT_TAIL(&pi->pi_ident, id, id_link); +} + +/* + * Allocate a new pnpinfo struct + */ +struct pnpinfo * +pnp_allocinfo(void) +{ + struct pnpinfo *pi; + + pi = malloc(sizeof(struct pnpinfo)); + bzero(pi, sizeof(struct pnpinfo)); + STAILQ_INIT(&pi->pi_ident); + return(pi); +} + +/* + * Release storage held by a pnpinfo struct + */ +void +pnp_freeinfo(struct pnpinfo *pi) +{ + struct pnpident *id; + + while (!STAILQ_EMPTY(&pi->pi_ident)) { + id = STAILQ_FIRST(&pi->pi_ident); + STAILQ_REMOVE_HEAD(&pi->pi_ident, id_link); + free(id->id_ident); + free(id); + } + if (pi->pi_desc) + free(pi->pi_desc); + if (pi->pi_module) + free(pi->pi_module); + if (pi->pi_argv) + free(pi->pi_argv); + free(pi); +} + +/* + * Add a new pnpinfo struct to the list. + */ +void +pnp_addinfo(struct pnpinfo *pi) +{ + STAILQ_INSERT_TAIL(&pnp_devices, pi, pi_link); +} + + +/* + * Format an EISA id as a string in standard ISA PnP format, AAAIIRR + * where 'AAA' is the EISA vendor ID, II is the product ID and RR the revision ID. + */ +char * +pnp_eisaformat(u_int8_t *data) +{ + static char idbuf[8]; + const char hextoascii[] = "0123456789abcdef"; + + idbuf[0] = '@' + ((data[0] & 0x7c) >> 2); + idbuf[1] = '@' + (((data[0] & 0x3) << 3) + ((data[1] & 0xe0) >> 5)); + idbuf[2] = '@' + (data[1] & 0x1f); + idbuf[3] = hextoascii[(data[2] >> 4)]; + idbuf[4] = hextoascii[(data[2] & 0xf)]; + idbuf[5] = hextoascii[(data[3] >> 4)]; + idbuf[6] = hextoascii[(data[3] & 0xf)]; + idbuf[7] = 0; + return(idbuf); +} + diff --git a/sys/boot/common/pnpdata b/sys/boot/common/pnpdata new file mode 100644 index 0000000..f2e2620 --- /dev/null +++ b/sys/boot/common/pnpdata @@ -0,0 +1,183 @@ +# +# $FreeBSD$ +# +# This file contains the system default Plug-and-Play data. It is +# derived from a number of sources, including: +# +# - The Microsoft "Windows Generic Device IDs" document +# + +[pci] +###################################################################### +# PCI devices. +# +# Required attributes: +# +# ident= PCI identifier in the form 0xDDDDVVVV where +# 'VVVV' is the 4-digit hex form of the vendor ID and +# 'DDDD' is the 4-digit hex form of the device ID. +# or +# +# vendor= 0xVVVV where 'VVVV' is above +# name= Vendor name + +vendor=0x8086 name=Intel + + +[isa] +###################################################################### +# ISA PnP devices +# +# Required attributes: +# +# ident= ISA PnP identifier in the form AAAIIRR where +# 'AAA' is the EISA vendor ID, 'II' is the device ID +# and 'RR' is the revision ID. +# or +# +# vendor= AAA to register just a vendor name. +# name= Vendor name +# +# Optional attributes: +# +# module= Support module identifier. +# +# args= Arguments to pass to the support module. +# + +vendor=CSC name=Crystal Semiconductor +vendor=CTL name=Creative Labs +vendor=PNP name=Generic + +# From "Windows Generic Device IDs" +# +# --Parallel Devices-- +ident=PNP0400 module=lpt # Standard LPT printer port +ident=PNP0401 module=lpt # ECP printer port + +# --Serial Devices-- +ident=PNP0500 module=sio # Standard PC COM port +ident=PNP0501 module=sio # 16550A-compatible COM port +ident=PNP0502 module=sio # Multiport serial device (non-intelligent 16550) + +# --Disk Controllers-- +ident=PNP0600 module=wd # Generic ESDI/IDE/ATA compatible hard disk controller +ident=PNP0603 module=wd # Generic IDE supporting Microsoft Device Bay Specification +ident=PNP0700 module=fd # PC standard floppy disk controller +ident=PNP0701 module=fd # Standard floppy controller supporting MS Device Bay Spec + +# --Peripheral Buses-- +ident=PNP0A00 module=isa # ISA Bus +ident=PNP0A01 module=eisa # EISA Bus +ident=PNP0A03 module=pci # PCI Bus +ident=PNP0A04 module=isa # VESA/VL Bus + +# -- Real Time Clock, BIOS, System board devices-- +ident=PNP0C04 module=npx # Math Coprocessor +ident=PNP0C05 module=apm # APM BIOS (Version independent) + +# --PCMCIA Controller Chipsets-- +ident=PNP0E00 module=pcic # Intel 82365-Compatible PCMCIA Controller +ident=PNP0E01 module=pcic # Cirrus Logic CL-PD6720 PCMCIA Controller +ident=PNP0E02 module=pcic # VLSI VL82C146 PCMCIA Controller +ident=PNP0E03 module=pcic # Intel 82365-compatible CardBus controller + +# --Network Adapters-- +ident=PNP8001 module=ed # Novell/Anthem NE3200 +ident=PNP8004 # Compaq NE3200 +ident=PNP80d3 module=ed # Novell/Anthem NE1000 +ident=PNP80d4 module=ed # Novell/Anthem NE2000 +ident=PNP80d5 module=ed # NE1000 Compatible +ident=PNP80d6 module=ed # NE2000 Compatible +ident=PNP80d8 module=lnc # Novell/Anthem NE2100 +ident=PNP80e9 module=le # DEC (DE200) EtherWorks Turbo +ident=PNP80eb module=le # DEC (DE201) EtherWorks Turbo/TP +ident=PNP80ec module=le # DEC (DE202) EtherWorks Turbo/TP_BNC +ident=PNP80f1 module=eg # 3Com EtherLink Plus +ident=PNP80f3 module=ed # 3Com EtherLink II or IITP (8 or 16-bit) +ident=PNP80f6 module=ed # 3Com EtherLink 16 +ident=PNP80f7 module=ep # 3Com EtherLink III +ident=PNP80f8 module=ep # 3Com Generic Etherlink Plug and Play Device +ident=PNP8123 module=ed # SMC StarCard PLUS (WD/8003S) +ident=PNP8124 module=ed # SMC StarCard PLUS With On Board Hub (WD/8003SH) +ident=PNP8125 module=ed # SMC EtherCard PLUS (WD/8003E) +ident=PNP8126 module=ed # SMC EtherCard PLUS With Boot ROM Socket (WD/8003EBT) +ident=PNP8127 module=ed # SMC EtherCard PLUS With Boot ROM Socket (WD/8003EB) +ident=PNP8128 module=ed # SMC EtherCard PLUS TP (WD/8003WT) +ident=PNP812a module=ed # SMC EtherCard PLUS 16 With Boot ROM Socket (WD/8013EBT) +ident=PNP812d module=ie # Intel EtherExpress 16 or 16TP +ident=PNP8137 module=ed # Artisoft AE-1 +ident=PNP8138 module=ed # Artisoft AE-2 or AE-3 +ident=PNP8158 module=ed # HP PC LAN Adapter/16 TP Plus (HP27247B) +ident=PNP8159 module=ed # HP PC LAN Adapter/16 TL Plus (HP27252) +ident=PNP81c3 module=ed # SMC EtherCard PLUS Elite (WD/8003EP) +ident=PNP81c4 module=ed # SMC EtherCard PLUS 10T (WD/8003W) +ident=PNP81c5 module=ed # SMC EtherCard PLUS Elite 16 (WD/8013EP) +ident=PNP81c6 module=ed # SMC EtherCard PLUS Elite 16T (WD/8013W) +ident=PNP81c7 module=ed # SMC EtherCard PLUS Elite 16 Combo (WD/8013EW or 8013EWC) +ident=PNP81c8 module=ed # SMC EtherElite Ultra 16 +ident=PNP820a module=ed # Zenith Data Systems NE2000-Compatible +ident=PNP8231 module=lnc # Advanced Micro Devices AM2100/AM1500T +ident=PNP828C module=lnc # AMD PCNet Family cards +ident=PNP828D module=lnc # AMD PCNet32 (VL version) +ident=PNP8323 module=ed # SMC EtherCard (All Types except 8013/A) +ident=PNP8390 module=ed # Generic network adapter + +# --SCSI, Proprietary CD Adapters-- +ident=PNPA003 module=matcd # Panasonic proprietary CD-ROM adapter (SBPro/SB16) +ident=PNPA02B module=scd # Sony proprietary CD-ROM controller +ident=PNPA030 module=mcd # Mitsumi LU-005 Single Speed CD-ROM controller + drive +ident=PNPA031 module=mcd # Mitsumi FX-001 Single Speed CD-ROM controller + drive +ident=PNPA032 module=mcd # Mitsumi FX-001 Double Speed CD-ROM controller + drive + +# --Sound/Video-capture, multimedia-- +ident=PNPB000 module=pcm # Sound Blaster 1.5 sound device +ident=PNPB001 module=pcm # Sound Blaster 2.0 sound device +ident=PNPB002 module=pcm # Sound Blaster Pro sound device +ident=PNPB003 module=pcm # Sound Blaster 16 sound device +ident=PNPB007 module=pcm # Microsoft Windows Sound System-compatible sound device +ident=PNPB009 module=pcm # Plug and Play Microsoft Windows Sound System Device +ident=PNPB020 module=pcm # Yamaha OPL3-compatible FM synthesizer device +ident=PNPB02F module=joy # Joystick/Game port + +# --Compatibility with early device ID list-- +ident=PNP0802 module=pcm # Microsoft Sound System compatible device (obsolete, use PNPB0xx instead) + +# --Modems-- +ident=PNPC000 module=sio # Compaq 14400 Modem (TBD) +ident=PNPC001 module=sio # Compaq 2400/9600 Modem (TBD) + +# Vendor supplied IDs. + +# --Parallel Devices-- + +# --Serial Devices-- + +# --Disk Controllers-- + +# --Peripheral Buses-- + +# --Real Time Clock, BIOS, System board devices-- + +# --PCMCIA Controller Chipsets-- + +# --Network Adapters-- +ident=CSC6040 module=cs # Crystal Semiconductor CS8920 + +# --SCSI, Proprietary CD Adapters-- + +# --Sound/Video-capture, multimedia-- + +# --Modems-- + + + +[com] +###################################################################### +# COM PnP devices +# + +[lpt] +###################################################################### +# LPT PnP devices +# diff --git a/sys/boot/common/ufsread.c b/sys/boot/common/ufsread.c new file mode 100644 index 0000000..0b7c3f4 --- /dev/null +++ b/sys/boot/common/ufsread.c @@ -0,0 +1,271 @@ +/*- + * Copyright (c) 2002 Networks Associates Technology, Inc. + * All rights reserved. + * + * This software was developed for the FreeBSD Project by Marshall + * Kirk McKusick and Network Associates Laboratories, the Security + * Research Division of Network Associates, Inc. under DARPA/SPAWAR + * contract N66001-01-C-8035 ("CBOSS"), as part of the DARPA CHATS + * research program + * + * Copyright (c) 1998 Robert Nordier + * All rights reserved. + * + * Redistribution and use in source and binary forms are freely + * permitted provided that the above copyright notice and this + * paragraph and the following disclaimer are duplicated in all + * such forms. + * + * This software is provided "AS IS" and without any express or + * implied warranties, including, without limitation, the implied + * warranties of merchantability and fitness for a particular + * purpose. + */ + +#include <sys/cdefs.h> +__FBSDID("$FreeBSD$"); + +#include <ufs/ufs/dinode.h> +#include <ufs/ffs/fs.h> +#ifdef __i386__ +/* XXX: Revert to old (broken for over 1.5Tb filesystems) version of cgbase + (see sys/ufs/ffs/fs.h rev 1.39) so that i386 boot loader (boot2) can + support both UFS1 and UFS2 again. */ +#undef cgbase +#define cgbase(fs, c) ((ufs2_daddr_t)((fs)->fs_fpg * (c))) +#endif + +/* + * We use 4k `virtual' blocks for filesystem data, whatever the actual + * filesystem block size. FFS blocks are always a multiple of 4k. + */ +#define VBLKSHIFT 12 +#define VBLKSIZE (1 << VBLKSHIFT) +#define VBLKMASK (VBLKSIZE - 1) +#define DBPERVBLK (VBLKSIZE / DEV_BSIZE) +#define INDIRPERVBLK(fs) (NINDIR(fs) / ((fs)->fs_bsize >> VBLKSHIFT)) +#define IPERVBLK(fs) (INOPB(fs) / ((fs)->fs_bsize >> VBLKSHIFT)) +#define INO_TO_VBA(fs, ipervblk, x) \ + (fsbtodb(fs, cgimin(fs, ino_to_cg(fs, x))) + \ + (((x) % (fs)->fs_ipg) / (ipervblk) * DBPERVBLK)) +#define INO_TO_VBO(ipervblk, x) ((x) % ipervblk) +#define FS_TO_VBA(fs, fsb, off) (fsbtodb(fs, fsb) + \ + ((off) / VBLKSIZE) * DBPERVBLK) +#define FS_TO_VBO(fs, fsb, off) ((off) & VBLKMASK) + +/* Buffers that must not span a 64k boundary. */ +struct dmadat { + char blkbuf[VBLKSIZE]; /* filesystem blocks */ + char indbuf[VBLKSIZE]; /* indir blocks */ + char sbbuf[SBLOCKSIZE]; /* superblock */ + char secbuf[DEV_BSIZE]; /* for MBR/disklabel */ +}; +static struct dmadat *dmadat; + +static ino_t lookup(const char *); +static ssize_t fsread(ino_t, void *, size_t); + +static int ls, dsk_meta; +static uint32_t fs_off; + +static __inline__ int +fsfind(const char *name, ino_t * ino) +{ + char buf[DEV_BSIZE]; + struct dirent *d; + char *s; + ssize_t n; + + fs_off = 0; + while ((n = fsread(*ino, buf, DEV_BSIZE)) > 0) + for (s = buf; s < buf + DEV_BSIZE;) { + d = (void *)s; + if (ls) + printf("%s ", d->d_name); + else if (!strcmp(name, d->d_name)) { + *ino = d->d_fileno; + return d->d_type; + } + s += d->d_reclen; + } + if (n != -1 && ls) + printf("\n"); + return 0; +} + +static ino_t +lookup(const char *path) +{ + char name[MAXNAMLEN + 1]; + const char *s; + ino_t ino; + ssize_t n; + int dt; + + ino = ROOTINO; + dt = DT_DIR; + name[0] = '/'; + name[1] = '\0'; + for (;;) { + if (*path == '/') + path++; + if (!*path) + break; + for (s = path; *s && *s != '/'; s++); + if ((n = s - path) > MAXNAMLEN) + return 0; + ls = *path == '?' && n == 1 && !*s; + memcpy(name, path, n); + name[n] = 0; + if (dt != DT_DIR) { + printf("%s: not a directory.\n", name); + return (0); + } + if ((dt = fsfind(name, &ino)) <= 0) + break; + path = s; + } + return dt == DT_REG ? ino : 0; +} + +/* + * Possible superblock locations ordered from most to least likely. + */ +static int sblock_try[] = SBLOCKSEARCH; + +#if defined(UFS2_ONLY) +#define DIP(field) dp2.field +#elif defined(UFS1_ONLY) +#define DIP(field) dp1.field +#else +#define DIP(field) fs->fs_magic == FS_UFS1_MAGIC ? dp1.field : dp2.field +#endif + +static ssize_t +fsread(ino_t inode, void *buf, size_t nbyte) +{ +#ifndef UFS2_ONLY + static struct ufs1_dinode dp1; +#endif +#ifndef UFS1_ONLY + static struct ufs2_dinode dp2; +#endif + static ino_t inomap; + char *blkbuf; + void *indbuf; + struct fs *fs; + char *s; + size_t n, nb, size, off, vboff; + ufs_lbn_t lbn; + ufs2_daddr_t addr, vbaddr; + static ufs2_daddr_t blkmap, indmap; + u_int u; + + + blkbuf = dmadat->blkbuf; + indbuf = dmadat->indbuf; + fs = (struct fs *)dmadat->sbbuf; + if (!dsk_meta) { + inomap = 0; + for (n = 0; sblock_try[n] != -1; n++) { + if (dskread(fs, sblock_try[n] / DEV_BSIZE, + SBLOCKSIZE / DEV_BSIZE)) + return -1; + if (( +#if defined(UFS1_ONLY) + fs->fs_magic == FS_UFS1_MAGIC +#elif defined(UFS2_ONLY) + (fs->fs_magic == FS_UFS2_MAGIC && + fs->fs_sblockloc == sblock_try[n]) +#else + fs->fs_magic == FS_UFS1_MAGIC || + (fs->fs_magic == FS_UFS2_MAGIC && + fs->fs_sblockloc == sblock_try[n]) +#endif + ) && + fs->fs_bsize <= MAXBSIZE && + fs->fs_bsize >= sizeof(struct fs)) + break; + } + if (sblock_try[n] == -1) { + printf("Not ufs\n"); + return -1; + } + dsk_meta++; + } + if (!inode) + return 0; + if (inomap != inode) { + n = IPERVBLK(fs); + if (dskread(blkbuf, INO_TO_VBA(fs, n, inode), DBPERVBLK)) + return -1; + n = INO_TO_VBO(n, inode); +#if defined(UFS1_ONLY) + dp1 = ((struct ufs1_dinode *)blkbuf)[n]; +#elif defined(UFS2_ONLY) + dp2 = ((struct ufs2_dinode *)blkbuf)[n]; +#else + if (fs->fs_magic == FS_UFS1_MAGIC) + dp1 = ((struct ufs1_dinode *)blkbuf)[n]; + else + dp2 = ((struct ufs2_dinode *)blkbuf)[n]; +#endif + inomap = inode; + fs_off = 0; + blkmap = indmap = 0; + } + s = buf; + size = DIP(di_size); + n = size - fs_off; + if (nbyte > n) + nbyte = n; + nb = nbyte; + while (nb) { + lbn = lblkno(fs, fs_off); + off = blkoff(fs, fs_off); + if (lbn < NDADDR) { + addr = DIP(di_db[lbn]); + } else if (lbn < NDADDR + NINDIR(fs)) { + n = INDIRPERVBLK(fs); + addr = DIP(di_ib[0]); + u = (u_int)(lbn - NDADDR) / (n * DBPERVBLK); + vbaddr = fsbtodb(fs, addr) + u; + if (indmap != vbaddr) { + if (dskread(indbuf, vbaddr, DBPERVBLK)) + return -1; + indmap = vbaddr; + } + n = (lbn - NDADDR) & (n - 1); +#if defined(UFS1_ONLY) + addr = ((ufs1_daddr_t *)indbuf)[n]; +#elif defined(UFS2_ONLY) + addr = ((ufs2_daddr_t *)indbuf)[n]; +#else + if (fs->fs_magic == FS_UFS1_MAGIC) + addr = ((ufs1_daddr_t *)indbuf)[n]; + else + addr = ((ufs2_daddr_t *)indbuf)[n]; +#endif + } else { + return -1; + } + vbaddr = fsbtodb(fs, addr) + (off >> VBLKSHIFT) * DBPERVBLK; + vboff = off & VBLKMASK; + n = sblksize(fs, size, lbn) - (off & ~VBLKMASK); + if (n > VBLKSIZE) + n = VBLKSIZE; + if (blkmap != vbaddr) { + if (dskread(blkbuf, vbaddr, n >> DEV_BSHIFT)) + return -1; + blkmap = vbaddr; + } + n -= vboff; + if (n > nb) + n = nb; + memcpy(s, blkbuf + vboff, n); + s += n; + fs_off += n; + nb -= n; + } + return nbyte; +} |