From 7d97ee5b28b409c00bfaf12daf5ab497a6038b9d Mon Sep 17 00:00:00 2001 From: kevans Date: Mon, 12 Feb 2018 01:08:44 +0000 Subject: MFC r325834,r325997,326502: Move sys/boot to stand/ This is effectively a direct commit to stable/11, due to differences between stable/11 and head. Changes to DTS in sys/boot/fdt/dts were often accompanied by kernel changes. Many of these were also risc-v updates that likely had many more dependencies to MFC. Because of this, sys/boot/fdt/dts remains as-is while everything else in sys/boot relocates to stand/. r325834: Move sys/boot to stand. Fix all references to new location r325997: Remove empty directories. r326502: Document the sys/boot -> stand move in hier.7 and the top-level README. --- stand/common/Makefile.depend | 11 + stand/common/bcache.c | 503 ++++++++++++++++++ stand/common/boot.c | 410 +++++++++++++++ stand/common/bootstrap.h | 334 ++++++++++++ stand/common/commands.c | 511 ++++++++++++++++++ stand/common/console.c | 302 +++++++++++ stand/common/dev_net.c | 435 ++++++++++++++++ stand/common/dev_net.h | 36 ++ stand/common/devopen.c | 67 +++ stand/common/disk.c | 432 +++++++++++++++ stand/common/disk.h | 117 +++++ stand/common/help.common | 407 +++++++++++++++ stand/common/install.c | 355 +++++++++++++ stand/common/interp.c | 371 +++++++++++++ stand/common/interp_backslash.c | 167 ++++++ stand/common/interp_forth.c | 332 ++++++++++++ stand/common/interp_parse.c | 222 ++++++++ stand/common/isapnp.c | 313 +++++++++++ stand/common/isapnp.h | 313 +++++++++++ stand/common/load_elf.c | 1038 +++++++++++++++++++++++++++++++++++++ stand/common/load_elf32.c | 7 + stand/common/load_elf32_obj.c | 7 + stand/common/load_elf64.c | 6 + stand/common/load_elf64_obj.c | 6 + stand/common/load_elf_obj.c | 537 +++++++++++++++++++ stand/common/ls.c | 212 ++++++++ stand/common/md.c | 157 ++++++ stand/common/merge_help.awk | 104 ++++ stand/common/misc.c | 219 ++++++++ stand/common/module.c | 1095 +++++++++++++++++++++++++++++++++++++++ stand/common/newvers.sh | 60 +++ stand/common/part.c | 898 ++++++++++++++++++++++++++++++++ stand/common/part.h | 83 +++ stand/common/paths.h | 39 ++ stand/common/pnp.c | 236 +++++++++ stand/common/rbx.h | 61 +++ stand/common/reloc_elf.c | 231 +++++++++ stand/common/reloc_elf32.c | 6 + stand/common/reloc_elf64.c | 6 + stand/common/self_reloc.c | 124 +++++ 40 files changed, 10770 insertions(+) create mode 100644 stand/common/Makefile.depend create mode 100644 stand/common/bcache.c create mode 100644 stand/common/boot.c create mode 100644 stand/common/bootstrap.h create mode 100644 stand/common/commands.c create mode 100644 stand/common/console.c create mode 100644 stand/common/dev_net.c create mode 100644 stand/common/dev_net.h create mode 100644 stand/common/devopen.c create mode 100644 stand/common/disk.c create mode 100644 stand/common/disk.h create mode 100644 stand/common/help.common create mode 100644 stand/common/install.c create mode 100644 stand/common/interp.c create mode 100644 stand/common/interp_backslash.c create mode 100644 stand/common/interp_forth.c create mode 100644 stand/common/interp_parse.c create mode 100644 stand/common/isapnp.c create mode 100644 stand/common/isapnp.h create mode 100644 stand/common/load_elf.c create mode 100644 stand/common/load_elf32.c create mode 100644 stand/common/load_elf32_obj.c create mode 100644 stand/common/load_elf64.c create mode 100644 stand/common/load_elf64_obj.c create mode 100644 stand/common/load_elf_obj.c create mode 100644 stand/common/ls.c create mode 100644 stand/common/md.c create mode 100644 stand/common/merge_help.awk create mode 100644 stand/common/misc.c create mode 100644 stand/common/module.c create mode 100755 stand/common/newvers.sh create mode 100644 stand/common/part.c create mode 100644 stand/common/part.h create mode 100644 stand/common/paths.h create mode 100644 stand/common/pnp.c create mode 100644 stand/common/rbx.h create mode 100644 stand/common/reloc_elf.c create mode 100644 stand/common/reloc_elf32.c create mode 100644 stand/common/reloc_elf64.c create mode 100644 stand/common/self_reloc.c (limited to 'stand/common') diff --git a/stand/common/Makefile.depend b/stand/common/Makefile.depend new file mode 100644 index 0000000..f80275d --- /dev/null +++ b/stand/common/Makefile.depend @@ -0,0 +1,11 @@ +# $FreeBSD$ +# Autogenerated - do NOT edit! + +DIRDEPS = \ + + +.include + +.if ${DEP_RELDIR} == ${_DEP_RELDIR} +# local dependencies - needed for -jN in clean tree +.endif diff --git a/stand/common/bcache.c b/stand/common/bcache.c new file mode 100644 index 0000000..198dd5f --- /dev/null +++ b/stand/common/bcache.c @@ -0,0 +1,503 @@ +/*- + * Copyright (c) 1998 Michael Smith + * Copyright 2015 Toomas Soome + * 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 +#include +__FBSDID("$FreeBSD$"); + +/* + * Simple hashed block cache + */ + +#include + +#include +#include +#include + +#include "bootstrap.h" + +/* #define BCACHE_DEBUG */ + +#ifdef BCACHE_DEBUG +# define DEBUG(fmt, args...) printf("%s: " fmt "\n" , __func__ , ## args) +#else +# define DEBUG(fmt, args...) +#endif + +struct bcachectl +{ + daddr_t bc_blkno; + int bc_count; +}; + +/* + * bcache per device node. cache is allocated on device first open and freed + * on last close, to save memory. The issue there is the size; biosdisk + * supports up to 31 (0x1f) devices. Classic setup would use single disk + * to boot from, but this has changed with zfs. + */ +struct bcache { + struct bcachectl *bcache_ctl; + caddr_t bcache_data; + size_t bcache_nblks; + size_t ra; +}; + +static u_int bcache_total_nblks; /* set by bcache_init */ +static u_int bcache_blksize; /* set by bcache_init */ +static u_int bcache_numdev; /* set by bcache_add_dev */ +/* statistics */ +static u_int bcache_units; /* number of devices with cache */ +static u_int bcache_unit_nblks; /* nblocks per unit */ +static u_int bcache_hits; +static u_int bcache_misses; +static u_int bcache_ops; +static u_int bcache_bypasses; +static u_int bcache_bcount; +static u_int bcache_rablks; + +#define BHASH(bc, blkno) ((blkno) & ((bc)->bcache_nblks - 1)) +#define BCACHE_LOOKUP(bc, blkno) \ + ((bc)->bcache_ctl[BHASH((bc), (blkno))].bc_blkno != (blkno)) +#define BCACHE_READAHEAD 256 +#define BCACHE_MINREADAHEAD 32 +#define BCACHE_MARKER 0xdeadbeef + +static void bcache_invalidate(struct bcache *bc, daddr_t blkno); +static void bcache_insert(struct bcache *bc, daddr_t blkno); +static void bcache_free_instance(struct bcache *bc); + +/* + * Initialise the cache for (nblks) of (bsize). + */ +void +bcache_init(size_t nblks, size_t bsize) +{ + /* set up control data */ + bcache_total_nblks = nblks; + bcache_blksize = bsize; +} + +/* + * add number of devices to bcache. we have to divide cache space + * between the devices, so bcache_add_dev() can be used to set up the + * number. The issue is, we need to get the number before actual allocations. + * bcache_add_dev() is supposed to be called from device init() call, so the + * assumption is, devsw dv_init is called for plain devices first, and + * for zfs, last. + */ +void +bcache_add_dev(int devices) +{ + bcache_numdev += devices; +} + +void * +bcache_allocate(void) +{ + u_int i; + struct bcache *bc = malloc(sizeof (struct bcache)); + int disks = bcache_numdev; + uint32_t *marker; + + if (disks == 0) + disks = 1; /* safe guard */ + + if (bc == NULL) { + errno = ENOMEM; + return (bc); + } + + /* + * the bcache block count must be power of 2 for hash function + */ + i = fls(disks) - 1; /* highbit - 1 */ + if (disks > (1 << i)) /* next power of 2 */ + i++; + + bc->bcache_nblks = bcache_total_nblks >> i; + bcache_unit_nblks = bc->bcache_nblks; + bc->bcache_data = malloc(bc->bcache_nblks * bcache_blksize + + sizeof(uint32_t)); + if (bc->bcache_data == NULL) { + /* dont error out yet. fall back to 32 blocks and try again */ + bc->bcache_nblks = 32; + bc->bcache_data = malloc(bc->bcache_nblks * bcache_blksize + + sizeof(uint32_t)); + } + + bc->bcache_ctl = malloc(bc->bcache_nblks * sizeof(struct bcachectl)); + + if ((bc->bcache_data == NULL) || (bc->bcache_ctl == NULL)) { + bcache_free_instance(bc); + errno = ENOMEM; + return (NULL); + } + /* Insert cache end marker. */ + marker = (uint32_t *)(bc->bcache_data + bc->bcache_nblks * bcache_blksize); + *marker = BCACHE_MARKER; + + /* Flush the cache */ + for (i = 0; i < bc->bcache_nblks; i++) { + bc->bcache_ctl[i].bc_count = -1; + bc->bcache_ctl[i].bc_blkno = -1; + } + bcache_units++; + bc->ra = BCACHE_READAHEAD; /* optimistic read ahead */ + return (bc); +} + +void +bcache_free(void *cache) +{ + struct bcache *bc = cache; + + if (bc == NULL) + return; + + bcache_free_instance(bc); + bcache_units--; +} + +/* + * Handle a write request; write directly to the disk, and populate the + * cache with the new values. + */ +static int +write_strategy(void *devdata, int rw, daddr_t blk, size_t size, + char *buf, size_t *rsize) +{ + struct bcache_devdata *dd = (struct bcache_devdata *)devdata; + struct bcache *bc = dd->dv_cache; + daddr_t i, nblk; + + nblk = size / bcache_blksize; + + /* Invalidate the blocks being written */ + for (i = 0; i < nblk; i++) { + bcache_invalidate(bc, blk + i); + } + + /* Write the blocks */ + return (dd->dv_strategy(dd->dv_devdata, rw, blk, size, buf, rsize)); +} + +/* + * 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 rw, daddr_t blk, size_t size, + char *buf, size_t *rsize) +{ + struct bcache_devdata *dd = (struct bcache_devdata *)devdata; + struct bcache *bc = dd->dv_cache; + size_t i, nblk, p_size, r_size, complete, ra; + int result; + daddr_t p_blk; + caddr_t p_buf; + uint32_t *marker; + + if (bc == NULL) { + errno = ENODEV; + return (-1); + } + + marker = (uint32_t *)(bc->bcache_data + bc->bcache_nblks * bcache_blksize); + + if (rsize != NULL) + *rsize = 0; + + nblk = size / bcache_blksize; + if (nblk == 0 && size != 0) + nblk++; + result = 0; + complete = 1; + + /* Satisfy any cache hits up front, break on first miss */ + for (i = 0; i < nblk; i++) { + if (BCACHE_LOOKUP(bc, (daddr_t)(blk + i))) { + bcache_misses += (nblk - i); + complete = 0; + if (nblk - i > BCACHE_MINREADAHEAD && bc->ra > BCACHE_MINREADAHEAD) + bc->ra >>= 1; /* reduce read ahead */ + break; + } else { + bcache_hits++; + } + } + + if (complete) { /* whole set was in cache, return it */ + if (bc->ra < BCACHE_READAHEAD) + bc->ra <<= 1; /* increase read ahead */ + bcopy(bc->bcache_data + (bcache_blksize * BHASH(bc, blk)), buf, size); + goto done; + } + + /* + * Fill in any misses. From check we have i pointing to first missing + * block, read in all remaining blocks + readahead. + * We have space at least for nblk - i before bcache wraps. + */ + p_blk = blk + i; + p_buf = bc->bcache_data + (bcache_blksize * BHASH(bc, p_blk)); + r_size = bc->bcache_nblks - BHASH(bc, p_blk); /* remaining blocks */ + + p_size = MIN(r_size, nblk - i); /* read at least those blocks */ + + /* + * The read ahead size setup. + * While the read ahead can save us IO, it also can complicate things: + * 1. We do not want to read ahead by wrapping around the + * bcache end - this would complicate the cache management. + * 2. We are using bc->ra as dynamic hint for read ahead size, + * detected cache hits will increase the read-ahead block count, and + * misses will decrease, see the code above. + * 3. The bcache is sized by 512B blocks, however, the underlying device + * may have a larger sector size, and we should perform the IO by + * taking into account these larger sector sizes. We could solve this by + * passing the sector size to bcache_allocate(), or by using ioctl(), but + * in this version we are using the constant, 16 blocks, and are rounding + * read ahead block count down to multiple of 16. + * Using the constant has two reasons, we are not entirely sure if the + * BIOS disk interface is providing the correct value for sector size. + * And secondly, this way we get the most conservative setup for the ra. + * + * The selection of multiple of 16 blocks (8KB) is quite arbitrary, however, + * we want to cover CDs (2K) and 4K disks. + * bcache_allocate() will always fall back to a minimum of 32 blocks. + * Our choice of 16 read ahead blocks will always fit inside the bcache. + */ + + if ((rw & F_NORA) == F_NORA) + ra = 0; + else + ra = bc->bcache_nblks - BHASH(bc, p_blk + p_size); + + if (ra != 0 && ra != bc->bcache_nblks) { /* do we have RA space? */ + ra = MIN(bc->ra, ra - 1); + ra = rounddown(ra, 16); /* multiple of 16 blocks */ + p_size += ra; + } + + /* invalidate bcache */ + for (i = 0; i < p_size; i++) { + bcache_invalidate(bc, p_blk + i); + } + + r_size = 0; + /* + * with read-ahead, it may happen we are attempting to read past + * disk end, as bcache has no information about disk size. + * in such case we should get partial read if some blocks can be + * read or error, if no blocks can be read. + * in either case we should return the data in bcache and only + * return error if there is no data. + */ + rw &= F_MASK; + result = dd->dv_strategy(dd->dv_devdata, rw, p_blk, + p_size * bcache_blksize, p_buf, &r_size); + + r_size /= bcache_blksize; + for (i = 0; i < r_size; i++) + bcache_insert(bc, p_blk + i); + + /* update ra statistics */ + if (r_size != 0) { + if (r_size < p_size) + bcache_rablks += (p_size - r_size); + else + bcache_rablks += ra; + } + + /* check how much data can we copy */ + for (i = 0; i < nblk; i++) { + if (BCACHE_LOOKUP(bc, (daddr_t)(blk + i))) + break; + } + + if (size > i * bcache_blksize) + size = i * bcache_blksize; + + if (size != 0) { + bcopy(bc->bcache_data + (bcache_blksize * BHASH(bc, blk)), buf, size); + result = 0; + } + + if (*marker != BCACHE_MARKER) { + printf("BUG: bcache corruption detected: nblks: %zu p_blk: %lu, " + "p_size: %zu, ra: %zu\n", bc->bcache_nblks, + (long unsigned)BHASH(bc, p_blk), p_size, ra); + } + + done: + if ((result == 0) && (rsize != NULL)) + *rsize = size; + return(result); +} + +/* + * Requests larger than 1/2 cache size will be bypassed and go + * directly to the disk. XXX tune this. + */ +int +bcache_strategy(void *devdata, int rw, daddr_t blk, size_t size, + char *buf, size_t *rsize) +{ + struct bcache_devdata *dd = (struct bcache_devdata *)devdata; + struct bcache *bc = dd->dv_cache; + u_int bcache_nblks = 0; + int nblk, cblk, ret; + size_t csize, isize, total; + + bcache_ops++; + + if (bc != NULL) + bcache_nblks = bc->bcache_nblks; + + /* bypass large requests, or when the cache is inactive */ + if (bc == NULL || + ((size * 2 / bcache_blksize) > bcache_nblks)) { + DEBUG("bypass %zu from %qu", size / bcache_blksize, blk); + bcache_bypasses++; + rw &= F_MASK; + return (dd->dv_strategy(dd->dv_devdata, rw, blk, size, buf, rsize)); + } + + switch (rw & F_MASK) { + case F_READ: + nblk = size / bcache_blksize; + if (size != 0 && nblk == 0) + nblk++; /* read at least one block */ + + ret = 0; + total = 0; + while(size) { + cblk = bcache_nblks - BHASH(bc, blk); /* # of blocks left */ + cblk = MIN(cblk, nblk); + + if (size <= bcache_blksize) + csize = size; + else + csize = cblk * bcache_blksize; + + ret = read_strategy(devdata, rw, blk, csize, buf+total, &isize); + + /* + * we may have error from read ahead, if we have read some data + * return partial read. + */ + if (ret != 0 || isize == 0) { + if (total != 0) + ret = 0; + break; + } + blk += isize / bcache_blksize; + total += isize; + size -= isize; + nblk = size / bcache_blksize; + } + + if (rsize) + *rsize = total; + + return (ret); + case F_WRITE: + return write_strategy(devdata, F_WRITE, blk, size, buf, rsize); + } + return -1; +} + +/* + * Free allocated bcache instance + */ +static void +bcache_free_instance(struct bcache *bc) +{ + if (bc != NULL) { + if (bc->bcache_ctl) + free(bc->bcache_ctl); + if (bc->bcache_data) + free(bc->bcache_data); + free(bc); + } +} + +/* + * Insert a block into the cache. + */ +static void +bcache_insert(struct bcache *bc, daddr_t blkno) +{ + u_int cand; + + cand = BHASH(bc, blkno); + + DEBUG("insert blk %llu -> %u # %d", blkno, cand, bcache_bcount); + bc->bcache_ctl[cand].bc_blkno = blkno; + bc->bcache_ctl[cand].bc_count = bcache_bcount++; +} + +/* + * Invalidate a block from the cache. + */ +static void +bcache_invalidate(struct bcache *bc, daddr_t blkno) +{ + u_int i; + + i = BHASH(bc, blkno); + if (bc->bcache_ctl[i].bc_blkno == blkno) { + bc->bcache_ctl[i].bc_count = -1; + bc->bcache_ctl[i].bc_blkno = -1; + DEBUG("invalidate blk %llu", blkno); + } +} + +#ifndef BOOT2 +COMMAND_SET(bcachestat, "bcachestat", "get disk block cache stats", command_bcache); + +static int +command_bcache(int argc, char *argv[]) +{ + if (argc != 1) { + command_errmsg = "wrong number of arguments"; + return(CMD_ERROR); + } + + printf("\ncache blocks: %d\n", bcache_total_nblks); + printf("cache blocksz: %d\n", bcache_blksize); + printf("cache readahead: %d\n", bcache_rablks); + printf("unit cache blocks: %d\n", bcache_unit_nblks); + printf("cached units: %d\n", bcache_units); + printf("%d ops %d bypasses %d hits %d misses\n", bcache_ops, + bcache_bypasses, bcache_hits, bcache_misses); + return(CMD_OK); +} +#endif diff --git a/stand/common/boot.c b/stand/common/boot.c new file mode 100644 index 0000000..5ee9521 --- /dev/null +++ b/stand/common/boot.c @@ -0,0 +1,410 @@ +/*- + * Copyright (c) 1998 Michael Smith + * 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 +__FBSDID("$FreeBSD$"); + +/* + * Loading modules, booting the system + */ + +#include +#include + +#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) { + snprintf(command_errbuf, sizeof(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) { + snprintf(command_errbuf, sizeof(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; +#ifdef BOOT_PROMPT_123 + const char *seq = "123", *p = seq; +#endif + + autoboot_tried = 1; + + if (timeout == -1) { + timeout = 10; + /* try to get a delay from the environment */ + if ((cp = getenv("autoboot_delay"))) { + timeout = strtol(cp, &ep, 0); + if (cp == ep) + timeout = 10; /* Unparseable? Set default! */ + } + } + + 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); + } + } + + if (timeout >= 0) { + otime = time(NULL); + when = otime + timeout; /* when to boot */ + + yes = 0; + +#ifdef BOOT_PROMPT_123 + printf("%s\n", (prompt == NULL) ? "Hit [Enter] to boot immediately, or " + "1 2 3 sequence for command prompt." : prompt); +#else + printf("%s\n", (prompt == NULL) ? "Hit [Enter] to boot immediately, or any other key for command prompt." : prompt); +#endif + + for (;;) { + if (ischar()) { + c = getchar(); +#ifdef BOOT_PROMPT_123 + if ((c == '\r') || (c == '\n')) { + yes = 1; + break; + } else if (c != *p++) + p = seq; + if (*p == 0) + break; +#else + if ((c == '\r') || (c == '\n')) + yes = 1; + break; +#endif + } + 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; + } + } + } else { + yes = 1; + } + + 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 : + * 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, *options; + int fd, error; + + if (getenv("vfs.root.mountfrom") != NULL) + return(0); + + error = 1; + sprintf(lbuf, "%s/etc/fstab", rootdev); + if ((fd = open(lbuf, O_RDONLY)) < 0) + goto notfound; + + /* loop reading lines from /etc/fstab What was that about sscanf again? */ + 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 / 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); + + /* skip whitespace up to mount options */ + cp += 1; + while ((*cp != 0) && isspace(*cp)) + cp++; + if (*cp == 0) /* misformatted */ + continue; + /* skip text to end of mount options and delimit */ + ep = cp; + while ((*cp != 0) && !isspace(*cp)) + cp++; + *cp = 0; + options = strdup(ep); + /* Build the : and save it in vfs.root.mountfrom */ + sprintf(lbuf, "%s:%s", fstyp, dev); + free(dev); + free(fstyp); + setenv("vfs.root.mountfrom", lbuf, 0); + + /* Don't override vfs.root.mountfrom.options if it is already set */ + if (getenv("vfs.root.mountfrom.options") == NULL) { + /* save mount options */ + setenv("vfs.root.mountfrom.options", options, 0); + } + free(options); + error = 0; + break; + } + close(fd); + +notfound: + if (error) { + const char *currdev; + + currdev = getenv("currdev"); + if (currdev != NULL && strncmp("zfs:", currdev, 4) == 0) { + cp = strdup(currdev); + cp[strlen(cp) - 1] = '\0'; + setenv("vfs.root.mountfrom", cp, 0); + error = 0; + } + } + + 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/stand/common/bootstrap.h b/stand/common/bootstrap.h new file mode 100644 index 0000000..2234e05 --- /dev/null +++ b/stand/common/bootstrap.h @@ -0,0 +1,334 @@ +/*- + * Copyright (c) 1998 Michael Smith + * 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$ + */ + +#ifndef _BOOTSTRAP_H_ +#define _BOOTSTRAP_H_ + +#include +#include +#include + +/* Commands and return values; nonzero return sets command_errmsg != NULL */ +typedef int (bootblk_cmd_t)(int argc, char *argv[]); +#define COMMAND_ERRBUFSZ (256) +extern char *command_errmsg; +extern char command_errbuf[COMMAND_ERRBUFSZ]; +#define CMD_OK 0 +#define CMD_WARN 1 +#define CMD_ERROR 2 +#define CMD_CRIT 3 +#define CMD_FATAL 4 + +/* interp.c */ +void interact(const char *rc); +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(const char *rc); +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); +void kern_bzero(vm_offset_t dest, size_t len); +int kern_pread(int fd, vm_offset_t dest, size_t len, off_t off); +void *alloc_pread(int fd, off_t off, size_t len); + +/* bcache.c */ +void bcache_init(size_t nblks, size_t bsize); +void bcache_add_dev(int); +void *bcache_allocate(void); +void bcache_free(void *); +int bcache_strategy(void *devdata, 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; + void *dv_cache; +}; + +/* + * Modular console support. + */ +struct console +{ + const char *c_name; + const char *c_desc; + int c_flags; +#define C_PRESENTIN (1<<0) /* console can provide input */ +#define C_PRESENTOUT (1<<1) /* console can provide output */ +#define C_ACTIVEIN (1<<2) /* user wants input from console */ +#define C_ACTIVEOUT (1<<3) /* user wants output to console */ +#define C_WIDEOUT (1<<4) /* c_out routine groks wide chars */ + 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 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[]); +void unload(void); + +struct preloaded_file *file_alloc(void); +struct preloaded_file *file_findfile(const char *name, const char *type); +struct file_metadata *file_findmetadata(struct preloaded_file *fp, int type); +struct preloaded_file *file_loadraw(const char *name, char *type, int insert); +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); +void file_removemetadata(struct preloaded_file *fp); + +/* MI module loaders */ +#ifdef __elfN +/* Relocation types. */ +#define ELF_RELOC_REL 1 +#define ELF_RELOC_RELA 2 + +/* Relocation offset for some architectures */ +extern u_int64_t __elfN(relocation_offset); + +struct elf_file; +typedef Elf_Addr (symaddr_fn)(struct elf_file *ef, Elf_Size symidx); + +int __elfN(loadfile)(char *filename, u_int64_t dest, struct preloaded_file **result); +int __elfN(obj_loadfile)(char *filename, u_int64_t dest, + struct preloaded_file **result); +int __elfN(reloc)(struct elf_file *ef, symaddr_fn *symaddr, + const void *reldata, int reltype, Elf_Addr relbase, + Elf_Addr dataaddr, void *data, size_t len); +int __elfN(loadfile_raw)(char *filename, u_int64_t dest, + struct preloaded_file **result, int multiboot); +int __elfN(load_modmetadata)(struct preloaded_file *fp, u_int64_t dest); +#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); + + /* + * Interface to adjust the load address according to the "object" + * being loaded. + */ + uint64_t (*arch_loadaddr)(u_int type, void *data, uint64_t addr); +#define LOAD_ELF 1 /* data points to the ELF header. */ +#define LOAD_RAW 2 /* data points to the file name. */ + + /* + * Interface to inform MD code about a loaded (ELF) segment. This + * can be used to flush caches and/or set up translations. + */ +#ifdef __elfN + void (*arch_loadseg)(Elf_Ehdr *eh, Elf_Phdr *ph, uint64_t delta); +#else + void (*arch_loadseg)(void *eh, void *ph, uint64_t delta); +#endif + + /* Probe ZFS pool(s), if needed. */ + void (*arch_zfs_probe)(void); +}; +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); + +#ifndef CTASSERT /* Allow lint to override */ +#define CTASSERT(x) _CTASSERT(x, __LINE__) +#define _CTASSERT(x, y) __CTASSERT(x, y) +#define __CTASSERT(x, y) typedef char __assert ## y[(x) ? 1 : -1] +#endif + +#endif /* !_BOOTSTRAP_H_ */ diff --git a/stand/common/commands.c b/stand/common/commands.c new file mode 100644 index 0000000..def7ff2 --- /dev/null +++ b/stand/common/commands.c @@ -0,0 +1,511 @@ +/*- + * Copyright (c) 1998 Michael Smith + * 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 +__FBSDID("$FreeBSD$"); + +#include +#include + +#include "bootstrap.h" + +char *command_errmsg; +/* XXX should have procedural interface for setting, size limit? */ +char command_errbuf[COMMAND_ERRBUFSZ]; + +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 int +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); + } + return (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 */ + snprintf(buf, sizeof(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 []"; + close(hfd); + 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 */ + if (help_emitsummary(t, s, d)) + break; + + } 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 */ + if (help_emitsummary(t, s, d)) + break; + } + } + free(t); + free(s); + free(d); + } + pager_close(); + close(hfd); + if (!matched) { + snprintf(command_errbuf, sizeof(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); + +/* + * Please note: although we use the pager for the list of commands, + * this routine is called from the ? FORTH function which then + * unconditionally prints some commands. This will lead to anomalous + * behavior. There's no 'pager_output' binding to FORTH to allow + * things to work right, so I'm documenting the bug rather than + * fixing it. + */ +static int +command_commandlist(int argc, char *argv[]) +{ + struct bootblk_command **cmdp; + int res; + char name[20]; + + res = 0; + pager_open(); + res = pager_output("Available commands:\n"); + SET_FOREACH(cmdp, Xcommand_set) { + if (res) + break; + if (((*cmdp)->c_name != NULL) && ((*cmdp)->c_desc != NULL)) { + sprintf(name, " %-15s ", (*cmdp)->c_name); + pager_output(name); + pager_output((*cmdp)->c_desc); + res = pager_output("\n"); + } + } + pager_close(); + 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 { + snprintf(command_errbuf, sizeof(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", "echo arguments", 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", "read input from the terminal", 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) { + snprintf(command_errbuf, sizeof(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) { + snprintf(command_errbuf, sizeof(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){ + if (devsw[i]->dv_print(verbose)) + break; + } else { + sprintf(line, "%s: (unknown)\n", devsw[i]->dv_name); + if (pager_output(line)) + break; + } + } + pager_close(); + return(CMD_OK); +} + diff --git a/stand/common/console.c b/stand/common/console.c new file mode 100644 index 0000000..f4ffc56 --- /dev/null +++ b/stand/common/console.c @@ -0,0 +1,302 @@ +/*- + * Copyright (c) 1998 Michael Smith + * 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 +__FBSDID("$FreeBSD$"); + +#include +#include + +#include "bootstrap.h" +/* + * Core console support + */ + +static int cons_set(struct env_var *ev, int flags, const void *value); +static int cons_find(const char *name); +static int cons_check(const char *string); +static int cons_change(const char *string); +static int twiddle_set(struct env_var *ev, int flags, const void *value); + +/* + * Detect possible console(s) to use. If preferred console(s) have been + * specified, mark them as active. Else, mark the first probed console + * as active. Also create the console variable. + */ +void +cons_probe(void) +{ + int cons; + int active; + char *prefconsole; + + /* We want a callback to install the new value when this var changes. */ + env_setenv("twiddle_divisor", EV_VOLATILE, "1", twiddle_set, env_nounset); + + /* 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; + } + /* Force a console even if all probes failed */ + if (active == -1) + active = 0; + + /* 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 */ + cons_change(prefconsole); + } else { + consoles[active]->c_flags |= C_ACTIVEIN | C_ACTIVEOUT; + consoles[active]->c_init(0); + prefconsole = strdup(consoles[active]->c_name); + } + + printf("Consoles: "); + for (cons = 0; consoles[cons] != NULL; cons++) + if (consoles[cons]->c_flags & (C_ACTIVEIN | C_ACTIVEOUT)) + printf("%s ", consoles[cons]->c_desc); + printf("\n"); + + if (prefconsole != NULL) { + env_setenv("console", EV_VOLATILE, prefconsole, cons_set, + env_nounset); + free(prefconsole); + } +} + +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_PRESENTIN | C_ACTIVEIN)) == + (C_PRESENTIN | 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_PRESENTIN | C_ACTIVEIN)) == + (C_PRESENTIN | 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_PRESENTOUT | C_ACTIVEOUT)) == + (C_PRESENTOUT | C_ACTIVEOUT)) + consoles[cons]->c_out(c); +} + +/* + * Find the console with the specified name. + */ +static int +cons_find(const char *name) +{ + int cons; + + for (cons = 0; consoles[cons] != NULL; cons++) + if (!strcmp(consoles[cons]->c_name, name)) + return (cons); + return (-1); +} + +/* + * Select one or more consoles. + */ +static int +cons_set(struct env_var *ev, int flags, const void *value) +{ + int ret; + + if ((value == NULL) || (cons_check(value) == 0)) { + /* + * Return CMD_OK instead of CMD_ERROR to prevent forth syntax error, + * which would prevent it processing any further loader.conf entries. + */ + return (CMD_OK); + } + + ret = cons_change(value); + if (ret != CMD_OK) + return (ret); + + env_setenv(ev->ev_name, flags | EV_NOHOOK, value, NULL, NULL); + return (CMD_OK); +} + +/* + * Check that at least one the consoles listed in *string is valid + */ +static int +cons_check(const char *string) +{ + int cons, found, failed; + char *curpos, *dup, *next; + + dup = next = strdup(string); + found = failed = 0; + while (next != NULL) { + curpos = strsep(&next, " ,"); + if (*curpos != '\0') { + cons = cons_find(curpos); + if (cons == -1) { + printf("console %s is invalid!\n", curpos); + failed++; + } else { + found++; + } + } + } + + free(dup); + + if (found == 0) + printf("no valid consoles!\n"); + + if (found == 0 || failed != 0) { + printf("Available consoles:\n"); + for (cons = 0; consoles[cons] != NULL; cons++) + printf(" %s\n", consoles[cons]->c_name); + } + + return (found); +} + +/* + * Activate all the valid consoles listed in *string and disable all others. + */ +static int +cons_change(const char *string) +{ + int cons, active; + char *curpos, *dup, *next; + + /* Disable all consoles */ + for (cons = 0; consoles[cons] != NULL; cons++) { + consoles[cons]->c_flags &= ~(C_ACTIVEIN | C_ACTIVEOUT); + } + + /* Enable selected consoles */ + dup = next = strdup(string); + active = 0; + while (next != NULL) { + curpos = strsep(&next, " ,"); + if (*curpos == '\0') + continue; + cons = cons_find(curpos); + if (cons >= 0) { + consoles[cons]->c_flags |= C_ACTIVEIN | C_ACTIVEOUT; + consoles[cons]->c_init(0); + if ((consoles[cons]->c_flags & (C_PRESENTIN | C_PRESENTOUT)) == + (C_PRESENTIN | C_PRESENTOUT)) { + active++; + continue; + } + + if (active != 0) { + /* If no consoles have initialised we wouldn't see this. */ + printf("console %s failed to initialize\n", consoles[cons]->c_name); + } + } + } + + free(dup); + + if (active == 0) { + /* All requested consoles failed to initialise, try to recover. */ + for (cons = 0; consoles[cons] != NULL; cons++) { + consoles[cons]->c_flags |= C_ACTIVEIN | C_ACTIVEOUT; + consoles[cons]->c_init(0); + if ((consoles[cons]->c_flags & + (C_PRESENTIN | C_PRESENTOUT)) == + (C_PRESENTIN | C_PRESENTOUT)) + active++; + } + + if (active == 0) + return (CMD_ERROR); /* Recovery failed. */ + } + + return (CMD_OK); +} + +/* + * Change the twiddle divisor. + * + * The user can set the twiddle_divisor variable to directly control how fast + * the progress twiddle spins, useful for folks with slow serial consoles. The + * code to monitor changes to the variable and propagate them to the twiddle + * routines has to live somewhere. Twiddling is console-related so it's here. + */ +static int +twiddle_set(struct env_var *ev, int flags, const void *value) +{ + u_long tdiv; + char * eptr; + + tdiv = strtoul(value, &eptr, 0); + if (*(const char *)value == 0 || *eptr != 0) { + printf("invalid twiddle_divisor '%s'\n", (const char *)value); + return (CMD_ERROR); + } + twiddle_divisor((u_int)tdiv); + env_setenv(ev->ev_name, flags | EV_NOHOOK, value, NULL, NULL); + + return(CMD_OK); +} diff --git a/stand/common/dev_net.c b/stand/common/dev_net.c new file mode 100644 index 0000000..c5b1e0a --- /dev/null +++ b/stand/common/dev_net.c @@ -0,0 +1,435 @@ +/* $NetBSD: dev_net.c,v 1.23 2008/04/28 20:24:06 martin 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. + * + * 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 +__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 +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#include "dev_net.h" +#include "bootstrap.h" + +#ifdef NETIF_DEBUG +int debug = 0; +#endif + +static char *netdev_name; +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 void net_cleanup(void); +static int net_strategy(void *, int, daddr_t, size_t, char *, size_t *); +static int 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, + net_cleanup +}; + +static struct uri_scheme { + const char *scheme; + int proto; +} uri_schemes[] = { + { "tftp:/", NET_TFTP }, + { "nfs:/", NET_NFS }, +}; + +static 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... + */ +static int +net_open(struct open_file *f, ...) +{ + struct iodesc *d; + 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); + + /* Before opening another interface, close the previous one first. */ + if (netdev_sock >= 0 && strcmp(devname, netdev_name) != 0) + net_cleanup(); + + /* 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); + } + netdev_name = strdup(devname); +#ifdef NETIF_DEBUG + if (debug) + printf("net_open: netif_open() succeeded\n"); +#endif + } + /* + * If network params were not set by netif_open(), try to get + * them via bootp, rarp, etc. + */ + if (rootip.s_addr == 0) { + /* Get root IP address, and path, etc. */ + error = net_getparams(netdev_sock); + if (error) { + /* getparams makes its own noise */ + free(netdev_name); + netif_close(netdev_sock); + netdev_sock = -1; + return (error); + } + } + /* + * Set the variables required by the kernel's nfs_diskless + * mechanism. This is the minimum set of variables required to + * mount a root filesystem without needing to obtain additional + * info from bootp or other sources. + */ + d = socktodesc(netdev_sock); + setenv("boot.netif.hwaddr", ether_sprintf(d->myea), 1); + 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.server", inet_ntoa(rootip), 1); + if (netproto == NET_TFTP) { + setenv("boot.tftproot.server", inet_ntoa(rootip), 1); + setenv("boot.tftproot.path", rootpath, 1); + } else if (netproto == NET_NFS) { + setenv("boot.nfsroot.server", inet_ntoa(rootip), 1); + setenv("boot.nfsroot.path", rootpath, 1); + } + if (intf_mtu != 0) { + char mtu[16]; + snprintf(mtu, sizeof(mtu), "%u", intf_mtu); + setenv("boot.netif.mtu", mtu, 1); + } + + } + netdev_opens++; + f->f_devdata = &netdev_sock; + return (error); +} + +static int +net_close(struct open_file *f) +{ + +#ifdef NETIF_DEBUG + if (debug) + printf("net_close: opens=%d\n", netdev_opens); +#endif + + f->f_devdata = NULL; + + return (0); +} + +static void +net_cleanup(void) +{ + + if (netdev_sock >= 0) { +#ifdef NETIF_DEBUG + if (debug) + printf("net_cleanup: calling netif_close()\n"); +#endif + rootip.s_addr = 0; + free(netdev_name); + netif_close(netdev_sock); + netdev_sock = -1; + } +} + +static int +net_strategy(void *devdata, int rw, daddr_t blk, size_t size, char *buf, + size_t *rsize) +{ + + 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(int sock) +{ + char buf[MAXHOSTNAMELEN]; + n_long rootaddr, 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); + if (myip.s_addr != 0) + goto exit; +#ifdef NETIF_DEBUG + if (debug) + printf("net_open: BOOTP failed, trying RARP/RPC...\n"); +#endif +#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); + } +#ifdef NETIF_DEBUG + if (debug) + printf("net_open: client name: %s\n", hostname); +#endif + + /* + * 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; +#ifdef NETIF_DEBUG + if (debug) + printf("net_open: subnet mask: %s\n", intoa(netmask)); +#endif + } +#ifdef NETIF_DEBUG + if (gateip.s_addr && debug) + printf("net_open: net gateway: %s\n", inet_ntoa(gateip)); +#endif + + /* 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 ((rootaddr = net_parse_rootpath()) != INADDR_NONE) + rootip.s_addr = rootaddr; + +#ifdef NETIF_DEBUG + if (debug) { + printf("net_open: server addr: %s\n", inet_ntoa(rootip)); + printf("net_open: server path: %s\n", rootpath); + } +#endif + + return (0); +} + +static int +net_print(int verbose) +{ + struct netif_driver *drv; + int i, d, cnt; + int ret = 0; + + if (netif_drivers[0] == NULL) + return (ret); + + printf("%s devices:", netdev.dv_name); + if ((ret = pager_output("\n")) != 0) + return (ret); + + cnt = 0; + for (d = 0; netif_drivers[d]; d++) { + drv = netif_drivers[d]; + for (i = 0; i < drv->netif_nifs; i++) { + printf("\t%s%d:", netdev.dv_name, cnt++); + if (verbose) { + printf(" (%s%d)", drv->netif_bname, + drv->netif_ifs[i].dif_unit); + } + if ((ret = pager_output("\n")) != 0) + return (ret); + } + } + return (ret); +} + +/* + * Parses the rootpath if present + * + * The rootpath format can be in the form + * ://ip/path + * :/path + * + * For compatibility with previous behaviour it also accepts as an NFS scheme + * ip:/path + * /path + * + * If an ip is set it returns it in network byte order. + * The default scheme defined in the global netproto, if not set it defaults to + * NFS. + * It leaves just the pathname in the global rootpath. + */ +uint32_t +net_parse_rootpath() +{ + n_long addr = htonl(INADDR_NONE); + size_t i; + char ip[FNAME_SIZE]; + char *ptr, *val; + + netproto = NET_NONE; + + for (i = 0; i < nitems(uri_schemes); i++) { + if (strncmp(rootpath, uri_schemes[i].scheme, + strlen(uri_schemes[i].scheme)) != 0) + continue; + + netproto = uri_schemes[i].proto; + break; + } + ptr = rootpath; + /* Fallback for compatibility mode */ + if (netproto == NET_NONE) { + netproto = NET_NFS; + (void)strsep(&ptr, ":"); + if (ptr != NULL) { + addr = inet_addr(rootpath); + bcopy(ptr, rootpath, strlen(ptr) + 1); + } + } else { + ptr += strlen(uri_schemes[i].scheme); + if (*ptr == '/') { + /* we are in the form ://, we do expect an ip */ + ptr++; + /* + * XXX when http will be there we will need to check for + * a port, but right now we do not need it yet + */ + val = strchr(ptr, '/'); + if (val != NULL) { + snprintf(ip, sizeof(ip), "%.*s", + (int)((uintptr_t)val - (uintptr_t)ptr), + ptr); + addr = inet_addr(ip); + bcopy(val, rootpath, strlen(val) + 1); + } + } else { + ptr--; + bcopy(ptr, rootpath, strlen(ptr) + 1); + } + } + + return (addr); +} diff --git a/stand/common/dev_net.h b/stand/common/dev_net.h new file mode 100644 index 0000000..995b672 --- /dev/null +++ b/stand/common/dev_net.h @@ -0,0 +1,36 @@ +/*- + * Copyright (c) 1998 Doug Rabson + * 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$ + */ + +#ifndef _BOOT_DEV_NET_H_ +#define _BOOT_DEV_NET_H_ + +extern struct devsw netdev; + +uint32_t net_parse_rootpath(void); + +#endif diff --git a/stand/common/devopen.c b/stand/common/devopen.c new file mode 100644 index 0000000..de6165c --- /dev/null +++ b/stand/common/devopen.c @@ -0,0 +1,67 @@ +/*- + * Copyright (c) 1998 Michael Smith + * 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 +__FBSDID("$FreeBSD$"); + +#include +#include + +#include "bootstrap.h" + +int +devopen(struct open_file *f, const char *fname, const char **file) +{ + struct devdesc *dev; + int result; + + result = archsw.arch_getdev((void **)&dev, fname, file); + if (result) + return (result); + + /* point to device-specific data so that device open can use it */ + f->f_devdata = dev; + result = dev->d_dev->dv_open(f, dev); + if (result != 0) { + f->f_devdata = NULL; + free(dev); + return (result); + } + + /* reference the devsw entry from the open_file structure */ + f->f_dev = dev->d_dev; + return (0); +} + +int +devclose(struct open_file *f) +{ + + if (f->f_devdata != NULL) { + free(f->f_devdata); + } + return (0); +} diff --git a/stand/common/disk.c b/stand/common/disk.c new file mode 100644 index 0000000..4cb57d4 --- /dev/null +++ b/stand/common/disk.c @@ -0,0 +1,432 @@ +/*- + * Copyright (c) 1998 Michael Smith + * Copyright (c) 2012 Andrey V. Elsukov + * 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 +__FBSDID("$FreeBSD$"); + +#include +#include +#include +#include +#include +#include + +#include "disk.h" + +#ifdef DISK_DEBUG +# define DEBUG(fmt, args...) printf("%s: " fmt "\n" , __func__ , ## args) +#else +# define DEBUG(fmt, args...) +#endif + +struct open_disk { + struct ptable *table; + uint64_t mediasize; + uint64_t entrysize; + u_int sectorsize; +}; + +struct print_args { + struct disk_devdesc *dev; + const char *prefix; + int verbose; +}; + +/* Convert size to a human-readable number. */ +static char * +display_size(uint64_t size, u_int sectorsize) +{ + static char buf[80]; + char unit; + + size = size * sectorsize / 1024; + unit = 'K'; + if (size >= 10485760000LL) { + size /= 1073741824; + unit = 'T'; + } else if (size >= 10240000) { + size /= 1048576; + unit = 'G'; + } else if (size >= 10000) { + size /= 1024; + unit = 'M'; + } + sprintf(buf, "%ld%cB", (long)size, unit); + return (buf); +} + +int +ptblread(void *d, void *buf, size_t blocks, uint64_t offset) +{ + struct disk_devdesc *dev; + struct open_disk *od; + + dev = (struct disk_devdesc *)d; + od = (struct open_disk *)dev->d_opendata; + + /* + * The strategy function assumes the offset is in units of 512 byte + * sectors. For larger sector sizes, we need to adjust the offset to + * match the actual sector size. + */ + offset *= (od->sectorsize / 512); + /* + * As the GPT backup partition is located at the end of the disk, + * to avoid reading past disk end, flag bcache not to use RA. + */ + return (dev->d_dev->dv_strategy(dev, F_READ | F_NORA, offset, + blocks * od->sectorsize, (char *)buf, NULL)); +} + +#define PWIDTH 35 +static int +ptable_print(void *arg, const char *pname, const struct ptable_entry *part) +{ + struct disk_devdesc dev; + struct print_args *pa, bsd; + struct open_disk *od; + struct ptable *table; + char line[80]; + int res; + + pa = (struct print_args *)arg; + od = (struct open_disk *)pa->dev->d_opendata; + sprintf(line, " %s%s: %s", pa->prefix, pname, + parttype2str(part->type)); + if (pa->verbose) + sprintf(line, "%-*s%s", PWIDTH, line, + display_size(part->end - part->start + 1, + od->sectorsize)); + strcat(line, "\n"); + if (pager_output(line)) + return 1; + res = 0; + if (part->type == PART_FREEBSD) { + /* Open slice with BSD label */ + dev.d_dev = pa->dev->d_dev; + dev.d_unit = pa->dev->d_unit; + dev.d_slice = part->index; + dev.d_partition = -1; + if (disk_open(&dev, part->end - part->start + 1, + od->sectorsize) == 0) { + table = ptable_open(&dev, part->end - part->start + 1, + od->sectorsize, ptblread); + if (table != NULL) { + sprintf(line, " %s%s", pa->prefix, pname); + bsd.dev = pa->dev; + bsd.prefix = line; + bsd.verbose = pa->verbose; + res = ptable_iterate(table, &bsd, ptable_print); + ptable_close(table); + } + disk_close(&dev); + } + } + + return (res); +} +#undef PWIDTH + +int +disk_print(struct disk_devdesc *dev, char *prefix, int verbose) +{ + struct open_disk *od; + struct print_args pa; + + /* Disk should be opened */ + od = (struct open_disk *)dev->d_opendata; + pa.dev = dev; + pa.prefix = prefix; + pa.verbose = verbose; + return (ptable_iterate(od->table, &pa, ptable_print)); +} + +int +disk_read(struct disk_devdesc *dev, void *buf, uint64_t offset, u_int blocks) +{ + struct open_disk *od; + int ret; + + od = (struct open_disk *)dev->d_opendata; + ret = dev->d_dev->dv_strategy(dev, F_READ, dev->d_offset + offset, + blocks * od->sectorsize, buf, NULL); + + return (ret); +} + +int +disk_write(struct disk_devdesc *dev, void *buf, uint64_t offset, u_int blocks) +{ + struct open_disk *od; + int ret; + + od = (struct open_disk *)dev->d_opendata; + ret = dev->d_dev->dv_strategy(dev, F_WRITE, dev->d_offset + offset, + blocks * od->sectorsize, buf, NULL); + + return (ret); +} + +int +disk_ioctl(struct disk_devdesc *dev, u_long cmd, void *data) +{ + struct open_disk *od = dev->d_opendata; + + if (od == NULL) + return (ENOTTY); + + switch (cmd) { + case DIOCGSECTORSIZE: + *(u_int *)data = od->sectorsize; + break; + case DIOCGMEDIASIZE: + if (dev->d_offset == 0) + *(uint64_t *)data = od->mediasize; + else + *(uint64_t *)data = od->entrysize * od->sectorsize; + break; + default: + return (ENOTTY); + } + + return (0); +} + +int +disk_open(struct disk_devdesc *dev, uint64_t mediasize, u_int sectorsize) +{ + struct open_disk *od; + struct ptable *table; + struct ptable_entry part; + int rc, slice, partition; + + rc = 0; + /* + * While we are reading disk metadata, make sure we do it relative + * to the start of the disk + */ + dev->d_offset = 0; + table = NULL; + slice = dev->d_slice; + partition = dev->d_partition; + od = (struct open_disk *)malloc(sizeof(struct open_disk)); + if (od == NULL) { + DEBUG("no memory"); + return (ENOMEM); + } + dev->d_opendata = od; + od->entrysize = 0; + od->mediasize = mediasize; + od->sectorsize = sectorsize; + DEBUG("%s unit %d, slice %d, partition %d => %p", + disk_fmtdev(dev), dev->d_unit, dev->d_slice, dev->d_partition, od); + + /* Determine disk layout. */ + od->table = ptable_open(dev, mediasize / sectorsize, sectorsize, + ptblread); + if (od->table == NULL) { + DEBUG("Can't read partition table"); + rc = ENXIO; + goto out; + } + + if (ptable_getsize(od->table, &mediasize) != 0) { + rc = ENXIO; + goto out; + } + if (mediasize > od->mediasize) { + od->mediasize = mediasize; + } + + if (ptable_gettype(od->table) == PTABLE_BSD && + partition >= 0) { + /* It doesn't matter what value has d_slice */ + rc = ptable_getpart(od->table, &part, partition); + if (rc == 0) { + dev->d_offset = part.start; + od->entrysize = part.end - part.start + 1; + } + } else if (slice >= 0) { + /* Try to get information about partition */ + if (slice == 0) + rc = ptable_getbestpart(od->table, &part); + else + rc = ptable_getpart(od->table, &part, slice); + if (rc != 0) /* Partition doesn't exist */ + goto out; + dev->d_offset = part.start; + od->entrysize = part.end - part.start + 1; + slice = part.index; + if (ptable_gettype(od->table) == PTABLE_GPT) { + partition = 255; + goto out; /* Nothing more to do */ + } else if (partition == 255) { + /* + * When we try to open GPT partition, but partition + * table isn't GPT, reset d_partition value to -1 + * and try to autodetect appropriate value. + */ + partition = -1; + } + /* + * If d_partition < 0 and we are looking at a BSD slice, + * then try to read BSD label, otherwise return the + * whole MBR slice. + */ + if (partition == -1 && + part.type != PART_FREEBSD) + goto out; + /* Try to read BSD label */ + table = ptable_open(dev, part.end - part.start + 1, + od->sectorsize, ptblread); + if (table == NULL) { + DEBUG("Can't read BSD label"); + rc = ENXIO; + goto out; + } + /* + * If slice contains BSD label and d_partition < 0, then + * assume the 'a' partition. Otherwise just return the + * whole MBR slice, because it can contain ZFS. + */ + if (partition < 0) { + if (ptable_gettype(table) != PTABLE_BSD) + goto out; + partition = 0; + } + rc = ptable_getpart(table, &part, partition); + if (rc != 0) + goto out; + dev->d_offset += part.start; + od->entrysize = part.end - part.start + 1; + } +out: + if (table != NULL) + ptable_close(table); + + if (rc != 0) { + if (od->table != NULL) + ptable_close(od->table); + free(od); + DEBUG("%s could not open", disk_fmtdev(dev)); + } else { + /* Save the slice and partition number to the dev */ + dev->d_slice = slice; + dev->d_partition = partition; + DEBUG("%s offset %lld => %p", disk_fmtdev(dev), + (long long)dev->d_offset, od); + } + return (rc); +} + +int +disk_close(struct disk_devdesc *dev) +{ + struct open_disk *od; + + od = (struct open_disk *)dev->d_opendata; + DEBUG("%s closed => %p", disk_fmtdev(dev), od); + ptable_close(od->table); + free(od); + return (0); +} + +char* +disk_fmtdev(struct disk_devdesc *dev) +{ + static char buf[128]; + char *cp; + + cp = buf + sprintf(buf, "%s%d", dev->d_dev->dv_name, dev->d_unit); + if (dev->d_slice >= 0) { +#ifdef LOADER_GPT_SUPPORT + if (dev->d_partition == 255) { + sprintf(cp, "p%d:", dev->d_slice); + return (buf); + } else +#endif +#ifdef LOADER_MBR_SUPPORT + cp += sprintf(cp, "s%d", dev->d_slice); +#endif + } + if (dev->d_partition >= 0) + cp += sprintf(cp, "%c", dev->d_partition + 'a'); + strcat(cp, ":"); + return (buf); +} + +int +disk_parsedev(struct disk_devdesc *dev, const char *devspec, const char **path) +{ + int unit, slice, partition; + const char *np; + char *cp; + + np = devspec; + unit = slice = partition = -1; + if (*np != '\0' && *np != ':') { + unit = strtol(np, &cp, 10); + if (cp == np) + return (EUNIT); +#ifdef LOADER_GPT_SUPPORT + if (*cp == 'p') { + np = cp + 1; + slice = strtol(np, &cp, 10); + if (np == cp) + return (ESLICE); + /* we don't support nested partitions on GPT */ + if (*cp != '\0' && *cp != ':') + return (EINVAL); + partition = 255; + } else +#endif +#ifdef LOADER_MBR_SUPPORT + if (*cp == 's') { + np = cp + 1; + slice = strtol(np, &cp, 10); + if (np == cp) + return (ESLICE); + } +#endif + if (*cp != '\0' && *cp != ':') { + partition = *cp - 'a'; + if (partition < 0) + return (EPART); + cp++; + } + } else + return (EINVAL); + + if (*cp != '\0' && *cp != ':') + return (EINVAL); + dev->d_unit = unit; + dev->d_slice = slice; + dev->d_partition = partition; + if (path != NULL) + *path = (*cp == '\0') ? cp: cp + 1; + return (0); +} diff --git a/stand/common/disk.h b/stand/common/disk.h new file mode 100644 index 0000000..51e1498 --- /dev/null +++ b/stand/common/disk.h @@ -0,0 +1,117 @@ +/*- + * Copyright (c) 2011 Google, Inc. + * 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$ + */ + +/* + * Device descriptor for partitioned disks. To use, set the + * d_slice and d_partition variables as follows: + * + * Whole disk access: + * + * d_slice = -1 + * d_partition = -1 + * + * Whole MBR slice: + * + * d_slice = MBR slice number (typically 1..4) + * d_partition = -1 + * + * BSD disklabel partition within an MBR slice: + * + * d_slice = MBR slice number (typically 1..4) + * d_partition = disklabel partition (typically 0..19) + * + * BSD disklabel partition on the true dedicated disk: + * + * d_slice = -1 + * d_partition = disklabel partition (typically 0..19) + * + * GPT partition: + * + * d_slice = GPT partition number (typically 1..N) + * d_partition = 255 + * + * For both MBR and GPT, to automatically find the 'best' slice or partition, + * set d_slice to zero. This uses the partition type to decide which partition + * to use according to the following list of preferences: + * + * FreeBSD (active) + * FreeBSD (inactive) + * Linux (active) + * Linux (inactive) + * DOS/Windows (active) + * DOS/Windows (inactive) + * + * Active MBR slices (marked as bootable) are preferred over inactive. GPT + * doesn't have the concept of active/inactive partitions. In both MBR and GPT, + * if there are multiple slices/partitions of a given type, the first one + * is chosen. + * + * The low-level disk device will typically call disk_open() from its open + * method to interpret the disk partition tables according to the rules above. + * This will initialize d_offset to the block offset of the start of the + * selected partition - this offset should be added to the offset passed to + * the device's strategy method. + */ + +#ifndef _DISK_H +#define _DISK_H + +struct disk_devdesc +{ + struct devsw *d_dev; + int d_type; + int d_unit; + void *d_opendata; + int d_slice; + int d_partition; + uint64_t d_offset; +}; + +enum disk_ioctl { + IOCTL_GET_BLOCKS, + IOCTL_GET_BLOCK_SIZE +}; + +/* + * Parse disk metadata and initialise dev->d_offset. + */ +extern int disk_open(struct disk_devdesc *, uint64_t, u_int); +extern int disk_close(struct disk_devdesc *); +extern int disk_ioctl(struct disk_devdesc *, u_long, void *); +extern int disk_read(struct disk_devdesc *, void *, uint64_t, u_int); +extern int disk_write(struct disk_devdesc *, void *, uint64_t, u_int); +extern int ptblread(void *, void *, size_t, uint64_t); + +/* + * Print information about slices on a disk. + */ +extern int disk_print(struct disk_devdesc *, char *, int); +extern char* disk_fmtdev(struct disk_devdesc *); +extern int disk_parsedev(struct disk_devdesc *, const char *, const char **); + +#endif /* _DISK_H */ diff --git a/stand/common/help.common b/stand/common/help.common new file mode 100644 index 0000000..3618501 --- /dev/null +++ b/stand/common/help.common @@ -0,0 +1,407 @@ +################################################################################ +# Thelp DDisplay command help + + help [topic [subtopic]] + help index + + 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. + +################################################################################ +# T? DList available commands + + ? + + Lists all available commands. + +################################################################################ +# Tautoboot DBoot after a delay + + autoboot [ []] + + Displays or a default prompt, and counts down seconds + before attempting to boot. If is not specified, the default + value is 10. + +################################################################################ +# Tboot DBoot immediately + + boot [] [- ...] + + Boot the system. If arguments are specified, they are added to the + arguments for the kernel. If is specified, and a kernel + has not already been loaded, it will be booted instead of the default + kernel. + +################################################################################ +# Tbcachestat DGet disk block cache stats + + bcachestat + + Displays statistics about disk cache usage. For debugging only. + +################################################################################ +# Techo DEcho arguments + + echo [-n] [] + + Emits , 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 ] + + Loads the module contained in into memory. If no other + modules are loaded, must be a kernel or the command will + fail. + + If -t is specified, the module is loaded as raw data of , for + later use by the kernel or other modules. may be any string. + +################################################################################ +# Tls DList files + + ls [-l] [] + + Displays a listing of files in the directory , or the root + directory of the current device if 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 [ ...] + + 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 + set = + + The set command is used to set variables. + +################################################################################ +# Tset Sautoboot_delay DSet the default autoboot delay + + set autoboot_delay= + + Sets the default delay for the autoboot command to seconds. + Set value to -1 if you don't want to allow user to interrupt autoboot + process and escape to the loader prompt. + +################################################################################ +# Tset Sbootfile DSet the default boot file set + + set bootfile=[;...] + + 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_cdrom DMount root file system from CD-ROM + + set boot_cdrom + + Instructs the kernel to try to mount the root file system from CD-ROM. + +################################################################################ +# 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 initialize when booted. + +################################################################################ +# Tset Sboot_dfltroot DUse default root file system + + set boot_dfltroot + + Instructs the kernel to mount the statically compiled-in root + file system. + +################################################################################ +# Tset Sboot_gdb DSelect gdb-remote mode for the kernel debugger + + set boot_gdb + + Selects gdb-remote mode for the kernel debugger by default. + +################################################################################ +# Tset Sboot_multicons DUse multiple consoles + + set boot_multicons + + Enables multiple console support in the kernel early on boot. + In a running system, console configuration can be manipulated + by the conscontrol(8) utility. + +################################################################################ +# Tset Sboot_mute DMute the console + + set boot_mute + + All console output is suppressed when console is muted. + In a running system, the state of console muting can be + manipulated by the conscontrol(8) utility. + +################################################################################ +# Tset Sboot_pause DPause after each line during device probing + + set boot_pause + + During the device probe, pause after each line is printed. + +################################################################################ +# Tset Sboot_serial DUse serial console + + set boot_serial + + Force the use of a serial console even when an internal console + is present. + +################################################################################ +# Tset Sboot_single DStart system in single-user mode + + set boot_single + + Prevents the kernel from initiating a multi-user startup; instead, + a 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[=] + + Sets the current console. If is omitted, a list of valid + consoles will be displayed. + +################################################################################ +# Tset Scurrdev DSet the current device + + set currdev= + + Selects the default device. See lsdev for available devices. + +################################################################################ +# Tset Sinit_path DSet the list of init candidates + + set init_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=[;...] + + 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/modules" with the kernel directory + prepended. + +################################################################################ +# Tset Sprompt DSet the command prompt + + set prompt= + + 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=\${interpret} + +################################################################################ +# Tset Srootdev DSet the root filesystem + + set rootdev= + + 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= + + 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= NSFBUFS + + Set the number of sendfile buffers to be allocated. This + overrides the value determined when the kernel was compiled. + + set vm.kmem_size= 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= 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= + + Disable VT switching on suspend. + + value is 0 (default) or non-zero to enable. + + set hw.physmem= MAXMEM (i386 only) + + Limits the amount of physical memory space available to + the system to bytes. 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.{acpi,pci}.host_start_mem= + + Sets the lowest address that the pci code will assign + when it doesn't have other information about the address + to assign (like from a pci bridge). This is only useful + in older systems without a pci bridge. Also, it only + impacts devices that the BIOS doesn't assign to, typically + CardBus bridges. The default is 0x80000000, but + some systems need values like 0xf0000000, 0xfc000000 or + 0xfe000000 may be suitable for older systems (the older + the system, the higher the number typically should be). + + set hw.pci.enable_io_modes= + + 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 [] + + Displays the value of , or all variables if not specified. + Multiple paths can be separated with a semicolon. + +################################################################################ +# Tinclude DRead commands from a script file + + include [ ...] + + The entire contents of 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 ] [-p ] [] + + 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 seconds. (Any keypress will cancel the + timeout). + + If -p is specified, 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 + + If allowed, the named variable's value is discarded and the variable + is removed. + +################################################################################ diff --git a/stand/common/install.c b/stand/common/install.c new file mode 100644 index 0000000..8c19066 --- /dev/null +++ b/stand/common/install.c @@ -0,0 +1,355 @@ +/*- + * Copyright (c) 2008-2014, Juniper Networks, Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE 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 +__FBSDID("$FreeBSD$"); + +#include +#include +#include +#include +#include + +#include +#include +#include + +#include "bootstrap.h" + +extern struct in_addr servip; + +extern int pkgfs_init(const char *, struct fs_ops *); +extern void pkgfs_cleanup(void); + +COMMAND_SET(install, "install", "install software package", command_install); + +static char *inst_kernel; +static char **inst_modules; +static char *inst_rootfs; +static char *inst_loader_rc; + +static int +setpath(char **what, char *val) +{ + char *path; + size_t len; + int rel; + + len = strlen(val) + 1; + rel = (val[0] != '/') ? 1 : 0; + path = malloc(len + rel); + if (path == NULL) + return (ENOMEM); + path[0] = '/'; + strcpy(path + rel, val); + + *what = path; + return (0); +} + +static int +setmultipath(char ***what, char *val) +{ + char *s, *v; + int count, error, idx; + + count = 0; + v = val; + do { + count++; + s = strchr(v, ','); + v = (s == NULL) ? NULL : s + 1; + } while (v != NULL); + + *what = calloc(count + 1, sizeof(char *)); + if (*what == NULL) + return (ENOMEM); + + for (idx = 0; idx < count; idx++) { + s = strchr(val, ','); + if (s != NULL) + *s++ = '\0'; + error = setpath(*what + idx, val); + if (error) + return (error); + val = s; + } + + return (0); +} + +static int +read_metatags(int fd) +{ + char buf[1024]; + char *p, *tag, *val; + ssize_t fsize; + int error; + + fsize = read(fd, buf, sizeof(buf)); + if (fsize == -1) + return (errno); + + /* + * Assume that if we read a whole buffer worth of data, we + * haven't read the entire file. In other words, the buffer + * size must always be larger than the file size. That way + * we can append a '\0' and use standard string operations. + * Return an error if this is not possible. + */ + if (fsize == sizeof(buf)) + return (ENOMEM); + + buf[fsize] = '\0'; + error = 0; + tag = buf; + while (!error && *tag != '\0') { + val = strchr(tag, '='); + if (val == NULL) { + error = EINVAL; + break; + } + *val++ = '\0'; + p = strchr(val, '\n'); + if (p == NULL) { + error = EINVAL; + break; + } + *p++ = '\0'; + + if (strcmp(tag, "KERNEL") == 0) + error = setpath(&inst_kernel, val); + else if (strcmp(tag, "MODULES") == 0) + error = setmultipath(&inst_modules, val); + else if (strcmp(tag, "ROOTFS") == 0) + error = setpath(&inst_rootfs, val); + else if (strcmp(tag, "LOADER_RC") == 0) + error = setpath(&inst_loader_rc, val); + + tag = p; + } + + return (error); +} + +static void +cleanup(void) +{ + u_int i; + + if (inst_kernel != NULL) { + free(inst_kernel); + inst_kernel = NULL; + } + if (inst_modules != NULL) { + i = 0; + while (inst_modules[i] != NULL) + free(inst_modules[i++]); + free(inst_modules); + inst_modules = NULL; + } + if (inst_rootfs != NULL) { + free(inst_rootfs); + inst_rootfs = NULL; + } + if (inst_loader_rc != NULL) { + free(inst_loader_rc); + inst_loader_rc = NULL; + } + pkgfs_cleanup(); +} + +/* + * usage: install URL + * where: URL = (tftp|file)://[host]/ + */ +static int +install(char *pkgname) +{ + static char buf[256]; + struct fs_ops *proto; + struct preloaded_file *fp; + char *s, *currdev; + const char *devname; + int error, fd, i, local; + + s = strstr(pkgname, "://"); + if (s == NULL) + goto invalid_url; + + i = s - pkgname; + if (i == 4 && !strncasecmp(pkgname, "tftp", i)) { + devname = "net0"; + proto = &tftp_fsops; + local = 0; + } else if (i == 4 && !strncasecmp(pkgname, "file", i)) { + currdev = getenv("currdev"); + if (currdev != NULL && strcmp(currdev, "pxe0:") == 0) { + devname = "pxe0"; + proto = NULL; + } else { + devname = "disk1"; + proto = &dosfs_fsops; + } + local = 1; + } else + goto invalid_url; + + s += 3; + if (*s == '\0') + goto invalid_url; + + if (*s != '/' ) { + if (local) + goto invalid_url; + + pkgname = strchr(s, '/'); + if (pkgname == NULL) + goto invalid_url; + + *pkgname = '\0'; + servip.s_addr = inet_addr(s); + if (servip.s_addr == htonl(INADDR_NONE)) + goto invalid_url; + + setenv("serverip", inet_ntoa(servip), 1); + + *pkgname = '/'; + } else + pkgname = s; + + if (strlen(devname) + strlen(pkgname) + 2 > sizeof(buf)) { + command_errmsg = "package name too long"; + return (CMD_ERROR); + } + sprintf(buf, "%s:%s", devname, pkgname); + setenv("install_package", buf, 1); + + error = pkgfs_init(buf, proto); + if (error) { + command_errmsg = "cannot open package"; + goto fail; + } + + /* + * Point of no return: unload anything that may have been + * loaded and prune the environment from harmful variables. + */ + unload(); + unsetenv("vfs.root.mountfrom"); + + /* + * read the metatags file. + */ + fd = open("/metatags", O_RDONLY); + if (fd != -1) { + error = read_metatags(fd); + close(fd); + if (error) { + command_errmsg = "cannot load metatags"; + goto fail; + } + } + + s = (inst_kernel == NULL) ? "/kernel" : inst_kernel; + error = mod_loadkld(s, 0, NULL); + if (error) { + command_errmsg = "cannot load kernel from package"; + goto fail; + } + + /* If there is a loader.rc in the package, execute it */ + s = (inst_loader_rc == NULL) ? "/loader.rc" : inst_loader_rc; + fd = open(s, O_RDONLY); + if (fd != -1) { + close(fd); + error = include(s); + if (error == CMD_ERROR) + goto fail; + } + + i = 0; + while (inst_modules != NULL && inst_modules[i] != NULL) { + error = mod_loadkld(inst_modules[i], 0, NULL); + if (error) { + command_errmsg = "cannot load module(s) from package"; + goto fail; + } + i++; + } + + s = (inst_rootfs == NULL) ? "/install.iso" : inst_rootfs; + if (file_loadraw(s, "mfs_root", 1) == NULL) { + error = errno; + command_errmsg = "cannot load root file system"; + goto fail; + } + + cleanup(); + + fp = file_findfile(NULL, NULL); + if (fp != NULL) + file_formats[fp->f_loader]->l_exec(fp); + error = CMD_ERROR; + command_errmsg = "unable to start installation"; + + fail: + sprintf(buf, "%s (error %d)", command_errmsg, error); + cleanup(); + unload(); + exclusive_file_system = NULL; + command_errmsg = buf; /* buf is static. */ + return (CMD_ERROR); + + invalid_url: + command_errmsg = "invalid URL"; + return (CMD_ERROR); +} + +static int +command_install(int argc, char *argv[]) +{ + int argidx; + + unsetenv("install_format"); + + argidx = 1; + while (1) { + if (argc == argidx) { + command_errmsg = + "usage: install [--format] "; + return (CMD_ERROR); + } + if (!strcmp(argv[argidx], "--format")) { + setenv("install_format", "yes", 1); + argidx++; + continue; + } + break; + } + + return (install(argv[argidx])); +} diff --git a/stand/common/interp.c b/stand/common/interp.c new file mode 100644 index 0000000..f4117b9 --- /dev/null +++ b/stand/common/interp.c @@ -0,0 +1,371 @@ +/*- + * Copyright (c) 1998 Michael Smith + * 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 +__FBSDID("$FreeBSD$"); + +/* + * Simple commandline interpreter, toplevel and misc. + * + * XXX may be obsoleted by BootFORTH or some other, better, interpreter. + */ + +#include +#include +#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(const char *rc) +{ + static char input[256]; /* big enough? */ +#ifndef BOOT_FORTH + int argc; + char **argv; +#endif + +#ifdef BOOT_FORTH + bf_init((rc) ? "" : NULL); +#endif + + if (rc == NULL) { + /* Read our default configuration. */ + include("/boot/loader.rc"); + } else if (*rc != '\0') + include(rc); + + 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); +} + +/* + * Header prepended to each line. The text immediately follows the header. + * We try to make this short in order to save memory -- the loader has + * limited memory available, and some of the forth files are very long. + */ +struct includeline +{ + struct includeline *next; +#ifndef BOOT_FORTH + int flags; + int line; +#define SL_QUIET (1<<0) +#define SL_IGNOREERR (1<<1) +#endif + char text[0]; +}; + +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)) { + snprintf(command_errbuf, sizeof(command_errbuf), + "can't open '%s': %s", 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 */ + if (*cp == '\0') + continue; /* ignore empty line, save memory */ + sp = malloc(sizeof(struct includeline) + strlen(cp) + 1); + /* On malloc failure (it happens!), free as much as possible and exit */ + if (sp == NULL) { + while (script != NULL) { + se = script; + script = script->next; + free(se); + } + snprintf(command_errbuf, sizeof(command_errbuf), + "file '%s' line %d: memory allocation failure - aborting", + filename, line); + return (CMD_ERROR); + } + strcpy(sp->text, cp); +#ifndef BOOT_FORTH + sp->flags = flags; + sp->line = line; +#endif + 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) { + snprintf(command_errbuf, sizeof(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/stand/common/interp_backslash.c b/stand/common/interp_backslash.c new file mode 100644 index 0000000..09b8f57 --- /dev/null +++ b/stand/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 +__FBSDID("$FreeBSD$"); + +#include +#include +#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/stand/common/interp_forth.c b/stand/common/interp_forth.c new file mode 100644 index 0000000..a3b7776 --- /dev/null +++ b/stand/common/interp_forth.c @@ -0,0 +1,332 @@ +/*- + * Copyright (c) 1998 Michael Smith + * 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 +__FBSDID("$FreeBSD$"); + +#include /* to pick up __FreeBSD_version */ +#include +#include +#include "bootstrap.h" +#include "ficl.h" + +extern unsigned 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 + +/* + * FreeBSD loader default dictionary cells + */ +#ifndef BF_DICTSIZE +#define BF_DICTSIZE 10000 +#endif + +/* + * BootForth Interface to Ficl Forth interpreter. + */ + +FICL_SYSTEM *bf_sys; +FICL_VM *bf_vm; + +/* + * 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; + } + + switch (result) { + case CMD_CRIT: + printf("%s\n", command_errmsg); + break; + case CMD_FATAL: + panic("%s\n", command_errmsg); + } + + free(line); + /* + * If there was error during nested ficlExec(), we may no longer have + * valid environment to return. Throw all exceptions from here. + */ + if (result != CMD_OK) + vmThrow(vm, result); + + /* 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(const char *rc) +{ + struct bootblk_command **cmdp; + char create_buf[41]; /* 31 characters-long builtins */ + int fd; + + bf_sys = ficlInitSystem(BF_DICTSIZE); + 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); + + /* try to load and run init file if present */ + if (rc == NULL) + rc = "/boot/boot.4th"; + if (*rc != '\0') { + fd = open(rc, O_RDONLY); + if (fd != -1) { + (void)ficlExecFD(bf_vm, fd); + close(fd); + } + } +} + +/* + * 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: + if (command_errmsg != NULL) { + printf("%s\n", command_errmsg); + command_errmsg = NULL; + } + } + + if (result == VM_USEREXIT) + panic("interpreter exit"); + setenv("interpret", bf_vm->state ? "" : "OK", 1); + + return (result); +} diff --git a/stand/common/interp_parse.c b/stand/common/interp_parse.c new file mode 100644 index 0000000..8d8a2e2 --- /dev/null +++ b/stand/common/interp_parse.c @@ -0,0 +1,222 @@ +/*- + * 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 +__FBSDID("$FreeBSD$"); + +#include +#include +#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 == '\''); +} + +static int +isdquote(int ch) +{ + return (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, dquote, *buf; + enum { STR, VAR, WHITE } state; + + ac = *argc = 0; + dquote = 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; + if (dquote) { /* keep quote */ + PARSE_FAIL(i == (PARSE_BUFSIZE - 1)); + buf[i++] = *p++; + } else + ++p; + } else if (isdquote(*p)) { + dquote = dquote ? 0 : *p; + if (quote) { /* keep dquote */ + PARSE_FAIL(i == (PARSE_BUFSIZE - 1)); + buf[i++] = *p++; + } else + ++p; + } else if (isspace(*p) && !quote && !dquote) { + state = WHITE; + if (i) { + buf[i] = '\0'; + PARSE_FAIL(insert(&ac, buf)); + i = 0; + } + ++p; + } else if (*p == '$' && !quote) { + 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 = strchr(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; + } + } + /* missing terminating ' or " */ + PARSE_FAIL(quote || dquote); + /* 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/stand/common/isapnp.c b/stand/common/isapnp.c new file mode 100644 index 0000000..5867600 --- /dev/null +++ b/stand/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 +__FBSDID("$FreeBSD$"); + +/* + * Machine-independant ISA PnP enumerator implementing a subset of the + * ISA PnP specification. + */ +#include +#include +#include +#include + +#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/stand/common/isapnp.h b/stand/common/isapnp.h new file mode 100644 index 0000000..0f9956c --- /dev/null +++ b/stand/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/stand/common/load_elf.c b/stand/common/load_elf.c new file mode 100644 index 0000000..679842b --- /dev/null +++ b/stand/common/load_elf.c @@ -0,0 +1,1038 @@ +/*- + * Copyright (c) 1998 Michael Smith + * Copyright (c) 1998 Peter Wemm + * 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 +__FBSDID("$FreeBSD$"); + +#include +#include +#include +#include +#include +#include +#include +#include +#define FREEBSD_ELF +#include + +#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_Rel *rel; + size_t relsz; + 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); +static int __elfN(reloc_ptr)(struct preloaded_file *mp, elf_file_t ef, + Elf_Addr p, void *val, size_t len); +static int __elfN(parse_modmetadata)(struct preloaded_file *mp, elf_file_t ef, + Elf_Addr p_start, Elf_Addr p_end); +static symaddr_fn __elfN(symaddr); +static char *fake_modname(const char *name); + +const char *__elfN(kerneltype) = "elf kernel"; +const char *__elfN(moduletype) = "elf module"; + +u_int64_t __elfN(relocation_offset) = 0; + +static int +__elfN(load_elf_header)(char *filename, elf_file_t ef) +{ + ssize_t bytes_read; + Elf_Ehdr *ehdr; + int err; + + /* + * 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 error; + } + ehdr = ef->ehdr = (Elf_Ehdr *)ef->firstpage; + + /* Is it ELF? */ + if (!IS_ELF(*ehdr)) { + err = EFTYPE; + goto error; + } + 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 error; + } + + return (0); + +error: + if (ef->firstpage != NULL) { + free(ef->firstpage); + ef->firstpage = NULL; + } + if (ef->fd != -1) { + close(ef->fd); + ef->fd = -1; + } + return (err); +} + +/* + * 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) +{ + return (__elfN(loadfile_raw)(filename, dest, result, 0)); +} + +int +__elfN(loadfile_raw)(char *filename, u_int64_t dest, + struct preloaded_file **result, int multiboot) +{ + struct preloaded_file *fp, *kfp; + struct elf_file ef; + Elf_Ehdr *ehdr; + int err; + + fp = NULL; + bzero(&ef, sizeof(struct elf_file)); + ef.fd = -1; + + err = __elfN(load_elf_header)(filename, &ef); + if (err != 0) + return (err); + + ehdr = ef.ehdr; + + /* + * Check to see what sort of module we are. + */ + kfp = file_findfile(NULL, __elfN(kerneltype)); +#ifdef __powerpc__ + /* + * Kernels can be ET_DYN, so just assume the first loaded object is the + * kernel. This assumption will be checked later. + */ + if (kfp == NULL) + ef.kernel = 1; +#endif + if (ef.kernel || 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. + * + * For ARM, the destination address is independent of any values in the + * elf header (an ARM kernel can be loaded at any 2MB boundary), so we + * leave dest set to the value calculated by archsw.arch_loadaddr() and + * passed in to this function. + */ +#ifndef __arm__ + if (ehdr->e_type == ET_EXEC) + dest = (ehdr->e_entry & ~PAGE_MASK); +#endif + if ((ehdr->e_entry & ~PAGE_MASK) == 0) { + printf("elf" __XSTRING(__ELF_WORD_SIZE) "_loadfile: not a kernel (maybe static binary?)\n"); + err = EPERM; + goto oerr; + } + ef.kernel = 1; + + } else if (ehdr->e_type == ET_DYN) { + /* Looks like a kld module */ + if (multiboot != 0) { + printf("elf" __XSTRING(__ELF_WORD_SIZE) "_loadfile: can't load module as multiboot\n"); + err = EPERM; + goto oerr; + } + 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; + + } else { + err = EFTYPE; + goto oerr; + } + + if (archsw.arch_loadaddr != NULL) + dest = archsw.arch_loadaddr(LOAD_ELF, ehdr, dest); + else + dest = roundup(dest, PAGE_SIZE); + + /* + * 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 == 1 && multiboot == 0) + setenv("kernelname", filename, 1); + fp->f_name = strdup(filename); + if (multiboot == 0) + fp->f_type = strdup(ef.kernel ? + __elfN(kerneltype) : __elfN(moduletype)); + else + fp->f_type = strdup("elf multiboot kernel"); + +#ifdef ELF_VERBOSE + if (ef.kernel) + printf("%s entry at 0x%jx\n", filename, (uintmax_t)ehdr->e_entry); +#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); + if (ef.fd != -1) + 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; + char *shstr; + int ret; + vm_offset_t firstaddr; + vm_offset_t lastaddr; + size_t chunk; + ssize_t result; + Elf_Addr ssym, esym; + Elf_Dyn *dp; + Elf_Addr adp; + Elf_Addr ctors; + int ndp; + int symstrindex; + int symtabindex; + Elf_Size size; + u_int fpcopy; + Elf_Sym sym; + Elf_Addr p_start, p_end; + + dp = NULL; + shdr = NULL; + ret = 0; + firstaddr = lastaddr = 0; + ehdr = ef->ehdr; + if (ehdr->e_type == ET_EXEC) { +#if defined(__i386__) || defined(__amd64__) +#if __ELF_WORD_SIZE == 64 + off = - (off & 0xffffffffff000000ull);/* x86_64 relocates after locore */ +#else + off = - (off & 0xff000000u); /* i386 relocates after locore */ +#endif +#elif defined(__powerpc__) + /* + * On the purely virtual memory machines like e500, the kernel is + * linked against its final VA range, which is most often not + * available at the loader stage, but only after kernel initializes + * and completes its VM settings. In such cases we cannot use p_vaddr + * field directly to load ELF segments, but put them at some + * 'load-time' locations. + */ + if (off & 0xf0000000u) { + off = -(off & 0xf0000000u); + /* + * XXX the physical load address should not be hardcoded. Note + * that the Book-E kernel assumes that it's loaded at a 16MB + * boundary for now... + */ + off += 0x01000000; + ehdr->e_entry += off; +#ifdef ELF_VERBOSE + printf("Converted entry 0x%08x\n", ehdr->e_entry); +#endif + } else + off = 0; +#elif defined(__arm__) && !defined(EFI) + /* + * The elf headers in arm kernels specify virtual addresses in all + * header fields, even the ones that should be physical addresses. + * We assume the entry point is in the first page, and masking the page + * offset will leave us with the virtual address the kernel was linked + * at. We subtract that from the load offset, making 'off' into the + * value which, when added to a virtual address in an elf header, + * translates it to a physical address. We do the va->pa conversion on + * the entry point address in the header now, so that later we can + * launch the kernel by just jumping to that address. + * + * When booting from UEFI the copyin and copyout functions handle + * adjusting the location relative to the first virtual address. + * Because of this there is no need to adjust the offset or entry + * point address as these will both be handled by the efi code. + */ + off -= ehdr->e_entry & ~PAGE_MASK; + ehdr->e_entry += off; +#ifdef ELF_VERBOSE + printf("ehdr->e_entry 0x%08x, va<->pa off %llx\n", ehdr->e_entry, off); +#endif +#else + off = 0; /* other archs use direct mapped kernels */ +#endif + } + ef->off = off; + + if (ef->kernel) + __elfN(relocation_offset) = 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 (kern_pread(ef->fd, phdr[i].p_vaddr + off + fpcopy, + phdr[i].p_filesz - fpcopy, phdr[i].p_offset + fpcopy) != 0) { + printf("\nelf" __XSTRING(__ELF_WORD_SIZE) + "_loadimage: read 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 + + kern_bzero(phdr[i].p_vaddr + off + phdr[i].p_filesz, + phdr[i].p_memsz - phdr[i].p_filesz); + } +#ifdef ELF_VERBOSE + printf("\n"); +#endif + + if (archsw.arch_loadseg != NULL) + archsw.arch_loadseg(ehdr, phdr + i, off); + + 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)); + + /* + * Get the section headers. We need this for finding the .ctors + * section as well as for loading any symbols. Both may be hard + * to do if reading from a .gz file as it involves seeking. I + * think the rule is going to have to be that you must strip a + * file to remove symbols before gzipping it. + */ + chunk = ehdr->e_shnum * ehdr->e_shentsize; + if (chunk == 0 || ehdr->e_shoff == 0) + goto nosyms; + shdr = alloc_pread(ef->fd, ehdr->e_shoff, chunk); + if (shdr == NULL) { + printf("\nelf" __XSTRING(__ELF_WORD_SIZE) + "_loadimage: failed to read section headers"); + goto nosyms; + } + file_addmetadata(fp, MODINFOMD_SHDR, chunk, shdr); + + /* + * Read the section string table and look for the .ctors section. + * We need to tell the kernel where it is so that it can call the + * ctors. + */ + chunk = shdr[ehdr->e_shstrndx].sh_size; + if (chunk) { + shstr = alloc_pread(ef->fd, shdr[ehdr->e_shstrndx].sh_offset, chunk); + if (shstr) { + for (i = 0; i < ehdr->e_shnum; i++) { + if (strcmp(shstr + shdr[i].sh_name, ".ctors") != 0) + continue; + ctors = shdr[i].sh_addr; + file_addmetadata(fp, MODINFOMD_CTORS_ADDR, sizeof(ctors), + &ctors); + size = shdr[i].sh_size; + file_addmetadata(fp, MODINFOMD_CTORS_SIZE, sizeof(size), + &size); + break; + } + free(shstr); + } + } + + /* + * Now load any symbols. + */ + 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%jx@0x%jx -> 0x%jx-0x%jx", secname, + (uintmax_t)shdr[i].sh_size, (uintmax_t)shdr[i].sh_offset, + (uintmax_t)lastaddr, (uintmax_t)(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! (%ju != %ju)", (uintmax_t)result, + (uintmax_t)shdr[i].sh_size); + 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 == 0) + 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_REL: + ef->rel = (Elf_Rel *)(uintptr_t)(dp[i].d_un.d_ptr + off); + break; + case DT_RELSZ: + ef->relsz = dp[i].d_un.d_val; + 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(lookup_symbol)(fp, ef, "__start_set_modmetadata_set", &sym) != 0) + return 0; + p_start = sym.st_value + ef->off; + if (__elfN(lookup_symbol)(fp, ef, "__stop_set_modmetadata_set", &sym) != 0) + return ENOENT; + p_end = sym.st_value + ef->off; + + if (__elfN(parse_modmetadata)(fp, ef, p_start, p_end) == 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__) || defined(__powerpc__)) && __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 +#if defined(__amd64__) && __ELF_WORD_SIZE == 32 +struct mod_metadata32 { + int md_version; /* structure version MDTV_* */ + int md_type; /* type of entry MDT_* */ + u_int32_t md_data; /* specific data */ + u_int32_t md_cval; /* common string label */ +}; +#endif + +int +__elfN(load_modmetadata)(struct preloaded_file *fp, u_int64_t dest) +{ + struct elf_file ef; + int err, i, j; + Elf_Shdr *sh_meta, *shdr = NULL; + Elf_Shdr *sh_data[2]; + char *shstrtab = NULL; + size_t size; + Elf_Addr p_start, p_end; + + bzero(&ef, sizeof(struct elf_file)); + ef.fd = -1; + + err = __elfN(load_elf_header)(fp->f_name, &ef); + if (err != 0) + goto out; + + if (ef.kernel == 1 || ef.ehdr->e_type == ET_EXEC) { + ef.kernel = 1; + } else if (ef.ehdr->e_type != ET_DYN) { + err = EFTYPE; + goto out; + } + + size = ef.ehdr->e_shnum * ef.ehdr->e_shentsize; + shdr = alloc_pread(ef.fd, ef.ehdr->e_shoff, size); + if (shdr == NULL) { + err = ENOMEM; + goto out; + } + + /* Load shstrtab. */ + shstrtab = alloc_pread(ef.fd, shdr[ef.ehdr->e_shstrndx].sh_offset, + shdr[ef.ehdr->e_shstrndx].sh_size); + if (shstrtab == NULL) { + printf("\nelf" __XSTRING(__ELF_WORD_SIZE) + "load_modmetadata: unable to load shstrtab\n"); + err = EFTYPE; + goto out; + } + + /* Find set_modmetadata_set and data sections. */ + sh_data[0] = sh_data[1] = sh_meta = NULL; + for (i = 0, j = 0; i < ef.ehdr->e_shnum; i++) { + if (strcmp(&shstrtab[shdr[i].sh_name], + "set_modmetadata_set") == 0) { + sh_meta = &shdr[i]; + } + if ((strcmp(&shstrtab[shdr[i].sh_name], ".data") == 0) || + (strcmp(&shstrtab[shdr[i].sh_name], ".rodata") == 0)) { + sh_data[j++] = &shdr[i]; + } + } + if (sh_meta == NULL || sh_data[0] == NULL || sh_data[1] == NULL) { + printf("\nelf" __XSTRING(__ELF_WORD_SIZE) + "load_modmetadata: unable to find set_modmetadata_set or data sections\n"); + err = EFTYPE; + goto out; + } + + /* Load set_modmetadata_set into memory */ + err = kern_pread(ef.fd, dest, sh_meta->sh_size, sh_meta->sh_offset); + if (err != 0) { + printf("\nelf" __XSTRING(__ELF_WORD_SIZE) + "load_modmetadata: unable to load set_modmetadata_set: %d\n", err); + goto out; + } + p_start = dest; + p_end = dest + sh_meta->sh_size; + dest += sh_meta->sh_size; + + /* Load data sections into memory. */ + err = kern_pread(ef.fd, dest, sh_data[0]->sh_size, + sh_data[0]->sh_offset); + if (err != 0) { + printf("\nelf" __XSTRING(__ELF_WORD_SIZE) + "load_modmetadata: unable to load data: %d\n", err); + goto out; + } + + /* + * We have to increment the dest, so that the offset is the same into + * both the .rodata and .data sections. + */ + ef.off = -(sh_data[0]->sh_addr - dest); + dest += (sh_data[1]->sh_addr - sh_data[0]->sh_addr); + + err = kern_pread(ef.fd, dest, sh_data[1]->sh_size, + sh_data[1]->sh_offset); + if (err != 0) { + printf("\nelf" __XSTRING(__ELF_WORD_SIZE) + "load_modmetadata: unable to load data: %d\n", err); + goto out; + } + + err = __elfN(parse_modmetadata)(fp, &ef, p_start, p_end); + if (err != 0) { + printf("\nelf" __XSTRING(__ELF_WORD_SIZE) + "load_modmetadata: unable to parse metadata: %d\n", err); + goto out; + } + +out: + if (shstrtab != NULL) + free(shstrtab); + if (shdr != NULL) + free(shdr); + if (ef.firstpage != NULL) + free(ef.firstpage); + if (ef.fd != -1) + close(ef.fd); + return (err); +} + +int +__elfN(parse_modmetadata)(struct preloaded_file *fp, elf_file_t ef, + Elf_Addr p_start, Elf_Addr p_end) +{ + struct mod_metadata md; +#if (defined(__i386__) || defined(__powerpc__)) && __ELF_WORD_SIZE == 64 + struct mod_metadata64 md64; +#elif defined(__amd64__) && __ELF_WORD_SIZE == 32 + struct mod_metadata32 md32; +#endif + struct mod_depend *mdepend; + struct mod_version mver; + char *s; + int error, modcnt, minfolen; + Elf_Addr v, p; + + modcnt = 0; + p = p_start; + while (p < p_end) { + COPYOUT(p, &v, sizeof(v)); + error = __elfN(reloc_ptr)(fp, ef, p, &v, sizeof(v)); + if (error == EOPNOTSUPP) + v += ef->off; + else if (error != 0) + return (error); +#if (defined(__i386__) || defined(__powerpc__)) && __ELF_WORD_SIZE == 64 + COPYOUT(v, &md64, sizeof(md64)); + error = __elfN(reloc_ptr)(fp, ef, v, &md64, sizeof(md64)); + if (error == EOPNOTSUPP) { + md64.md_cval += ef->off; + md64.md_data += ef->off; + } else if (error != 0) + return (error); + md.md_version = md64.md_version; + md.md_type = md64.md_type; + md.md_cval = (const char *)(uintptr_t)md64.md_cval; + md.md_data = (void *)(uintptr_t)md64.md_data; +#elif defined(__amd64__) && __ELF_WORD_SIZE == 32 + COPYOUT(v, &md32, sizeof(md32)); + error = __elfN(reloc_ptr)(fp, ef, v, &md32, sizeof(md32)); + if (error == EOPNOTSUPP) { + md32.md_cval += ef->off; + md32.md_data += ef->off; + } else if (error != 0) + return (error); + md.md_version = md32.md_version; + md.md_type = md32.md_type; + md.md_cval = (const char *)(uintptr_t)md32.md_cval; + md.md_data = (void *)(uintptr_t)md32.md_data; +#else + COPYOUT(v, &md, sizeof(md)); + error = __elfN(reloc_ptr)(fp, ef, v, &md, sizeof(md)); + if (error == EOPNOTSUPP) { + md.md_cval += ef->off; + md.md_data = (void *)((uintptr_t)md.md_data + (uintptr_t)ef->off); + } else if (error != 0) + return (error); +#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; +} + +/* + * 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. + * + * Returns EOPNOTSUPP if no relocation method is supplied. + */ +static int +__elfN(reloc_ptr)(struct preloaded_file *mp, elf_file_t ef, + Elf_Addr p, void *val, size_t len) +{ + size_t n; + Elf_Rela a; + Elf_Rel r; + int error; + + /* + * The kernel is already relocated, but we still want to apply + * offset adjustments. + */ + if (ef->kernel) + return (EOPNOTSUPP); + + for (n = 0; n < ef->relsz / sizeof(r); n++) { + COPYOUT(ef->rel + n, &r, sizeof(r)); + + error = __elfN(reloc)(ef, __elfN(symaddr), &r, ELF_RELOC_REL, + ef->off, p, val, len); + if (error != 0) + return (error); + } + for (n = 0; n < ef->relasz / sizeof(a); n++) { + COPYOUT(ef->rela + n, &a, sizeof(a)); + + error = __elfN(reloc)(ef, __elfN(symaddr), &a, ELF_RELOC_RELA, + ef->off, p, val, len); + if (error != 0) + return (error); + } + + return (0); +} + +static Elf_Addr +__elfN(symaddr)(struct elf_file *ef, Elf_Size symidx) +{ + + /* Symbol lookup by index not required here. */ + return (0); +} diff --git a/stand/common/load_elf32.c b/stand/common/load_elf32.c new file mode 100644 index 0000000..0c9f460 --- /dev/null +++ b/stand/common/load_elf32.c @@ -0,0 +1,7 @@ +#include +__FBSDID("$FreeBSD$"); + +#define __ELF_WORD_SIZE 32 +#define _MACHINE_ELF_WANT_32BIT + +#include "load_elf.c" diff --git a/stand/common/load_elf32_obj.c b/stand/common/load_elf32_obj.c new file mode 100644 index 0000000..94b0896 --- /dev/null +++ b/stand/common/load_elf32_obj.c @@ -0,0 +1,7 @@ +#include +__FBSDID("$FreeBSD$"); + +#define __ELF_WORD_SIZE 32 +#define _MACHINE_ELF_WANT_32BIT + +#include "load_elf_obj.c" diff --git a/stand/common/load_elf64.c b/stand/common/load_elf64.c new file mode 100644 index 0000000..c29e8e3 --- /dev/null +++ b/stand/common/load_elf64.c @@ -0,0 +1,6 @@ +#include +__FBSDID("$FreeBSD$"); + +#define __ELF_WORD_SIZE 64 + +#include "load_elf.c" diff --git a/stand/common/load_elf64_obj.c b/stand/common/load_elf64_obj.c new file mode 100644 index 0000000..3c9371b --- /dev/null +++ b/stand/common/load_elf64_obj.c @@ -0,0 +1,6 @@ +#include +__FBSDID("$FreeBSD$"); + +#define __ELF_WORD_SIZE 64 + +#include "load_elf_obj.c" diff --git a/stand/common/load_elf_obj.c b/stand/common/load_elf_obj.c new file mode 100644 index 0000000..a32b9fd --- /dev/null +++ b/stand/common/load_elf_obj.c @@ -0,0 +1,537 @@ +/*- + * Copyright (c) 2004 Ian Dowse + * Copyright (c) 1998 Michael Smith + * Copyright (c) 1998 Peter Wemm + * 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 +__FBSDID("$FreeBSD$"); + +#include +#include +#include +#include +#include +#include +#include +#include +#define FREEBSD_ELF +#include + +#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_Ehdr hdr; + Elf_Shdr *e_shdr; + + int symtabindex; /* Index of symbol table */ + int shstrindex; /* Index of section name string table */ + + int fd; + vm_offset_t off; +} *elf_file_t; + +static int __elfN(obj_loadimage)(struct preloaded_file *mp, elf_file_t ef, + u_int64_t loadaddr); +static int __elfN(obj_lookup_set)(struct preloaded_file *mp, elf_file_t ef, + const char *name, Elf_Addr *startp, Elf_Addr *stopp, int *countp); +static int __elfN(obj_reloc_ptr)(struct preloaded_file *mp, elf_file_t ef, + Elf_Addr p, void *val, size_t len); +static int __elfN(obj_parse_modmetadata)(struct preloaded_file *mp, + elf_file_t ef); +static Elf_Addr __elfN(obj_symaddr)(struct elf_file *ef, Elf_Size symidx); + +const char *__elfN(obj_kerneltype) = "elf kernel"; +const char *__elfN(obj_moduletype) = "elf obj 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(obj_loadfile)(char *filename, u_int64_t dest, + struct preloaded_file **result) +{ + struct preloaded_file *fp, *kfp; + struct elf_file ef; + Elf_Ehdr *hdr; + int err; + 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); + + hdr = &ef.hdr; + bytes_read = read(ef.fd, hdr, sizeof(*hdr)); + if (bytes_read != sizeof(*hdr)) { + err = EFTYPE; /* could be EIO, but may be small file */ + goto oerr; + } + + /* Is it ELF? */ + if (!IS_ELF(*hdr)) { + err = EFTYPE; + goto oerr; + } + if (hdr->e_ident[EI_CLASS] != ELF_TARG_CLASS || /* Layout ? */ + hdr->e_ident[EI_DATA] != ELF_TARG_DATA || + hdr->e_ident[EI_VERSION] != EV_CURRENT || /* Version ? */ + hdr->e_version != EV_CURRENT || + hdr->e_machine != ELF_TARG_MACH || /* Machine ? */ + hdr->e_type != ET_REL) { + err = EFTYPE; + goto oerr; + } + + if (hdr->e_shnum * hdr->e_shentsize == 0 || hdr->e_shoff == 0 || + hdr->e_shentsize != sizeof(Elf_Shdr)) { + err = EFTYPE; + goto oerr; + } + + kfp = file_findfile(NULL, __elfN(obj_kerneltype)); + if (kfp == NULL) { + printf("elf" __XSTRING(__ELF_WORD_SIZE) + "_obj_loadfile: can't load module before kernel\n"); + err = EPERM; + goto oerr; + } + + if (archsw.arch_loadaddr != NULL) + dest = archsw.arch_loadaddr(LOAD_ELF, hdr, dest); + else + dest = roundup(dest, PAGE_SIZE); + + /* + * Ok, we think we should handle this. + */ + fp = file_alloc(); + if (fp == NULL) { + printf("elf" __XSTRING(__ELF_WORD_SIZE) + "_obj_loadfile: cannot allocate module info\n"); + err = EPERM; + goto out; + } + fp->f_name = strdup(filename); + fp->f_type = strdup(__elfN(obj_moduletype)); + + printf("%s ", filename); + + fp->f_size = __elfN(obj_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(*hdr), hdr); + + /* Load OK, return module pointer */ + *result = (struct preloaded_file *)fp; + err = 0; + goto out; + +ioerr: + err = EIO; +oerr: + file_discard(fp); +out: + close(ef.fd); + if (ef.e_shdr != NULL) + free(ef.e_shdr); + + return(err); +} + +/* + * With the file (fd) open on the image, and (ehdr) containing + * the Elf header, load the image at (off) + */ +static int +__elfN(obj_loadimage)(struct preloaded_file *fp, elf_file_t ef, u_int64_t off) +{ + Elf_Ehdr *hdr; + Elf_Shdr *shdr, *cshdr, *lshdr; + vm_offset_t firstaddr, lastaddr; + int i, nsym, res, ret, shdrbytes, symstrindex; + + ret = 0; + firstaddr = lastaddr = (vm_offset_t)off; + hdr = &ef->hdr; + ef->off = (vm_offset_t)off; + + /* Read in the section headers. */ + shdrbytes = hdr->e_shnum * hdr->e_shentsize; + shdr = alloc_pread(ef->fd, (off_t)hdr->e_shoff, shdrbytes); + if (shdr == NULL) { + printf("\nelf" __XSTRING(__ELF_WORD_SIZE) + "_obj_loadimage: read section headers failed\n"); + goto out; + } + ef->e_shdr = shdr; + + /* + * Decide where to load everything, but don't read it yet. + * We store the load address as a non-zero sh_addr value. + * Start with the code/data and bss. + */ + for (i = 0; i < hdr->e_shnum; i++) + shdr[i].sh_addr = 0; + for (i = 0; i < hdr->e_shnum; i++) { + if (shdr[i].sh_size == 0) + continue; + switch (shdr[i].sh_type) { + case SHT_PROGBITS: + case SHT_NOBITS: +#if defined(__i386__) || defined(__amd64__) + case SHT_X86_64_UNWIND: +#endif + lastaddr = roundup(lastaddr, shdr[i].sh_addralign); + shdr[i].sh_addr = (Elf_Addr)lastaddr; + lastaddr += shdr[i].sh_size; + break; + } + } + + /* Symbols. */ + nsym = 0; + for (i = 0; i < hdr->e_shnum; i++) { + switch (shdr[i].sh_type) { + case SHT_SYMTAB: + nsym++; + ef->symtabindex = i; + shdr[i].sh_addr = (Elf_Addr)lastaddr; + lastaddr += shdr[i].sh_size; + break; + } + } + if (nsym != 1) { + printf("\nelf" __XSTRING(__ELF_WORD_SIZE) + "_obj_loadimage: file has no valid symbol table\n"); + goto out; + } + lastaddr = roundup(lastaddr, shdr[ef->symtabindex].sh_addralign); + shdr[ef->symtabindex].sh_addr = (Elf_Addr)lastaddr; + lastaddr += shdr[ef->symtabindex].sh_size; + + symstrindex = shdr[ef->symtabindex].sh_link; + if (symstrindex < 0 || symstrindex >= hdr->e_shnum || + shdr[symstrindex].sh_type != SHT_STRTAB) { + printf("\nelf" __XSTRING(__ELF_WORD_SIZE) + "_obj_loadimage: file has invalid symbol strings\n"); + goto out; + } + lastaddr = roundup(lastaddr, shdr[symstrindex].sh_addralign); + shdr[symstrindex].sh_addr = (Elf_Addr)lastaddr; + lastaddr += shdr[symstrindex].sh_size; + + /* Section names. */ + if (hdr->e_shstrndx == 0 || hdr->e_shstrndx >= hdr->e_shnum || + shdr[hdr->e_shstrndx].sh_type != SHT_STRTAB) { + printf("\nelf" __XSTRING(__ELF_WORD_SIZE) + "_obj_loadimage: file has no section names\n"); + goto out; + } + ef->shstrindex = hdr->e_shstrndx; + lastaddr = roundup(lastaddr, shdr[ef->shstrindex].sh_addralign); + shdr[ef->shstrindex].sh_addr = (Elf_Addr)lastaddr; + lastaddr += shdr[ef->shstrindex].sh_size; + + /* Relocation tables. */ + for (i = 0; i < hdr->e_shnum; i++) { + switch (shdr[i].sh_type) { + case SHT_REL: + case SHT_RELA: + lastaddr = roundup(lastaddr, shdr[i].sh_addralign); + shdr[i].sh_addr = (Elf_Addr)lastaddr; + lastaddr += shdr[i].sh_size; + break; + } + } + + /* Clear the whole area, including bss regions. */ + kern_bzero(firstaddr, lastaddr - firstaddr); + + /* Figure section with the lowest file offset we haven't loaded yet. */ + for (cshdr = NULL; /* none */; /* none */) + { + /* + * Find next section to load. The complexity of this loop is + * O(n^2), but with the number of sections being typically + * small, we do not care. + */ + lshdr = cshdr; + + for (i = 0; i < hdr->e_shnum; i++) { + if (shdr[i].sh_addr == 0 || + shdr[i].sh_type == SHT_NOBITS) + continue; + /* Skip sections that were loaded already. */ + if (lshdr != NULL && + lshdr->sh_offset >= shdr[i].sh_offset) + continue; + /* Find section with smallest offset. */ + if (cshdr == lshdr || + cshdr->sh_offset > shdr[i].sh_offset) + cshdr = &shdr[i]; + } + + if (cshdr == lshdr) + break; + + if (kern_pread(ef->fd, (vm_offset_t)cshdr->sh_addr, + cshdr->sh_size, (off_t)cshdr->sh_offset) != 0) { + printf("\nelf" __XSTRING(__ELF_WORD_SIZE) + "_obj_loadimage: read failed\n"); + goto out; + } + } + + file_addmetadata(fp, MODINFOMD_SHDR, shdrbytes, shdr); + + res = __elfN(obj_parse_modmetadata)(fp, ef); + if (res != 0) + goto out; + + ret = lastaddr - firstaddr; + fp->f_addr = firstaddr; + + printf("size 0x%lx at 0x%lx", (u_long)ret, (u_long)firstaddr); + +out: + printf("\n"); + return ret; +} + +#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(obj_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; + char *s; + int error, modcnt, minfolen; + Elf_Addr v, p, p_stop; + + if (__elfN(obj_lookup_set)(fp, ef, "modmetadata_set", &p, &p_stop, + &modcnt) != 0) + return 0; + + modcnt = 0; + while (p < p_stop) { + COPYOUT(p, &v, sizeof(v)); + error = __elfN(obj_reloc_ptr)(fp, ef, p, &v, sizeof(v)); + if (error != 0) + return (error); +#if defined(__i386__) && __ELF_WORD_SIZE == 64 + COPYOUT(v, &md64, sizeof(md64)); + error = __elfN(obj_reloc_ptr)(fp, ef, v, &md64, sizeof(md64)); + if (error != 0) + return (error); + md.md_version = md64.md_version; + md.md_type = md64.md_type; + md.md_cval = (const char *)(uintptr_t)md64.md_cval; + md.md_data = (void *)(uintptr_t)md64.md_data; +#else + COPYOUT(v, &md, sizeof(md)); + error = __elfN(obj_reloc_ptr)(fp, ef, v, &md, sizeof(md)); + if (error != 0) + return (error); +#endif + p += sizeof(Elf_Addr); + switch(md.md_type) { + case MDT_DEPEND: + 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; + case MDT_MODULE: + case MDT_PNP_INFO: + break; + default: + printf("unknown type %d\n", md.md_type); + break; + } + } + return 0; +} + +static int +__elfN(obj_lookup_set)(struct preloaded_file *fp, elf_file_t ef, + const char* name, Elf_Addr *startp, Elf_Addr *stopp, int *countp) +{ + Elf_Ehdr *hdr; + Elf_Shdr *shdr; + char *p; + vm_offset_t shstrtab; + int i; + + hdr = &ef->hdr; + shdr = ef->e_shdr; + shstrtab = shdr[ef->shstrindex].sh_addr; + + for (i = 0; i < hdr->e_shnum; i++) { + if (shdr[i].sh_type != SHT_PROGBITS) + continue; + if (shdr[i].sh_name == 0) + continue; + p = strdupout(shstrtab + shdr[i].sh_name); + if (strncmp(p, "set_", 4) == 0 && strcmp(p + 4, name) == 0) { + *startp = shdr[i].sh_addr; + *stopp = shdr[i].sh_addr + shdr[i].sh_size; + *countp = (*stopp - *startp) / sizeof(Elf_Addr); + free(p); + return (0); + } + free(p); + } + + return (ESRCH); +} + +/* + * 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 int +__elfN(obj_reloc_ptr)(struct preloaded_file *mp, elf_file_t ef, Elf_Addr p, + void *val, size_t len) +{ + Elf_Ehdr *hdr; + Elf_Shdr *shdr; + Elf_Addr off = p; + Elf_Addr base; + Elf_Rela a, *abase; + Elf_Rel r, *rbase; + int error, i, j, nrel, nrela; + + hdr = &ef->hdr; + shdr = ef->e_shdr; + + for (i = 0; i < hdr->e_shnum; i++) { + if (shdr[i].sh_type != SHT_RELA && shdr[i].sh_type != SHT_REL) + continue; + base = shdr[shdr[i].sh_info].sh_addr; + if (base == 0 || shdr[i].sh_addr == 0) + continue; + if (off < base || off + len > base + + shdr[shdr[i].sh_info].sh_size) + continue; + + switch (shdr[i].sh_type) { + case SHT_RELA: + abase = (Elf_Rela *)(intptr_t)shdr[i].sh_addr; + + nrela = shdr[i].sh_size / sizeof(Elf_Rela); + for (j = 0; j < nrela; j++) { + COPYOUT(abase + j, &a, sizeof(a)); + + error = __elfN(reloc)(ef, __elfN(obj_symaddr), + &a, ELF_RELOC_RELA, base, off, val, len); + if (error != 0) + return (error); + } + break; + case SHT_REL: + rbase = (Elf_Rel *)(intptr_t)shdr[i].sh_addr; + + nrel = shdr[i].sh_size / sizeof(Elf_Rel); + for (j = 0; j < nrel; j++) { + COPYOUT(rbase + j, &r, sizeof(r)); + + error = __elfN(reloc)(ef, __elfN(obj_symaddr), + &r, ELF_RELOC_REL, base, off, val, len); + if (error != 0) + return (error); + } + break; + } + } + return (0); +} + +/* Look up the address of a specified symbol. */ +static Elf_Addr +__elfN(obj_symaddr)(struct elf_file *ef, Elf_Size symidx) +{ + Elf_Sym sym; + Elf_Addr base; + + if (symidx >= ef->e_shdr[ef->symtabindex].sh_size / sizeof(Elf_Sym)) + return (0); + COPYOUT(ef->e_shdr[ef->symtabindex].sh_addr + symidx * sizeof(Elf_Sym), + &sym, sizeof(sym)); + if (sym.st_shndx == SHN_UNDEF || sym.st_shndx >= ef->hdr.e_shnum) + return (0); + base = ef->e_shdr[sym.st_shndx].sh_addr; + if (base == 0) + return (0); + return (base + sym.st_value); +} diff --git a/stand/common/ls.c b/stand/common/ls.c new file mode 100644 index 0000000..cd6b7c4 --- /dev/null +++ b/stand/common/ls.c @@ -0,0 +1,212 @@ +/* + * $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 +__FBSDID("$FreeBSD$"); + +#include +#include +#include + +#include +#include + +#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]; + } + + if (stat(path, &sb) == 0 && !S_ISDIR(sb.st_mode)) { + if (verbose) { + printf(" %c %8d %s\n", + typestr[sb.st_mode >> 12], + (int)sb.st_size, path); + } else { + printf(" %c %s\n", + typestr[sb.st_mode >> 12], path); + } + return (CMD_OK); + } + + 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 (d->d_type == 0 || verbose) { + /* stat the file, if possible */ + sb.st_size = 0; + sb.st_mode = 0; + buf = malloc(strlen(path) + strlen(d->d_name) + 2); + if (buf != NULL) { + sprintf(buf, "%s/%s", path, d->d_name); + /* ignore return, could be symlink, etc. */ + if (stat(buf, &sb)) { + sb.st_size = 0; + sb.st_mode = 0; + } + free(buf); + } + } + if (verbose) { + snprintf(lbuf, sizeof(lbuf), " %c %8d %s\n", + typestr[d->d_type? d->d_type:sb.st_mode >> 12], + (int)sb.st_size, d->d_name); + } else { + snprintf(lbuf, sizeof(lbuf), " %c %s\n", + typestr[d->d_type? d->d_type:sb.st_mode >> 12], d->d_name); + } + if (pager_output(lbuf)) + goto out; + } + } + out: + pager_close(); + if (fd != -1) + close(fd); + free(path); /* ls_getdir() did allocate 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; + + fd = -1; + + /* one extra byte for a possible trailing slash required */ + path = malloc(strlen(*pathp) + 2); + if (path == NULL) { + snprintf(command_errbuf, sizeof (command_errbuf), + "out of memory"); + goto out; + } + strcpy(path, *pathp); + + /* Make sure the path is respectable to begin with */ + if (archsw.arch_getdev(NULL, path, &cp)) { + snprintf(command_errbuf, sizeof(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) { + snprintf(command_errbuf, sizeof(command_errbuf), + "open '%s' failed: %s", path, strerror(errno)); + goto out; + } + if (fstat(fd, &sb) < 0) { + snprintf(command_errbuf, sizeof(command_errbuf), + "stat failed: %s", strerror(errno)); + goto out; + } + if (!S_ISDIR(sb.st_mode)) { + snprintf(command_errbuf, sizeof(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/stand/common/md.c b/stand/common/md.c new file mode 100644 index 0000000..5585218 --- /dev/null +++ b/stand/common/md.c @@ -0,0 +1,157 @@ +/*- + * Copyright (c) 2009 Marcel Moolenaar + * 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 +__FBSDID("$FreeBSD$"); + +#include +#include +#include +#include +#include + +#include "bootstrap.h" + +#define MD_BLOCK_SIZE 512 + +#ifndef MD_IMAGE_SIZE +#error Must be compiled with MD_IMAGE_SIZE defined +#endif +#if (MD_IMAGE_SIZE == 0 || MD_IMAGE_SIZE % MD_BLOCK_SIZE) +#error Image size must be a multiple of 512. +#endif + +/* + * Preloaded image gets put here. + * Applications that patch the object with the image can determine + * the size looking at the start and end markers (strings), + * so we want them contiguous. + */ +static struct { + u_char start[MD_IMAGE_SIZE]; + u_char end[128]; +} md_image = { + .start = "MFS Filesystem goes here", + .end = "MFS Filesystem had better STOP here", +}; + +/* devsw I/F */ +static int md_init(void); +static int md_strategy(void *, int, daddr_t, size_t, char *, size_t *); +static int md_open(struct open_file *, ...); +static int md_close(struct open_file *); +static int md_print(int); + +struct devsw md_dev = { + "md", + DEVT_DISK, + md_init, + md_strategy, + md_open, + md_close, + noioctl, + md_print, + NULL +}; + +static int +md_init(void) +{ + + return (0); +} + +static int +md_strategy(void *devdata, int rw, daddr_t blk, size_t size, + char *buf, size_t *rsize) +{ + struct devdesc *dev = (struct devdesc *)devdata; + size_t ofs; + + if (dev->d_unit != 0) + return (ENXIO); + + if (blk < 0 || blk >= (MD_IMAGE_SIZE / MD_BLOCK_SIZE)) + return (EIO); + + if (size % MD_BLOCK_SIZE) + return (EIO); + + ofs = blk * MD_BLOCK_SIZE; + if ((ofs + size) > MD_IMAGE_SIZE) + size = MD_IMAGE_SIZE - ofs; + + if (rsize != NULL) + *rsize = size; + + switch (rw & F_MASK) { + case F_READ: + bcopy(md_image.start + ofs, buf, size); + return (0); + case F_WRITE: + bcopy(buf, md_image.start + ofs, size); + return (0); + } + + return (ENODEV); +} + +static int +md_open(struct open_file *f, ...) +{ + va_list ap; + struct devdesc *dev; + + va_start(ap, f); + dev = va_arg(ap, struct devdesc *); + va_end(ap); + + if (dev->d_unit != 0) + return (ENXIO); + + return (0); +} + +static int +md_close(struct open_file *f) +{ + struct devdesc *dev; + + dev = (struct devdesc *)(f->f_devdata); + return ((dev->d_unit != 0) ? ENXIO : 0); +} + +static int +md_print(int verbose) +{ + + printf("%s devices:", md_dev.dv_name); + if (pager_output("\n") != 0) + return (1); + + printf("MD (%u bytes)", MD_IMAGE_SIZE); + return (pager_output("\n")); +} diff --git a/stand/common/merge_help.awk b/stand/common/merge_help.awk new file mode 100644 index 0000000..1070f73 --- /dev/null +++ b/stand/common/merge_help.awk @@ -0,0 +1,104 @@ +#!/usr/bin/awk -f +# +# $FreeBSD$ +# +# Merge two boot loader help files for FreeBSD 3.0 +# Joe Abley + +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:]]+"); + SSTART = RSTART + S = (RLENGTH == -1) ? "" : substr($0, RSTART + 2, RLENGTH - 2); + match($0, " D[[:graph:]][[:print:]]*$"); + D = substr($0, RSTART + 2); + if (SSTART > RSTART) + S = ""; + + # 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/stand/common/misc.c b/stand/common/misc.c new file mode 100644 index 0000000..9b938af --- /dev/null +++ b/stand/common/misc.c @@ -0,0 +1,219 @@ +/*- + * Copyright (c) 1998 Michael Smith + * 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 +__FBSDID("$FreeBSD$"); + +#include +#include +#include + +/* + * 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 (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); +} + +/* Zero a region in kernel space. */ +void +kern_bzero(vm_offset_t dest, size_t len) +{ + char buf[256]; + size_t chunk, resid; + + bzero(buf, sizeof(buf)); + resid = len; + while (resid > 0) { + chunk = min(sizeof(buf), resid); + archsw.arch_copyin(buf, dest, chunk); + resid -= chunk; + dest += chunk; + } +} + +/* + * Read the specified part of a file to kernel space. Unlike regular + * pread, the file pointer is advanced to the end of the read data, + * and it just returns 0 if successful. + */ +int +kern_pread(int fd, vm_offset_t dest, size_t len, off_t off) +{ + + if (lseek(fd, off, SEEK_SET) == -1) { +#ifdef DEBUG + printf("\nlseek failed\n"); +#endif + return (-1); + } + if ((size_t)archsw.arch_readin(fd, dest, len) != len) { +#ifdef DEBUG + printf("\nreadin failed\n"); +#endif + return (-1); + } + return (0); +} + +/* + * Read the specified part of a file to a malloced buffer. The file + * pointer is advanced to the end of the read data. + */ +void * +alloc_pread(int fd, off_t off, size_t len) +{ + void *buf; + + buf = malloc(len); + if (buf == NULL) { +#ifdef DEBUG + printf("\nmalloc(%d) failed\n", (int)len); +#endif + return (NULL); + } + if (lseek(fd, off, SEEK_SET) == -1) { +#ifdef DEBUG + printf("\nlseek failed\n"); +#endif + free(buf); + return (NULL); + } + if ((size_t)read(fd, buf, len) != len) { +#ifdef DEBUG + printf("\nread failed\n"); +#endif + free(buf); + return (NULL); + } + return (buf); +} + +/* + * 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/stand/common/module.c b/stand/common/module.c new file mode 100644 index 0000000..d651ad1 --- /dev/null +++ b/stand/common/module.c @@ -0,0 +1,1095 @@ +/*- + * Copyright (c) 1998 Michael Smith + * 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 +__FBSDID("$FreeBSD$"); + +/* + * file/module function dispatcher, support, etc. + */ + +#include +#include +#include +#include +#include +#include +#include + +#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_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; + +#if defined(LOADER_FDT_SUPPORT) +static const char *default_searchpath = + "/boot/kernel;/boot/modules;/boot/dtb"; +#else +static const char *default_searchpath ="/boot/kernel;/boot/modules"; +#endif + +static STAILQ_HEAD(, moduledir) moduledir_list = STAILQ_HEAD_INITIALIZER(moduledir_list); + +struct preloaded_file *preloaded_files = NULL; + +static char *kld_ext_list[] = { + ".ko", + "", + ".debug", + NULL +}; + + +/* + * load an object, either a disk file or code module. + * + * To load a file, the syntax is: + * + * load -t + * + * code modules are loaded as: + * + * load + */ + +COMMAND_SET(load, "load", "load a kernel or module", command_load); + +static int +command_load(int argc, char *argv[]) +{ + struct preloaded_file *fp; + 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_CRIT); + } + 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_CRIT); + } + + fp = file_findfile(argv[1], typestr); + if (fp) { + snprintf(command_errbuf, sizeof(command_errbuf), + "warning: file '%s' already loaded", argv[1]); + return (CMD_WARN); + } + + if (file_loadraw(argv[1], typestr, 1) != NULL) + return (CMD_OK); + + /* Failing to load mfs_root is never going to end well! */ + if (strcmp("mfs_root", typestr) == 0) + return (CMD_FATAL); + + return (CMD_ERROR); + } + /* + * Do we have explicit KLD load ? + */ + if (dokld || file_havepath(argv[1])) { + error = mod_loadkld(argv[1], argc - 2, argv + 2); + if (error == EEXIST) { + snprintf(command_errbuf, sizeof(command_errbuf), + "warning: KLD '%s' already loaded", argv[1]); + return (CMD_WARN); + } + + return (error == 0 ? CMD_OK : CMD_CRIT); + } + /* + * Looks like a request for a module. + */ + error = mod_load(argv[1], NULL, argc - 2, argv + 2); + if (error == EEXIST) { + snprintf(command_errbuf, sizeof(command_errbuf), + "warning: module '%s' already loaded", argv[1]); + return (CMD_WARN); + } + + return (error == 0 ? CMD_OK : CMD_CRIT); +} + +#ifdef LOADER_GELI_SUPPORT +COMMAND_SET(load_geli, "load_geli", "load a geli key", command_load_geli); + +static int +command_load_geli(int argc, char *argv[]) +{ + char typestr[80]; + char *cp; + int ch, num; + + if (argc < 3) { + command_errmsg = "usage is [-n key#] "; + return(CMD_ERROR); + } + + num = 0; + optind = 1; + optreset = 1; + while ((ch = getopt(argc, argv, "n:")) != -1) { + switch(ch) { + case 'n': + num = strtol(optarg, &cp, 0); + if (cp == optarg) { + snprintf(command_errbuf, sizeof(command_errbuf), + "bad key index '%s'", optarg); + return(CMD_ERROR); + } + break; + case '?': + default: + /* getopt has already reported an error */ + return(CMD_OK); + } + } + argv += (optind - 1); + argc -= (optind - 1); + sprintf(typestr, "%s:geli_keyfile%d", argv[1], num); + return (file_loadraw(argv[2], typestr, 1) ? CMD_OK : CMD_ERROR); +} +#endif + +void +unload(void) +{ + struct preloaded_file *fp; + + while (preloaded_files != NULL) { + fp = preloaded_files; + preloaded_files = preloaded_files->f_next; + file_discard(fp); + } + loadaddr = 0; + unsetenv("kernelname"); +} + +COMMAND_SET(unload, "unload", "unload all modules", command_unload); + +static int +command_unload(int argc, char *argv[]) +{ + unload(); + 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, ret = 0; + + 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) { + snprintf(lbuf, sizeof(lbuf), " %p: ", (void *) fp->f_addr); + pager_output(lbuf); + pager_output(fp->f_name); + snprintf(lbuf, sizeof(lbuf), " (%s, 0x%lx)\n", fp->f_type, + (long)fp->f_size); + if (pager_output(lbuf)) + break; + if (fp->f_args != NULL) { + pager_output(" args: "); + pager_output(fp->f_args); + if (pager_output("\n")) + break; + } + if (fp->f_modules) { + pager_output(" modules: "); + for (mp = fp->f_modules; mp; mp = mp->m_next) { + snprintf(lbuf, sizeof(lbuf), "%s.%d ", mp->m_name, + mp->m_version); + pager_output(lbuf); + } + if (pager_output("\n")) + break; + } + if (verbose) { + /* XXX could add some formatting smarts here to display some better */ + for (md = fp->f_metadata; md != NULL; md = md->md_next) { + snprintf(lbuf, sizeof(lbuf), " 0x%04x, 0x%lx\n", + md->md_type, (long) md->md_size); + if (pager_output(lbuf)) + break; + } + } + if (ret) + break; + } + pager_close(); + return(CMD_OK); +} + +/* + * File level interface, functions file_* + */ +int +file_load(char *filename, vm_offset_t dest, struct preloaded_file **result) +{ + static int last_file_format = 0; + struct preloaded_file *fp; + int error; + int i; + + if (archsw.arch_loadaddr != NULL) + dest = archsw.arch_loadaddr(LOAD_RAW, filename, dest); + + error = EFTYPE; + for (i = last_file_format, fp = NULL; + file_formats[i] && fp == NULL; i++) { + error = (file_formats[i]->l_load)(filename, dest, &fp); + if (error == 0) { + fp->f_loader = last_file_format = i; /* remember the loader */ + *result = fp; + break; + } else if (last_file_format == i && i != 0) { + /* Restart from the beginning */ + i = -1; + last_file_format = 0; + fp = NULL; + continue; + } + if (error == EFTYPE) + continue; /* Unknown to this handler? */ + if (error) { + snprintf(command_errbuf, sizeof(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) { + snprintf(command_errbuf, sizeof(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 (fname) as (type), so just suck it in, + * no arguments or anything. + */ +struct preloaded_file * +file_loadraw(const char *fname, char *type, int insert) +{ + struct preloaded_file *fp; + char *name; + 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(NULL); + } + + /* locate the file on the load path */ + name = file_search(fname, NULL); + if (name == NULL) { + snprintf(command_errbuf, sizeof(command_errbuf), + "can't find '%s'", fname); + return(NULL); + } + + if ((fd = open(name, O_RDONLY)) < 0) { + snprintf(command_errbuf, sizeof(command_errbuf), + "can't open '%s': %s", name, strerror(errno)); + free(name); + return(NULL); + } + + if (archsw.arch_loadaddr != NULL) + loadaddr = archsw.arch_loadaddr(LOAD_RAW, name, loadaddr); + + printf("%s ", name); + + 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 */ + snprintf(command_errbuf, sizeof(command_errbuf), + "error reading '%s': %s", name, strerror(errno)); + free(name); + close(fd); + return(NULL); + } + laddr += got; + } + + printf("size=%#jx\n", (uintmax_t)(laddr - loadaddr)); + + /* Looks OK so far; create & populate control structure */ + fp = file_alloc(); + fp->f_name = strdup(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 */ + if (insert != 0) + file_insert_tail(fp); + close(fd); + return(fp); +} + +/* + * 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 + snprintf(command_errbuf, sizeof(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) { + snprintf(command_errbuf, sizeof(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) { + snprintf(command_errbuf, sizeof(command_errbuf), + "can't find '%s'", kldname); + return (ENOENT); + } + /* + * Check if KLD already loaded + */ + fp = file_findfile(filename, NULL); + if (fp) { + snprintf(command_errbuf, sizeof(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) { + snprintf(command_errbuf, sizeof(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(const char *name, const 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); +} + +/* + * Remove all metadata from the file. + */ +void +file_removemetadata(struct preloaded_file *fp) +{ + struct file_metadata *md, *next; + + for (md = fp->f_metadata; md != NULL; md = next) + { + next = md->md_next; + free(md); + } + fp->f_metadata = NULL; +} + +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) + roundup2((ptr) - (base), sizeof(int)) + +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 = (u_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, (const char *)cp, clen, NULL); + else if (best) + result = file_lookup(mdp->d_path, (const char *)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 < (ssize_t)(sizeof(version) + sizeof(int)) || + st.st_size > LINKER_HINTS_MAX || (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; + size_t 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/stand/common/newvers.sh b/stand/common/newvers.sh new file mode 100755 index 0000000..75efece --- /dev/null +++ b/stand/common/newvers.sh @@ -0,0 +1,60 @@ +#!/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. 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 + +tempfile=$(mktemp tmp.XXXXXX) || exit +trap "rm -f $tempfile" EXIT INT TERM + +include_metadata=true +while getopts r opt; do + case "$opt" in + r) + include_metadata= + ;; + esac +done +shift $((OPTIND - 1)) + +LC_ALL=C; export LC_ALL +u=${USER-root} h=${HOSTNAME-`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` + +bootprog_info="FreeBSD/${3} ${2}, Revision ${r}\\n" +if [ -n "${include_metadata}" ]; then + bootprog_info="$bootprog_info(${t} ${u}@${h})\\n" +fi + +echo "char bootprog_info[] = \"$bootprog_info\";" > $tempfile +echo "unsigned bootprog_rev = ${r%%.*}${r##*.};" >> $tempfile +mv $tempfile vers.c diff --git a/stand/common/part.c b/stand/common/part.c new file mode 100644 index 0000000..cdb1e00 --- /dev/null +++ b/stand/common/part.c @@ -0,0 +1,898 @@ +/*- + * Copyright (c) 2012 Andrey V. Elsukov + * 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 AUTHORS 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 AUTHORS 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 +__FBSDID("$FreeBSD$"); + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#ifdef PART_DEBUG +#define DEBUG(fmt, args...) printf("%s: " fmt "\n", __func__, ## args) +#else +#define DEBUG(fmt, args...) +#endif + +#ifdef LOADER_GPT_SUPPORT +#define MAXTBLSZ 64 +static const uuid_t gpt_uuid_unused = GPT_ENT_TYPE_UNUSED; +static const uuid_t gpt_uuid_ms_basic_data = GPT_ENT_TYPE_MS_BASIC_DATA; +static const uuid_t gpt_uuid_freebsd_ufs = GPT_ENT_TYPE_FREEBSD_UFS; +static const uuid_t gpt_uuid_efi = GPT_ENT_TYPE_EFI; +static const uuid_t gpt_uuid_freebsd = GPT_ENT_TYPE_FREEBSD; +static const uuid_t gpt_uuid_freebsd_boot = GPT_ENT_TYPE_FREEBSD_BOOT; +static const uuid_t gpt_uuid_freebsd_nandfs = GPT_ENT_TYPE_FREEBSD_NANDFS; +static const uuid_t gpt_uuid_freebsd_swap = GPT_ENT_TYPE_FREEBSD_SWAP; +static const uuid_t gpt_uuid_freebsd_zfs = GPT_ENT_TYPE_FREEBSD_ZFS; +static const uuid_t gpt_uuid_freebsd_vinum = GPT_ENT_TYPE_FREEBSD_VINUM; +#endif + +struct pentry { + struct ptable_entry part; + uint64_t flags; + union { + uint8_t bsd; + uint8_t mbr; + uuid_t gpt; + uint16_t vtoc8; + } type; + STAILQ_ENTRY(pentry) entry; +}; + +struct ptable { + enum ptable_type type; + uint16_t sectorsize; + uint64_t sectors; + + STAILQ_HEAD(, pentry) entries; +}; + +static struct parttypes { + enum partition_type type; + const char *desc; +} ptypes[] = { + { PART_UNKNOWN, "Unknown" }, + { PART_EFI, "EFI" }, + { PART_FREEBSD, "FreeBSD" }, + { PART_FREEBSD_BOOT, "FreeBSD boot" }, + { PART_FREEBSD_NANDFS, "FreeBSD nandfs" }, + { PART_FREEBSD_UFS, "FreeBSD UFS" }, + { PART_FREEBSD_ZFS, "FreeBSD ZFS" }, + { PART_FREEBSD_SWAP, "FreeBSD swap" }, + { PART_FREEBSD_VINUM, "FreeBSD vinum" }, + { PART_LINUX, "Linux" }, + { PART_LINUX_SWAP, "Linux swap" }, + { PART_DOS, "DOS/Windows" }, +}; + +const char * +parttype2str(enum partition_type type) +{ + size_t i; + + for (i = 0; i < nitems(ptypes); i++) + if (ptypes[i].type == type) + return (ptypes[i].desc); + return (ptypes[0].desc); +} + +#ifdef LOADER_GPT_SUPPORT +static void +uuid_letoh(uuid_t *uuid) +{ + + uuid->time_low = le32toh(uuid->time_low); + uuid->time_mid = le16toh(uuid->time_mid); + uuid->time_hi_and_version = le16toh(uuid->time_hi_and_version); +} + +static enum partition_type +gpt_parttype(uuid_t type) +{ + + if (uuid_equal(&type, &gpt_uuid_efi, NULL)) + return (PART_EFI); + else if (uuid_equal(&type, &gpt_uuid_ms_basic_data, NULL)) + return (PART_DOS); + else if (uuid_equal(&type, &gpt_uuid_freebsd_boot, NULL)) + return (PART_FREEBSD_BOOT); + else if (uuid_equal(&type, &gpt_uuid_freebsd_ufs, NULL)) + return (PART_FREEBSD_UFS); + else if (uuid_equal(&type, &gpt_uuid_freebsd_zfs, NULL)) + return (PART_FREEBSD_ZFS); + else if (uuid_equal(&type, &gpt_uuid_freebsd_swap, NULL)) + return (PART_FREEBSD_SWAP); + else if (uuid_equal(&type, &gpt_uuid_freebsd_vinum, NULL)) + return (PART_FREEBSD_VINUM); + else if (uuid_equal(&type, &gpt_uuid_freebsd_nandfs, NULL)) + return (PART_FREEBSD_NANDFS); + else if (uuid_equal(&type, &gpt_uuid_freebsd, NULL)) + return (PART_FREEBSD); + return (PART_UNKNOWN); +} + +static struct gpt_hdr * +gpt_checkhdr(struct gpt_hdr *hdr, uint64_t lba_self, uint64_t lba_last, + uint16_t sectorsize) +{ + uint32_t sz, crc; + + if (memcmp(hdr->hdr_sig, GPT_HDR_SIG, sizeof(hdr->hdr_sig)) != 0) { + DEBUG("no GPT signature"); + return (NULL); + } + sz = le32toh(hdr->hdr_size); + if (sz < 92 || sz > sectorsize) { + DEBUG("invalid GPT header size: %d", sz); + return (NULL); + } + crc = le32toh(hdr->hdr_crc_self); + hdr->hdr_crc_self = 0; + if (crc32(hdr, sz) != crc) { + DEBUG("GPT header's CRC doesn't match"); + return (NULL); + } + hdr->hdr_crc_self = crc; + hdr->hdr_revision = le32toh(hdr->hdr_revision); + if (hdr->hdr_revision < GPT_HDR_REVISION) { + DEBUG("unsupported GPT revision %d", hdr->hdr_revision); + return (NULL); + } + hdr->hdr_lba_self = le64toh(hdr->hdr_lba_self); + if (hdr->hdr_lba_self != lba_self) { + DEBUG("self LBA doesn't match"); + return (NULL); + } + hdr->hdr_lba_alt = le64toh(hdr->hdr_lba_alt); + if (hdr->hdr_lba_alt == hdr->hdr_lba_self) { + DEBUG("invalid alternate LBA"); + return (NULL); + } + hdr->hdr_entries = le32toh(hdr->hdr_entries); + hdr->hdr_entsz = le32toh(hdr->hdr_entsz); + if (hdr->hdr_entries == 0 || + hdr->hdr_entsz < sizeof(struct gpt_ent) || + sectorsize % hdr->hdr_entsz != 0) { + DEBUG("invalid entry size or number of entries"); + return (NULL); + } + hdr->hdr_lba_start = le64toh(hdr->hdr_lba_start); + hdr->hdr_lba_end = le64toh(hdr->hdr_lba_end); + hdr->hdr_lba_table = le64toh(hdr->hdr_lba_table); + hdr->hdr_crc_table = le32toh(hdr->hdr_crc_table); + uuid_letoh(&hdr->hdr_uuid); + return (hdr); +} + +static int +gpt_checktbl(const struct gpt_hdr *hdr, uint8_t *tbl, size_t size, + uint64_t lba_last) +{ + struct gpt_ent *ent; + uint32_t i, cnt; + + cnt = size / hdr->hdr_entsz; + if (hdr->hdr_entries <= cnt) { + cnt = hdr->hdr_entries; + /* Check CRC only when buffer size is enough for table. */ + if (hdr->hdr_crc_table != + crc32(tbl, hdr->hdr_entries * hdr->hdr_entsz)) { + DEBUG("GPT table's CRC doesn't match"); + return (-1); + } + } + for (i = 0; i < cnt; i++) { + ent = (struct gpt_ent *)(tbl + i * hdr->hdr_entsz); + uuid_letoh(&ent->ent_type); + if (uuid_equal(&ent->ent_type, &gpt_uuid_unused, NULL)) + continue; + ent->ent_lba_start = le64toh(ent->ent_lba_start); + ent->ent_lba_end = le64toh(ent->ent_lba_end); + } + return (0); +} + +static struct ptable * +ptable_gptread(struct ptable *table, void *dev, diskread_t dread) +{ + struct pentry *entry; + struct gpt_hdr *phdr, hdr; + struct gpt_ent *ent; + uint8_t *buf, *tbl; + uint64_t offset; + int pri, sec; + size_t size, i; + + buf = malloc(table->sectorsize); + if (buf == NULL) + return (NULL); + tbl = malloc(table->sectorsize * MAXTBLSZ); + if (tbl == NULL) { + free(buf); + return (NULL); + } + /* Read the primary GPT header. */ + if (dread(dev, buf, 1, 1) != 0) { + ptable_close(table); + table = NULL; + goto out; + } + pri = sec = 0; + /* Check the primary GPT header. */ + phdr = gpt_checkhdr((struct gpt_hdr *)buf, 1, table->sectors - 1, + table->sectorsize); + if (phdr != NULL) { + /* Read the primary GPT table. */ + size = MIN(MAXTBLSZ, + howmany(phdr->hdr_entries * phdr->hdr_entsz, + table->sectorsize)); + if (dread(dev, tbl, size, phdr->hdr_lba_table) == 0 && + gpt_checktbl(phdr, tbl, size * table->sectorsize, + table->sectors - 1) == 0) { + memcpy(&hdr, phdr, sizeof(hdr)); + pri = 1; + } + } + offset = pri ? hdr.hdr_lba_alt: table->sectors - 1; + /* Read the backup GPT header. */ + if (dread(dev, buf, 1, offset) != 0) + phdr = NULL; + else + phdr = gpt_checkhdr((struct gpt_hdr *)buf, offset, + table->sectors - 1, table->sectorsize); + if (phdr != NULL) { + /* + * Compare primary and backup headers. + * If they are equal, then we do not need to read backup + * table. If they are different, then prefer backup header + * and try to read backup table. + */ + if (pri == 0 || + uuid_equal(&hdr.hdr_uuid, &phdr->hdr_uuid, NULL) == 0 || + hdr.hdr_revision != phdr->hdr_revision || + hdr.hdr_size != phdr->hdr_size || + hdr.hdr_lba_start != phdr->hdr_lba_start || + hdr.hdr_lba_end != phdr->hdr_lba_end || + hdr.hdr_entries != phdr->hdr_entries || + hdr.hdr_entsz != phdr->hdr_entsz || + hdr.hdr_crc_table != phdr->hdr_crc_table) { + /* Read the backup GPT table. */ + size = MIN(MAXTBLSZ, + howmany(phdr->hdr_entries * phdr->hdr_entsz, + table->sectorsize)); + if (dread(dev, tbl, size, phdr->hdr_lba_table) == 0 && + gpt_checktbl(phdr, tbl, size * table->sectorsize, + table->sectors - 1) == 0) { + memcpy(&hdr, phdr, sizeof(hdr)); + sec = 1; + } + } + } + if (pri == 0 && sec == 0) { + /* Both primary and backup tables are invalid. */ + table->type = PTABLE_NONE; + goto out; + } + DEBUG("GPT detected"); + size = MIN(hdr.hdr_entries * hdr.hdr_entsz, + MAXTBLSZ * table->sectorsize); + + /* + * If the disk's sector count is smaller than the sector count recorded + * in the disk's GPT table header, set the table->sectors to the value + * recorded in GPT tables. This is done to work around buggy firmware + * that returns truncated disk sizes. + * + * Note, this is still not a foolproof way to get disk's size. For + * example, an image file can be truncated when copied to smaller media. + */ + if (hdr.hdr_lba_alt + 1 > table->sectors) + table->sectors = hdr.hdr_lba_alt + 1; + + for (i = 0; i < size / hdr.hdr_entsz; i++) { + ent = (struct gpt_ent *)(tbl + i * hdr.hdr_entsz); + if (uuid_equal(&ent->ent_type, &gpt_uuid_unused, NULL)) + continue; + + /* Simple sanity checks. */ + if (ent->ent_lba_start < hdr.hdr_lba_start || + ent->ent_lba_end > hdr.hdr_lba_end || + ent->ent_lba_start > ent->ent_lba_end) + continue; + + entry = malloc(sizeof(*entry)); + if (entry == NULL) + break; + entry->part.start = ent->ent_lba_start; + entry->part.end = ent->ent_lba_end; + entry->part.index = i + 1; + entry->part.type = gpt_parttype(ent->ent_type); + entry->flags = le64toh(ent->ent_attr); + memcpy(&entry->type.gpt, &ent->ent_type, sizeof(uuid_t)); + STAILQ_INSERT_TAIL(&table->entries, entry, entry); + DEBUG("new GPT partition added"); + } +out: + free(buf); + free(tbl); + return (table); +} +#endif /* LOADER_GPT_SUPPORT */ + +#ifdef LOADER_MBR_SUPPORT +/* We do not need to support too many EBR partitions in the loader */ +#define MAXEBRENTRIES 8 +static enum partition_type +mbr_parttype(uint8_t type) +{ + + switch (type) { + case DOSPTYP_386BSD: + return (PART_FREEBSD); + case DOSPTYP_LINSWP: + return (PART_LINUX_SWAP); + case DOSPTYP_LINUX: + return (PART_LINUX); + case 0x01: + case 0x04: + case 0x06: + case 0x07: + case 0x0b: + case 0x0c: + case 0x0e: + return (PART_DOS); + } + return (PART_UNKNOWN); +} + +static struct ptable * +ptable_ebrread(struct ptable *table, void *dev, diskread_t dread) +{ + struct dos_partition *dp; + struct pentry *e1, *entry; + uint32_t start, end, offset; + u_char *buf; + int i, index; + + STAILQ_FOREACH(e1, &table->entries, entry) { + if (e1->type.mbr == DOSPTYP_EXT || + e1->type.mbr == DOSPTYP_EXTLBA) + break; + } + if (e1 == NULL) + return (table); + index = 5; + offset = e1->part.start; + buf = malloc(table->sectorsize); + if (buf == NULL) + return (table); + DEBUG("EBR detected"); + for (i = 0; i < MAXEBRENTRIES; i++) { +#if 0 /* Some BIOSes return an incorrect number of sectors */ + if (offset >= table->sectors) + break; +#endif + if (dread(dev, buf, 1, offset) != 0) + break; + dp = (struct dos_partition *)(buf + DOSPARTOFF); + if (dp[0].dp_typ == 0) + break; + start = le32toh(dp[0].dp_start); + if (dp[0].dp_typ == DOSPTYP_EXT && + dp[1].dp_typ == 0) { + offset = e1->part.start + start; + continue; + } + end = le32toh(dp[0].dp_size); + entry = malloc(sizeof(*entry)); + if (entry == NULL) + break; + entry->part.start = offset + start; + entry->part.end = entry->part.start + end - 1; + entry->part.index = index++; + entry->part.type = mbr_parttype(dp[0].dp_typ); + entry->flags = dp[0].dp_flag; + entry->type.mbr = dp[0].dp_typ; + STAILQ_INSERT_TAIL(&table->entries, entry, entry); + DEBUG("new EBR partition added"); + if (dp[1].dp_typ == 0) + break; + offset = e1->part.start + le32toh(dp[1].dp_start); + } + free(buf); + return (table); +} +#endif /* LOADER_MBR_SUPPORT */ + +static enum partition_type +bsd_parttype(uint8_t type) +{ + + switch (type) { + case FS_NANDFS: + return (PART_FREEBSD_NANDFS); + case FS_SWAP: + return (PART_FREEBSD_SWAP); + case FS_BSDFFS: + return (PART_FREEBSD_UFS); + case FS_VINUM: + return (PART_FREEBSD_VINUM); + case FS_ZFS: + return (PART_FREEBSD_ZFS); + } + return (PART_UNKNOWN); +} + +static struct ptable * +ptable_bsdread(struct ptable *table, void *dev, diskread_t dread) +{ + struct disklabel *dl; + struct partition *part; + struct pentry *entry; + uint8_t *buf; + uint32_t raw_offset; + int i; + + if (table->sectorsize < sizeof(struct disklabel)) { + DEBUG("Too small sectorsize"); + return (table); + } + buf = malloc(table->sectorsize); + if (buf == NULL) + return (table); + if (dread(dev, buf, 1, 1) != 0) { + DEBUG("read failed"); + ptable_close(table); + table = NULL; + goto out; + } + dl = (struct disklabel *)buf; + if (le32toh(dl->d_magic) != DISKMAGIC && + le32toh(dl->d_magic2) != DISKMAGIC) + goto out; + if (le32toh(dl->d_secsize) != table->sectorsize) { + DEBUG("unsupported sector size"); + goto out; + } + dl->d_npartitions = le16toh(dl->d_npartitions); + if (dl->d_npartitions > 20 || dl->d_npartitions < 8) { + DEBUG("invalid number of partitions"); + goto out; + } + DEBUG("BSD detected"); + part = &dl->d_partitions[0]; + raw_offset = le32toh(part[RAW_PART].p_offset); + for (i = 0; i < dl->d_npartitions; i++, part++) { + if (i == RAW_PART) + continue; + if (part->p_size == 0) + continue; + entry = malloc(sizeof(*entry)); + if (entry == NULL) + break; + entry->part.start = le32toh(part->p_offset) - raw_offset; + entry->part.end = entry->part.start + + le32toh(part->p_size) - 1; + entry->part.type = bsd_parttype(part->p_fstype); + entry->part.index = i; /* starts from zero */ + entry->type.bsd = part->p_fstype; + STAILQ_INSERT_TAIL(&table->entries, entry, entry); + DEBUG("new BSD partition added"); + } + table->type = PTABLE_BSD; +out: + free(buf); + return (table); +} + +#ifdef LOADER_VTOC8_SUPPORT +static enum partition_type +vtoc8_parttype(uint16_t type) +{ + + switch (type) { + case VTOC_TAG_FREEBSD_NANDFS: + return (PART_FREEBSD_NANDFS); + case VTOC_TAG_FREEBSD_SWAP: + return (PART_FREEBSD_SWAP); + case VTOC_TAG_FREEBSD_UFS: + return (PART_FREEBSD_UFS); + case VTOC_TAG_FREEBSD_VINUM: + return (PART_FREEBSD_VINUM); + case VTOC_TAG_FREEBSD_ZFS: + return (PART_FREEBSD_ZFS); + } + return (PART_UNKNOWN); +} + +static struct ptable * +ptable_vtoc8read(struct ptable *table, void *dev, diskread_t dread) +{ + struct pentry *entry; + struct vtoc8 *dl; + uint8_t *buf; + uint16_t sum, heads, sectors; + int i; + + if (table->sectorsize != sizeof(struct vtoc8)) + return (table); + buf = malloc(table->sectorsize); + if (buf == NULL) + return (table); + if (dread(dev, buf, 1, 0) != 0) { + DEBUG("read failed"); + ptable_close(table); + table = NULL; + goto out; + } + dl = (struct vtoc8 *)buf; + /* Check the sum */ + for (i = sum = 0; i < sizeof(struct vtoc8); i += sizeof(sum)) + sum ^= be16dec(buf + i); + if (sum != 0) { + DEBUG("incorrect checksum"); + goto out; + } + if (be16toh(dl->nparts) != VTOC8_NPARTS) { + DEBUG("invalid number of entries"); + goto out; + } + sectors = be16toh(dl->nsecs); + heads = be16toh(dl->nheads); + if (sectors * heads == 0) { + DEBUG("invalid geometry"); + goto out; + } + DEBUG("VTOC8 detected"); + for (i = 0; i < VTOC8_NPARTS; i++) { + dl->part[i].tag = be16toh(dl->part[i].tag); + if (i == VTOC_RAW_PART || + dl->part[i].tag == VTOC_TAG_UNASSIGNED) + continue; + entry = malloc(sizeof(*entry)); + if (entry == NULL) + break; + entry->part.start = be32toh(dl->map[i].cyl) * heads * sectors; + entry->part.end = be32toh(dl->map[i].nblks) + + entry->part.start - 1; + entry->part.type = vtoc8_parttype(dl->part[i].tag); + entry->part.index = i; /* starts from zero */ + entry->type.vtoc8 = dl->part[i].tag; + STAILQ_INSERT_TAIL(&table->entries, entry, entry); + DEBUG("new VTOC8 partition added"); + } + table->type = PTABLE_VTOC8; +out: + free(buf); + return (table); + +} +#endif /* LOADER_VTOC8_SUPPORT */ + +struct ptable * +ptable_open(void *dev, uint64_t sectors, uint16_t sectorsize, + diskread_t *dread) +{ + struct dos_partition *dp; + struct ptable *table; + uint8_t *buf; + int i, count; +#ifdef LOADER_MBR_SUPPORT + struct pentry *entry; + uint32_t start, end; + int has_ext; +#endif + table = NULL; + buf = malloc(sectorsize); + if (buf == NULL) + return (NULL); + /* First, read the MBR. */ + if (dread(dev, buf, 1, DOSBBSECTOR) != 0) { + DEBUG("read failed"); + goto out; + } + + table = malloc(sizeof(*table)); + if (table == NULL) + goto out; + table->sectors = sectors; + table->sectorsize = sectorsize; + table->type = PTABLE_NONE; + STAILQ_INIT(&table->entries); + +#ifdef LOADER_VTOC8_SUPPORT + if (be16dec(buf + offsetof(struct vtoc8, magic)) == VTOC_MAGIC) { + if (ptable_vtoc8read(table, dev, dread) == NULL) { + /* Read error. */ + table = NULL; + goto out; + } else if (table->type == PTABLE_VTOC8) + goto out; + } +#endif + /* Check the BSD label. */ + if (ptable_bsdread(table, dev, dread) == NULL) { /* Read error. */ + table = NULL; + goto out; + } else if (table->type == PTABLE_BSD) + goto out; + +#if defined(LOADER_GPT_SUPPORT) || defined(LOADER_MBR_SUPPORT) + /* Check the MBR magic. */ + if (buf[DOSMAGICOFFSET] != 0x55 || + buf[DOSMAGICOFFSET + 1] != 0xaa) { + DEBUG("magic sequence not found"); +#if defined(LOADER_GPT_SUPPORT) + /* There is no PMBR, check that we have backup GPT */ + table->type = PTABLE_GPT; + table = ptable_gptread(table, dev, dread); +#endif + goto out; + } + /* Check that we have PMBR. Also do some validation. */ + dp = (struct dos_partition *)(buf + DOSPARTOFF); + for (i = 0, count = 0; i < NDOSPART; i++) { + if (dp[i].dp_flag != 0 && dp[i].dp_flag != 0x80) { + DEBUG("invalid partition flag %x", dp[i].dp_flag); + goto out; + } +#ifdef LOADER_GPT_SUPPORT + if (dp[i].dp_typ == DOSPTYP_PMBR) { + table->type = PTABLE_GPT; + DEBUG("PMBR detected"); + } +#endif + if (dp[i].dp_typ != 0) + count++; + } + /* Do we have some invalid values? */ + if (table->type == PTABLE_GPT && count > 1) { + if (dp[1].dp_typ != DOSPTYP_HFS) { + table->type = PTABLE_NONE; + DEBUG("Incorrect PMBR, ignore it"); + } else { + DEBUG("Bootcamp detected"); + } + } +#ifdef LOADER_GPT_SUPPORT + if (table->type == PTABLE_GPT) { + table = ptable_gptread(table, dev, dread); + goto out; + } +#endif +#ifdef LOADER_MBR_SUPPORT + /* Read MBR. */ + DEBUG("MBR detected"); + table->type = PTABLE_MBR; + for (i = has_ext = 0; i < NDOSPART; i++) { + if (dp[i].dp_typ == 0) + continue; + start = le32dec(&(dp[i].dp_start)); + end = le32dec(&(dp[i].dp_size)); + if (start == 0 || end == 0) + continue; +#if 0 /* Some BIOSes return an incorrect number of sectors */ + if (start + end - 1 >= sectors) + continue; /* XXX: ignore */ +#endif + if (dp[i].dp_typ == DOSPTYP_EXT || + dp[i].dp_typ == DOSPTYP_EXTLBA) + has_ext = 1; + entry = malloc(sizeof(*entry)); + if (entry == NULL) + break; + entry->part.start = start; + entry->part.end = start + end - 1; + entry->part.index = i + 1; + entry->part.type = mbr_parttype(dp[i].dp_typ); + entry->flags = dp[i].dp_flag; + entry->type.mbr = dp[i].dp_typ; + STAILQ_INSERT_TAIL(&table->entries, entry, entry); + DEBUG("new MBR partition added"); + } + if (has_ext) { + table = ptable_ebrread(table, dev, dread); + /* FALLTHROUGH */ + } +#endif /* LOADER_MBR_SUPPORT */ +#endif /* LOADER_MBR_SUPPORT || LOADER_GPT_SUPPORT */ +out: + free(buf); + return (table); +} + +void +ptable_close(struct ptable *table) +{ + struct pentry *entry; + + while (!STAILQ_EMPTY(&table->entries)) { + entry = STAILQ_FIRST(&table->entries); + STAILQ_REMOVE_HEAD(&table->entries, entry); + free(entry); + } + free(table); +} + +enum ptable_type +ptable_gettype(const struct ptable *table) +{ + + return (table->type); +} + +int +ptable_getsize(const struct ptable *table, uint64_t *sizep) +{ + uint64_t tmp = table->sectors * table->sectorsize; + + if (tmp < table->sectors) + return (EOVERFLOW); + + if (sizep != NULL) + *sizep = tmp; + return (0); +} + +int +ptable_getpart(const struct ptable *table, struct ptable_entry *part, int index) +{ + struct pentry *entry; + + if (part == NULL || table == NULL) + return (EINVAL); + + STAILQ_FOREACH(entry, &table->entries, entry) { + if (entry->part.index != index) + continue; + memcpy(part, &entry->part, sizeof(*part)); + return (0); + } + return (ENOENT); +} + +/* + * Search for a slice with the following preferences: + * + * 1: Active FreeBSD slice + * 2: Non-active FreeBSD slice + * 3: Active Linux slice + * 4: non-active Linux slice + * 5: Active FAT/FAT32 slice + * 6: non-active FAT/FAT32 slice + */ +#define PREF_RAWDISK 0 +#define PREF_FBSD_ACT 1 +#define PREF_FBSD 2 +#define PREF_LINUX_ACT 3 +#define PREF_LINUX 4 +#define PREF_DOS_ACT 5 +#define PREF_DOS 6 +#define PREF_NONE 7 +int +ptable_getbestpart(const struct ptable *table, struct ptable_entry *part) +{ + struct pentry *entry, *best; + int pref, preflevel; + + if (part == NULL || table == NULL) + return (EINVAL); + + best = NULL; + preflevel = pref = PREF_NONE; + STAILQ_FOREACH(entry, &table->entries, entry) { +#ifdef LOADER_MBR_SUPPORT + if (table->type == PTABLE_MBR) { + switch (entry->type.mbr) { + case DOSPTYP_386BSD: + pref = entry->flags & 0x80 ? PREF_FBSD_ACT: + PREF_FBSD; + break; + case DOSPTYP_LINUX: + pref = entry->flags & 0x80 ? PREF_LINUX_ACT: + PREF_LINUX; + break; + case 0x01: /* DOS/Windows */ + case 0x04: + case 0x06: + case 0x0c: + case 0x0e: + case DOSPTYP_FAT32: + pref = entry->flags & 0x80 ? PREF_DOS_ACT: + PREF_DOS; + break; + default: + pref = PREF_NONE; + } + } +#endif /* LOADER_MBR_SUPPORT */ +#ifdef LOADER_GPT_SUPPORT + if (table->type == PTABLE_GPT) { + if (entry->part.type == PART_DOS) + pref = PREF_DOS; + else if (entry->part.type == PART_FREEBSD_UFS || + entry->part.type == PART_FREEBSD_ZFS) + pref = PREF_FBSD; + else + pref = PREF_NONE; + } +#endif /* LOADER_GPT_SUPPORT */ + if (pref < preflevel) { + preflevel = pref; + best = entry; + } + } + if (best != NULL) { + memcpy(part, &best->part, sizeof(*part)); + return (0); + } + return (ENOENT); +} + +int +ptable_iterate(const struct ptable *table, void *arg, ptable_iterate_t *iter) +{ + struct pentry *entry; + char name[32]; + int ret = 0; + + name[0] = '\0'; + STAILQ_FOREACH(entry, &table->entries, entry) { +#ifdef LOADER_MBR_SUPPORT + if (table->type == PTABLE_MBR) + sprintf(name, "s%d", entry->part.index); + else +#endif +#ifdef LOADER_GPT_SUPPORT + if (table->type == PTABLE_GPT) + sprintf(name, "p%d", entry->part.index); + else +#endif +#ifdef LOADER_VTOC8_SUPPORT + if (table->type == PTABLE_VTOC8) + sprintf(name, "%c", (uint8_t) 'a' + + entry->part.index); + else +#endif + if (table->type == PTABLE_BSD) + sprintf(name, "%c", (uint8_t) 'a' + + entry->part.index); + if ((ret = iter(arg, name, &entry->part)) != 0) + return (ret); + } + return (ret); +} diff --git a/stand/common/part.h b/stand/common/part.h new file mode 100644 index 0000000..19bd670 --- /dev/null +++ b/stand/common/part.h @@ -0,0 +1,83 @@ +/*- + * Copyright (c) 2012 Andrey V. Elsukov + * 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 AUTHORS 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 AUTHORS 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 _PART_H_ +#define _PART_H_ + +struct ptable; + +enum ptable_type { + PTABLE_NONE, + PTABLE_BSD, + PTABLE_MBR, + PTABLE_GPT, + PTABLE_VTOC8 +}; + +enum partition_type { + PART_UNKNOWN, + PART_EFI, + PART_FREEBSD, + PART_FREEBSD_BOOT, + PART_FREEBSD_NANDFS, + PART_FREEBSD_UFS, + PART_FREEBSD_ZFS, + PART_FREEBSD_SWAP, + PART_FREEBSD_VINUM, + PART_LINUX, + PART_LINUX_SWAP, + PART_DOS, +}; + +struct ptable_entry { + uint64_t start; + uint64_t end; + int index; + enum partition_type type; +}; + +/* The offset and size are in sectors */ +typedef int (diskread_t)(void *arg, void *buf, size_t blocks, uint64_t offset); +typedef int (ptable_iterate_t)(void *arg, const char *partname, + const struct ptable_entry *part); + +struct ptable *ptable_open(void *dev, uint64_t sectors, uint16_t sectorsize, + diskread_t *dread); +void ptable_close(struct ptable *table); +enum ptable_type ptable_gettype(const struct ptable *table); +int ptable_getsize(const struct ptable *table, uint64_t *sizep); + +int ptable_getpart(const struct ptable *table, struct ptable_entry *part, + int index); +int ptable_getbestpart(const struct ptable *table, struct ptable_entry *part); + +int ptable_iterate(const struct ptable *table, void *arg, + ptable_iterate_t *iter); +const char *parttype2str(enum partition_type type); + +#endif /* !_PART_H_ */ diff --git a/stand/common/paths.h b/stand/common/paths.h new file mode 100644 index 0000000..9ed45e6 --- /dev/null +++ b/stand/common/paths.h @@ -0,0 +1,39 @@ +/*- + * Copyright (c) 2016 M. Warner Losh + * 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 AUTHORS 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 AUTHORS 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 _PATHS_H_ +#define _PATHS_H_ + +#define PATH_DOTCONFIG "/boot.config" +#define PATH_CONFIG "/boot/config" +#define PATH_LOADER "/boot/loader" +#define PATH_LOADER_EFI "/boot/loader.efi" +#define PATH_LOADER_ZFS "/boot/zfsloader" +#define PATH_KERNEL "/boot/kernel/kernel" + +#endif /* _PATHS_H_ */ diff --git a/stand/common/pnp.c b/stand/common/pnp.c new file mode 100644 index 0000000..6197776 --- /dev/null +++ b/stand/common/pnp.c @@ -0,0 +1,236 @@ +/* + * mjs copyright + * + */ + +#include +__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 +#include +#include +#ifdef BOOT_FORTH +#include "ficl.h" +#endif + +static 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(); + if (pager_output("PNP scan summary:\n")) + goto out; + 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); + } + if (pager_output("\n")) + break; + } +out: + pager_close(); + } + return(CMD_OK); +} + +/* + * 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); + } +} + +/* + * 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); +} + +#ifdef BOOT_FORTH +void +ficlPnpdevices(FICL_VM *pVM) +{ + static int pnp_devices_initted = 0; +#if FICL_ROBUST > 1 + vmCheckStack(pVM, 0, 1); +#endif + + if(!pnp_devices_initted) { + STAILQ_INIT(&pnp_devices); + pnp_devices_initted = 1; + } + + stackPushPtr(pVM->pStack, &pnp_devices); + + return; +} + +void +ficlPnphandlers(FICL_VM *pVM) +{ +#if FICL_ROBUST > 1 + vmCheckStack(pVM, 0, 1); +#endif + + stackPushPtr(pVM->pStack, pnphandlers); + + return; +} + +/* + * Glue function to add the appropriate forth words to access pnp BIOS + * functionality. + */ +static void ficlCompilePnp(FICL_SYSTEM *pSys) +{ + FICL_DICT *dp = pSys->dp; + assert (dp); + + dictAppendWord(dp, "pnpdevices",ficlPnpdevices, FW_DEFAULT); + dictAppendWord(dp, "pnphandlers",ficlPnphandlers, FW_DEFAULT); +} + +FICL_COMPILE_SET(ficlCompilePnp); +#endif diff --git a/stand/common/rbx.h b/stand/common/rbx.h new file mode 100644 index 0000000..21371a5 --- /dev/null +++ b/stand/common/rbx.h @@ -0,0 +1,61 @@ +/*- + * 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. + * + * $FreeBSD$ + */ + +#ifndef _RBX_H_ +#define _RBX_H_ + +#define RBX_ASKNAME 0x0 /* -a */ +#define RBX_SINGLE 0x1 /* -s */ +/* 0x2 is reserved for log2(RB_NOSYNC). */ +/* 0x3 is reserved for log2(RB_HALT). */ +/* 0x4 is reserved for log2(RB_INITNAME). */ +#define RBX_DFLTROOT 0x5 /* -r */ +#define RBX_KDB 0x6 /* -d */ +/* 0x7 is reserved for log2(RB_RDONLY). */ +/* 0x8 is reserved for log2(RB_DUMP). */ +/* 0x9 is reserved for log2(RB_MINIROOT). */ +#define RBX_CONFIG 0xa /* -c */ +#define RBX_VERBOSE 0xb /* -v */ +#define RBX_SERIAL 0xc /* -h */ +#define RBX_CDROM 0xd /* -C */ +/* 0xe is reserved for log2(RB_POWEROFF). */ +#define RBX_GDB 0xf /* -g */ +#define RBX_MUTE 0x10 /* -m */ +/* 0x11 is reserved for log2(RB_SELFTEST). */ +/* 0x12 is reserved for boot programs. */ +/* 0x13 is reserved for boot programs. */ +#define RBX_PAUSE 0x14 /* -p */ +#define RBX_QUIET 0x15 /* -q */ +#define RBX_NOINTR 0x1c /* -n */ +/* 0x1d is reserved for log2(RB_MULTIPLE) and is just misnamed here. */ +#define RBX_DUAL 0x1d /* -D */ +/* 0x1f is reserved for log2(RB_BOOTINFO). */ + +/* pass: -a, -s, -r, -d, -c, -v, -h, -C, -g, -m, -p, -D */ +#define RBX_MASK (OPT_SET(RBX_ASKNAME) | OPT_SET(RBX_SINGLE) | \ + OPT_SET(RBX_DFLTROOT) | OPT_SET(RBX_KDB ) | \ + OPT_SET(RBX_CONFIG) | OPT_SET(RBX_VERBOSE) | \ + OPT_SET(RBX_SERIAL) | OPT_SET(RBX_CDROM) | \ + OPT_SET(RBX_GDB ) | OPT_SET(RBX_MUTE) | \ + OPT_SET(RBX_PAUSE) | OPT_SET(RBX_DUAL)) + +#define OPT_SET(opt) (1 << (opt)) +#define OPT_CHECK(opt) ((opts) & OPT_SET(opt)) + +extern uint32_t opts; + +#endif /* !_RBX_H_ */ diff --git a/stand/common/reloc_elf.c b/stand/common/reloc_elf.c new file mode 100644 index 0000000..aa3b0f7 --- /dev/null +++ b/stand/common/reloc_elf.c @@ -0,0 +1,231 @@ +/*- + * Copyright (c) 2003 Jake Burkholder. + * Copyright 1996-1998 John D. Polstra. + * Copyright (c) 1998 Michael Smith + * Copyright (c) 1998 Peter Wemm + * 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 +__FBSDID("$FreeBSD$"); + +#include +#include + +#include + +#define FREEBSD_ELF +#include + +#include "bootstrap.h" + +#define COPYOUT(s,d,l) archsw.arch_copyout((vm_offset_t)(s), d, l) + +/* + * Apply a single intra-module relocation to the data. `relbase' is the + * target relocation base for the section (i.e. it corresponds to where + * r_offset == 0). `dataaddr' is the relocated address corresponding to + * the start of the data, and `len' is the number of bytes. + */ +int +__elfN(reloc)(struct elf_file *ef, symaddr_fn *symaddr, const void *reldata, + int reltype, Elf_Addr relbase, Elf_Addr dataaddr, void *data, size_t len) +{ +#ifdef __sparc__ + Elf_Size w; + const Elf_Rela *a; + + switch (reltype) { + case ELF_RELOC_RELA: + a = reldata; + if (relbase + a->r_offset >= dataaddr && + relbase + a->r_offset < dataaddr + len) { + switch (ELF_R_TYPE(a->r_info)) { + case R_SPARC_RELATIVE: + w = relbase + a->r_addend; + bcopy(&w, (u_char *)data + (relbase + + a->r_offset - dataaddr), sizeof(w)); + break; + default: + printf("\nunhandled relocation type %u\n", + (u_int)ELF_R_TYPE(a->r_info)); + return (EFTYPE); + } + } + break; + } + + return (0); +#elif (defined(__i386__) || defined(__amd64__)) && __ELF_WORD_SIZE == 64 + Elf64_Addr *where, val; + Elf_Addr addend, addr; + Elf_Size rtype, symidx; + const Elf_Rel *rel; + const Elf_Rela *rela; + + switch (reltype) { + case ELF_RELOC_REL: + rel = (const Elf_Rel *)reldata; + where = (Elf_Addr *)((char *)data + relbase + rel->r_offset - + dataaddr); + addend = 0; + rtype = ELF_R_TYPE(rel->r_info); + symidx = ELF_R_SYM(rel->r_info); + addend = 0; + break; + case ELF_RELOC_RELA: + rela = (const Elf_Rela *)reldata; + where = (Elf_Addr *)((char *)data + relbase + rela->r_offset - + dataaddr); + addend = rela->r_addend; + rtype = ELF_R_TYPE(rela->r_info); + symidx = ELF_R_SYM(rela->r_info); + break; + default: + return (EINVAL); + } + + if ((char *)where < (char *)data || (char *)where >= (char *)data + len) + return (0); + + if (reltype == ELF_RELOC_REL) + addend = *where; + +/* XXX, definitions not available on i386. */ +#define R_X86_64_64 1 +#define R_X86_64_RELATIVE 8 +#define R_X86_64_IRELATIVE 37 + + switch (rtype) { + case R_X86_64_64: /* S + A */ + addr = symaddr(ef, symidx); + if (addr == 0) + return (ESRCH); + val = addr + addend; + *where = val; + break; + case R_X86_64_RELATIVE: + addr = (Elf_Addr)addend + relbase; + val = addr; + *where = val; + break; + case R_X86_64_IRELATIVE: + /* leave it to kernel */ + break; + default: + printf("\nunhandled relocation type %u\n", (u_int)rtype); + return (EFTYPE); + } + + return (0); +#elif defined(__i386__) && __ELF_WORD_SIZE == 32 + Elf_Addr addend, addr, *where, val; + Elf_Size rtype, symidx; + const Elf_Rel *rel; + const Elf_Rela *rela; + + switch (reltype) { + case ELF_RELOC_REL: + rel = (const Elf_Rel *)reldata; + where = (Elf_Addr *)((char *)data + relbase + rel->r_offset - + dataaddr); + addend = 0; + rtype = ELF_R_TYPE(rel->r_info); + symidx = ELF_R_SYM(rel->r_info); + addend = 0; + break; + case ELF_RELOC_RELA: + rela = (const Elf_Rela *)reldata; + where = (Elf_Addr *)((char *)data + relbase + rela->r_offset - + dataaddr); + addend = rela->r_addend; + rtype = ELF_R_TYPE(rela->r_info); + symidx = ELF_R_SYM(rela->r_info); + break; + default: + return (EINVAL); + } + + if ((char *)where < (char *)data || (char *)where >= (char *)data + len) + return (0); + + if (reltype == ELF_RELOC_REL) + addend = *where; + +/* XXX, definitions not available on amd64. */ +#define R_386_32 1 /* Add symbol value. */ +#define R_386_GLOB_DAT 6 /* Set GOT entry to data address. */ +#define R_386_RELATIVE 8 /* Add load address of shared object. */ +#define R_386_IRELATIVE 42 + + switch (rtype) { + case R_386_RELATIVE: + addr = addend + relbase; + *where = addr; + break; + case R_386_32: /* S + A */ + addr = symaddr(ef, symidx); + if (addr == 0) + return (ESRCH); + val = addr + addend; + *where = val; + break; + case R_386_IRELATIVE: + /* leave it to kernel */ + break; + default: + printf("\nunhandled relocation type %u\n", (u_int)rtype); + return (EFTYPE); + } + + return (0); +#elif defined(__powerpc__) + Elf_Size w; + const Elf_Rela *rela; + + switch (reltype) { + case ELF_RELOC_RELA: + rela = reldata; + if (relbase + rela->r_offset >= dataaddr && + relbase + rela->r_offset < dataaddr + len) { + switch (ELF_R_TYPE(rela->r_info)) { + case R_PPC_RELATIVE: + w = relbase + rela->r_addend; + bcopy(&w, (u_char *)data + (relbase + + rela->r_offset - dataaddr), sizeof(w)); + break; + default: + printf("\nunhandled relocation type %u\n", + (u_int)ELF_R_TYPE(rela->r_info)); + return (EFTYPE); + } + } + break; + } + + return (0); +#else + return (EOPNOTSUPP); +#endif +} diff --git a/stand/common/reloc_elf32.c b/stand/common/reloc_elf32.c new file mode 100644 index 0000000..03d9d73 --- /dev/null +++ b/stand/common/reloc_elf32.c @@ -0,0 +1,6 @@ +#include +__FBSDID("$FreeBSD$"); + +#define __ELF_WORD_SIZE 32 + +#include "reloc_elf.c" diff --git a/stand/common/reloc_elf64.c b/stand/common/reloc_elf64.c new file mode 100644 index 0000000..c8dcf2a --- /dev/null +++ b/stand/common/reloc_elf64.c @@ -0,0 +1,6 @@ +#include +__FBSDID("$FreeBSD$"); + +#define __ELF_WORD_SIZE 64 + +#include "reloc_elf.c" diff --git a/stand/common/self_reloc.c b/stand/common/self_reloc.c new file mode 100644 index 0000000..5f6bfcb --- /dev/null +++ b/stand/common/self_reloc.c @@ -0,0 +1,124 @@ +/*- + * Copyright (c) 2008-2010 Rui Paulo + * 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 +__FBSDID("$FreeBSD$"); + +#include +#include +#include + +#if defined(__aarch64__) || defined(__amd64__) +#define ElfW_Rel Elf64_Rela +#define ElfW_Dyn Elf64_Dyn +#define ELFW_R_TYPE ELF64_R_TYPE +#define ELF_RELA +#elif defined(__arm__) || defined(__i386__) +#define ElfW_Rel Elf32_Rel +#define ElfW_Dyn Elf32_Dyn +#define ELFW_R_TYPE ELF32_R_TYPE +#else +#error architecture not supported +#endif +#if defined(__aarch64__) +#define RELOC_TYPE_NONE R_AARCH64_NONE +#define RELOC_TYPE_RELATIVE R_AARCH64_RELATIVE +#elif defined(__amd64__) +#define RELOC_TYPE_NONE R_X86_64_NONE +#define RELOC_TYPE_RELATIVE R_X86_64_RELATIVE +#elif defined(__arm__) +#define RELOC_TYPE_NONE R_ARM_NONE +#define RELOC_TYPE_RELATIVE R_ARM_RELATIVE +#elif defined(__i386__) +#define RELOC_TYPE_NONE R_386_NONE +#define RELOC_TYPE_RELATIVE R_386_RELATIVE +#endif + +void self_reloc(Elf_Addr baseaddr, ElfW_Dyn *dynamic); + +/* + * A simple elf relocator. + */ +void +self_reloc(Elf_Addr baseaddr, ElfW_Dyn *dynamic) +{ + Elf_Word relsz, relent; + Elf_Addr *newaddr; + ElfW_Rel *rel; + ElfW_Dyn *dynp; + + /* + * Find the relocation address, its size and the relocation entry. + */ + relsz = 0; + relent = 0; + for (dynp = dynamic; dynp->d_tag != DT_NULL; dynp++) { + switch (dynp->d_tag) { + case DT_REL: + case DT_RELA: + rel = (ElfW_Rel *)(dynp->d_un.d_ptr + baseaddr); + break; + case DT_RELSZ: + case DT_RELASZ: + relsz = dynp->d_un.d_val; + break; + case DT_RELENT: + case DT_RELAENT: + relent = dynp->d_un.d_val; + break; + default: + break; + } + } + + /* + * Perform the actual relocation. We rely on the object having been + * linked at 0, so that the difference between the load and link + * address is the same as the load address. + */ + for (; relsz > 0; relsz -= relent) { + switch (ELFW_R_TYPE(rel->r_info)) { + case RELOC_TYPE_NONE: + /* No relocation needs be performed. */ + break; + + case RELOC_TYPE_RELATIVE: + newaddr = (Elf_Addr *)(rel->r_offset + baseaddr); +#ifdef ELF_RELA + /* Addend relative to the base address. */ + *newaddr = baseaddr + rel->r_addend; +#else + /* Address relative to the base address. */ + *newaddr += baseaddr; +#endif + break; + default: + /* XXX: do we need other relocations ? */ + break; + } + rel = (ElfW_Rel *)(void *)((caddr_t) rel + relent); + } +} -- cgit v1.1