diff --git a/stand/arm64/libarm64/cache.c b/stand/arm64/libarm64/cache.c index 291b55f3b250..7b74f584ffe3 100644 --- a/stand/arm64/libarm64/cache.c +++ b/stand/arm64/libarm64/cache.c @@ -1,148 +1,147 @@ /*- * Copyright (c) 2014 The FreeBSD Foundation * * This software was developed by Semihalf under * the sponsorship of the FreeBSD Foundation. * 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 #include #include #include #include "bootstrap.h" #include "cache.h" static long cache_flags; #define CACHE_FLAG_DIC_OFF (1<<0) #define CACHE_FLAG_IDC_OFF (1<<1) static bool get_cache_dic(uint64_t ctr) { if ((cache_flags & CACHE_FLAG_DIC_OFF) != 0) { return (false); } return (CTR_DIC_VAL(ctr) != 0); } static bool get_cache_idc(uint64_t ctr) { if ((cache_flags & CACHE_FLAG_IDC_OFF) != 0) { return (false); } return (CTR_IDC_VAL(ctr) != 0); } static unsigned int get_dcache_line_size(uint64_t ctr) { unsigned int dcl_size; /* * Relevant field [19:16] is LOG2 * of the number of words in DCache line */ dcl_size = CTR_DLINE_SIZE(ctr); /* Size of word shifted by cache line size */ return (sizeof(int) << dcl_size); } void cpu_flush_dcache(const void *ptr, size_t len) { uint64_t cl_size, ctr; vm_offset_t addr, end; /* Accessible from all security levels */ ctr = READ_SPECIALREG(ctr_el0); if (get_cache_idc(ctr)) { dsb(ishst); } else { cl_size = get_dcache_line_size(ctr); /* Calculate end address to clean */ end = (vm_offset_t)ptr + (vm_offset_t)len; /* Align start address to cache line */ addr = (vm_offset_t)ptr; addr = rounddown2(addr, cl_size); for (; addr < end; addr += cl_size) __asm __volatile("dc civac, %0" : : "r" (addr) : "memory"); /* Full system DSB */ dsb(ish); } } void cpu_inval_icache(void) { uint64_t ctr; /* Accessible from all security levels */ ctr = READ_SPECIALREG(ctr_el0); if (get_cache_dic(ctr)) { isb(); } else { __asm __volatile( "ic ialluis \n" "dsb ish \n" "isb \n" : : : "memory"); } } static int command_cache_flags(int argc, char *argv[]) { char *cp; long new_flags; if (argc == 3) { if (strcmp(argv[1], "set") == 0) { new_flags = strtol(argv[2], &cp, 0); if (cp[0] != '\0') { printf("Invalid flags\n"); } else { printf("Setting cache flags to %#lx\n", new_flags); cache_flags = new_flags; return (CMD_OK); } } } printf("usage: cache_flags set \n"); return (CMD_ERROR); } COMMAND_SET(cache_flags, "cache_flags", "Set cache flags", command_cache_flags); diff --git a/stand/common/bcache.c b/stand/common/bcache.c index 2b4603525740..f4805cbaa715 100644 --- a/stand/common/bcache.c +++ b/stand/common/bcache.c @@ -1,523 +1,522 @@ /*- * 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 /* * Simple hashed block cache */ #include #include #include #include #include "bootstrap.h" /* #define BCACHE_DEBUG */ #ifdef BCACHE_DEBUG # define DPRINTF(fmt, args...) printf("%s: " fmt "\n" , __func__ , ## args) #else # define DPRINTF(fmt, args...) ((void)0) #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; daddr_t bcache_nextblkno; size_t ralen; }; 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 512 #define BCACHE_MINREADAHEAD 32 #define BCACHE_MAXIOWRA 512 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; 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); 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); } /* 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 */ bc->bcache_nextblkno = -1; 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; if (bc == NULL) { errno = ENODEV; return (-1); } 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; break; } else { bcache_hits++; } } /* * Adjust read-ahead size if appropriate. Subject to the requirement * that bc->ra must stay in between MINREADAHEAD and READAHEAD, we * increase it when we notice that readahead was useful and decrease * it when we notice that readahead was not useful. */ if (complete || (i == bc->ralen && bc->ralen > 0)) { if (bc->ra < BCACHE_READAHEAD) bc->ra <<= 1; /* increase read ahead */ } else { if (nblk - i > BCACHE_MINREADAHEAD && bc->ralen > 0 && bc->ra > BCACHE_MINREADAHEAD) bc->ra >>= 1; /* reduce read ahead */ } /* Adjust our "unconsumed readahead" value. */ if (blk == bc->bcache_nextblkno) { if (nblk > bc->ralen) bc->ralen = 0; else bc->ralen -= nblk; } if (complete) { /* whole set was in cache, return it */ 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); /* * Only trigger read-ahead if we detect two blocks being read * sequentially. */ if ((bc->bcache_nextblkno != blk) && ra != 0) { ra = 0; } 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 */ if (ra + p_size > BCACHE_MAXIOWRA) ra = BCACHE_MAXIOWRA - p_size; bc->ralen = ra; p_size += ra; } else { bc->ralen = 0; } /* 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; } done: if (result == 0) { if (rsize != NULL) *rsize = size; bc->bcache_nextblkno = blk + (size / DEV_BSIZE); } 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)) { DPRINTF("bypass %zu from %jd", 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) { free(bc->bcache_ctl); 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); DPRINTF("insert blk %jd -> %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; DPRINTF("invalidate blk %ju", blkno); } } #ifndef BOOT2 COMMAND_SET(bcachestat, "bcachestat", "get disk block cache stats", command_bcache); static int command_bcache(int argc, char *argv[] __unused) { if (argc != 1) { command_errmsg = "wrong number of arguments"; return(CMD_ERROR); } printf("\ncache blocks: %u\n", bcache_total_nblks); printf("cache blocksz: %u\n", bcache_blksize); printf("cache readahead: %u\n", bcache_rablks); printf("unit cache blocks: %u\n", bcache_unit_nblks); printf("cached units: %u\n", bcache_units); printf("%u ops %d bypasses %u hits %u misses\n", bcache_ops, bcache_bypasses, bcache_hits, bcache_misses); return(CMD_OK); } #endif diff --git a/stand/common/console.c b/stand/common/console.c index d9d075ef7bde..82cb552b4ef2 100644 --- a/stand/common/console.c +++ b/stand/common/console.c @@ -1,350 +1,349 @@ /*- * 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 #include #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); #ifndef MODULE_VERBOSE # define MODULE_VERBOSE MODULE_VERBOSE_TWIDDLE #endif int module_verbose = MODULE_VERBOSE; static int module_verbose_set(struct env_var *ev, int flags, const void *value) { u_long v; char *eptr; v = strtoul(value, &eptr, 0); if (*(const char *)value == 0 || *eptr != 0) { printf("invalid module_verbose '%s'\n", (const char *)value); return (CMD_ERROR); } module_verbose = (int)v; if (module_verbose < MODULE_VERBOSE_TWIDDLE) { /* A hack for now; we do not want twiddling */ twiddle_divisor(UINT_MAX); } env_setenv(ev->ev_name, flags | EV_NOHOOK, value, NULL, NULL); return (CMD_OK); } /* * 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; char module_verbose_buf[8]; TSENTER(); /* We want a callback to install the new value when these vars change. */ snprintf(module_verbose_buf, sizeof(module_verbose_buf), "%d", module_verbose); env_setenv("module_verbose", EV_VOLATILE, module_verbose_buf, module_verbose_set, env_nounset); env_setenv("twiddle_divisor", EV_VOLATILE, "16", 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); } TSEXIT(); } 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) == 0) 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 unavailable\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/gfx_fb.c b/stand/common/gfx_fb.c index 27ac66f259b1..c10d227a5fc8 100644 --- a/stand/common/gfx_fb.c +++ b/stand/common/gfx_fb.c @@ -1,2920 +1,2919 @@ /*- * SPDX-License-Identifier: BSD-2-Clause * * Copyright 2020 Toomas Soome * Copyright 2019 OmniOS Community Edition (OmniOSce) Association. * Copyright 2020 RackTop Systems, Inc. * * 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. */ /* * The workhorse here is gfxfb_blt(). It is implemented to mimic UEFI * GOP Blt, and allows us to fill the rectangle on screen, copy * rectangle from video to buffer and buffer to video and video to video. * Such implementation does allow us to have almost identical implementation * for both BIOS VBE and UEFI. * * ALL pixel data is assumed to be 32-bit BGRA (byte order Blue, Green, Red, * Alpha) format, this allows us to only handle RGB data and not to worry * about mixing RGB with indexed colors. * Data exchange between memory buffer and video will translate BGRA * and native format as following: * * 32-bit to/from 32-bit is trivial case. * 32-bit to/from 24-bit is also simple - we just drop the alpha channel. * 32-bit to/from 16-bit is more complicated, because we nee to handle * data loss from 32-bit to 16-bit. While reading/writing from/to video, we * need to apply masks of 16-bit color components. This will preserve * colors for terminal text. For 32-bit truecolor PMG images, we need to * translate 32-bit colors to 15/16 bit colors and this means data loss. * There are different algorithms how to perform such color space reduction, * we are currently using bitwise right shift to reduce color space and so far * this technique seems to be sufficient (see also gfx_fb_putimage(), the * end of for loop). * 32-bit to/from 8-bit is the most troublesome because 8-bit colors are * indexed. From video, we do get color indexes, and we do translate * color index values to RGB. To write to video, we again need to translate * RGB to color index. Additionally, we need to translate between VGA and * console colors. * * Our internal color data is represented using BGRA format. But the hardware * used indexed colors for 8-bit colors (0-255) and for this mode we do * need to perform translation to/from BGRA and index values. * * - paletteentry RGB <-> index - * BGRA BUFFER <----/ \ - VIDEO * \ / * - RGB (16/24/32) - * * To perform index to RGB translation, we use palette table generated * from when we set up 8-bit mode video. We cannot read palette data from * the hardware, because not all hardware supports reading it. * * BGRA to index is implemented in rgb_to_color_index() by searching * palette array for closest match of RBG values. * * Note: In 8-bit mode, We do store first 16 colors to palette registers * in VGA color order, this serves two purposes; firstly, * if palette update is not supported, we still have correct 16 colors. * Secondly, the kernel does get correct 16 colors when some other boot * loader is used. However, the palette map for 8-bit colors is using * console color ordering - this does allow us to skip translation * from VGA colors to console colors, while we are reading RGB data. */ -#include #include #include #include #include #include #include #include #include #include #include #if defined(EFI) #include #include #else #include #endif /* VGA text mode does use bold font. */ #if !defined(VGA_8X16_FONT) #define VGA_8X16_FONT "/boot/fonts/8x16b.fnt" #endif #if !defined(DEFAULT_8X16_FONT) #define DEFAULT_8X16_FONT "/boot/fonts/8x16.fnt" #endif /* * Must be sorted by font size in descending order */ font_list_t fonts = STAILQ_HEAD_INITIALIZER(fonts); #define DEFAULT_FONT_DATA font_data_8x16 extern vt_font_bitmap_data_t font_data_8x16; teken_gfx_t gfx_state = { 0 }; static struct { unsigned char r; /* Red percentage value. */ unsigned char g; /* Green percentage value. */ unsigned char b; /* Blue percentage value. */ } color_def[NCOLORS] = { {0, 0, 0}, /* black */ {50, 0, 0}, /* dark red */ {0, 50, 0}, /* dark green */ {77, 63, 0}, /* dark yellow */ {20, 40, 64}, /* dark blue */ {50, 0, 50}, /* dark magenta */ {0, 50, 50}, /* dark cyan */ {75, 75, 75}, /* light gray */ {18, 20, 21}, /* dark gray */ {100, 0, 0}, /* light red */ {0, 100, 0}, /* light green */ {100, 100, 0}, /* light yellow */ {45, 62, 81}, /* light blue */ {100, 0, 100}, /* light magenta */ {0, 100, 100}, /* light cyan */ {100, 100, 100}, /* white */ }; uint32_t cmap[NCMAP]; /* * Between console's palette and VGA's one: * - blue and red are swapped (1 <-> 4) * - yellow and cyan are swapped (3 <-> 6) */ const int cons_to_vga_colors[NCOLORS] = { 0, 4, 2, 6, 1, 5, 3, 7, 8, 12, 10, 14, 9, 13, 11, 15 }; static const int vga_to_cons_colors[NCOLORS] = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15 }; struct text_pixel *screen_buffer; #if defined(EFI) static EFI_GRAPHICS_OUTPUT_BLT_PIXEL *GlyphBuffer; #else static struct paletteentry *GlyphBuffer; #endif static size_t GlyphBufferSize; static bool insert_font(char *, FONT_FLAGS); static int font_set(struct env_var *, int, const void *); static void * allocate_glyphbuffer(uint32_t, uint32_t); static void gfx_fb_cursor_draw(teken_gfx_t *, const teken_pos_t *, bool); /* * Initialize gfx framework. */ void gfx_framework_init(void) { /* * Setup font list to have builtin font. */ (void) insert_font(NULL, FONT_BUILTIN); } static uint8_t * gfx_get_fb_address(void) { return (ptov((uint32_t)gfx_state.tg_fb.fb_addr)); } /* * Utility function to parse gfx mode line strings. */ bool gfx_parse_mode_str(char *str, int *x, int *y, int *depth) { char *p, *end; errno = 0; p = str; *x = strtoul(p, &end, 0); if (*x == 0 || errno != 0) return (false); if (*end != 'x') return (false); p = end + 1; *y = strtoul(p, &end, 0); if (*y == 0 || errno != 0) return (false); if (*end != 'x') { *depth = -1; /* auto select */ } else { p = end + 1; *depth = strtoul(p, &end, 0); if (*depth == 0 || errno != 0 || *end != '\0') return (false); } return (true); } static uint32_t rgb_color_map(uint8_t index, uint32_t rmax, int roffset, uint32_t gmax, int goffset, uint32_t bmax, int boffset) { uint32_t color, code, gray, level; if (index < NCOLORS) { #define CF(_f, _i) ((_f ## max * color_def[(_i)]._f / 100) << _f ## offset) return (CF(r, index) | CF(g, index) | CF(b, index)); #undef CF } #define CF(_f, _c) ((_f ## max & _c) << _f ## offset) /* 6x6x6 color cube */ if (index > 15 && index < 232) { uint32_t red, green, blue; for (red = 0; red < 6; red++) { for (green = 0; green < 6; green++) { for (blue = 0; blue < 6; blue++) { code = 16 + (red * 36) + (green * 6) + blue; if (code != index) continue; red = red ? (red * 40 + 55) : 0; green = green ? (green * 40 + 55) : 0; blue = blue ? (blue * 40 + 55) : 0; color = CF(r, red); color |= CF(g, green); color |= CF(b, blue); return (color); } } } } /* colors 232-255 are a grayscale ramp */ for (gray = 0; gray < 24; gray++) { level = (gray * 10) + 8; code = 232 + gray; if (code == index) break; } return (CF(r, level) | CF(g, level) | CF(b, level)); #undef CF } /* * Support for color mapping. * For 8, 24 and 32 bit depth, use mask size 8. * 15/16 bit depth needs to use mask size from mode, * or we will lose color information from 32-bit to 15/16 bit translation. */ uint32_t gfx_fb_color_map(uint8_t index) { int rmask, gmask, bmask; int roff, goff, boff, bpp; roff = ffs(gfx_state.tg_fb.fb_mask_red) - 1; goff = ffs(gfx_state.tg_fb.fb_mask_green) - 1; boff = ffs(gfx_state.tg_fb.fb_mask_blue) - 1; bpp = roundup2(gfx_state.tg_fb.fb_bpp, 8) >> 3; if (bpp == 2) rmask = gfx_state.tg_fb.fb_mask_red >> roff; else rmask = 0xff; if (bpp == 2) gmask = gfx_state.tg_fb.fb_mask_green >> goff; else gmask = 0xff; if (bpp == 2) bmask = gfx_state.tg_fb.fb_mask_blue >> boff; else bmask = 0xff; return (rgb_color_map(index, rmask, 16, gmask, 8, bmask, 0)); } /* * Get indexed color from RGB. This function is used to write data to video * memory when the adapter is set to use indexed colors. * Since UEFI does only support 32-bit colors, we do not implement it for * UEFI because there is no need for it and we do not have palette array * for UEFI. */ static uint8_t rgb_to_color_index(uint8_t r, uint8_t g, uint8_t b) { #if !defined(EFI) uint32_t color, best, dist, k; int diff; color = 0; best = 255 * 255 * 255; for (k = 0; k < NCMAP; k++) { diff = r - pe8[k].Red; dist = diff * diff; diff = g - pe8[k].Green; dist += diff * diff; diff = b - pe8[k].Blue; dist += diff * diff; /* Exact match, exit the loop */ if (dist == 0) break; if (dist < best) { color = k; best = dist; } } if (k == NCMAP) k = color; return (k); #else (void) r; (void) g; (void) b; return (0); #endif } int generate_cons_palette(uint32_t *palette, int format, uint32_t rmax, int roffset, uint32_t gmax, int goffset, uint32_t bmax, int boffset) { int i; switch (format) { case COLOR_FORMAT_VGA: for (i = 0; i < NCOLORS; i++) palette[i] = cons_to_vga_colors[i]; for (; i < NCMAP; i++) palette[i] = i; break; case COLOR_FORMAT_RGB: for (i = 0; i < NCMAP; i++) palette[i] = rgb_color_map(i, rmax, roffset, gmax, goffset, bmax, boffset); break; default: return (ENODEV); } return (0); } static void gfx_mem_wr1(uint8_t *base, size_t size, uint32_t o, uint8_t v) { if (o >= size) return; *(uint8_t *)(base + o) = v; } static void gfx_mem_wr2(uint8_t *base, size_t size, uint32_t o, uint16_t v) { if (o >= size) return; *(uint16_t *)(base + o) = v; } static void gfx_mem_wr4(uint8_t *base, size_t size, uint32_t o, uint32_t v) { if (o >= size) return; *(uint32_t *)(base + o) = v; } static int gfxfb_blt_fill(void *BltBuffer, uint32_t DestinationX, uint32_t DestinationY, uint32_t Width, uint32_t Height) { #if defined(EFI) EFI_GRAPHICS_OUTPUT_BLT_PIXEL *p; #else struct paletteentry *p; #endif uint32_t data, bpp, pitch, y, x; int roff, goff, boff; size_t size; off_t off; uint8_t *destination; if (BltBuffer == NULL) return (EINVAL); if (DestinationY + Height > gfx_state.tg_fb.fb_height) return (EINVAL); if (DestinationX + Width > gfx_state.tg_fb.fb_width) return (EINVAL); if (Width == 0 || Height == 0) return (EINVAL); p = BltBuffer; roff = ffs(gfx_state.tg_fb.fb_mask_red) - 1; goff = ffs(gfx_state.tg_fb.fb_mask_green) - 1; boff = ffs(gfx_state.tg_fb.fb_mask_blue) - 1; if (gfx_state.tg_fb.fb_bpp == 8) { data = rgb_to_color_index(p->Red, p->Green, p->Blue); } else { data = (p->Red & (gfx_state.tg_fb.fb_mask_red >> roff)) << roff; data |= (p->Green & (gfx_state.tg_fb.fb_mask_green >> goff)) << goff; data |= (p->Blue & (gfx_state.tg_fb.fb_mask_blue >> boff)) << boff; } bpp = roundup2(gfx_state.tg_fb.fb_bpp, 8) >> 3; pitch = gfx_state.tg_fb.fb_stride * bpp; destination = gfx_get_fb_address(); size = gfx_state.tg_fb.fb_size; for (y = DestinationY; y < Height + DestinationY; y++) { off = y * pitch + DestinationX * bpp; for (x = 0; x < Width; x++) { switch (bpp) { case 1: gfx_mem_wr1(destination, size, off, (data < NCOLORS) ? cons_to_vga_colors[data] : data); break; case 2: gfx_mem_wr2(destination, size, off, data); break; case 3: gfx_mem_wr1(destination, size, off, (data >> 16) & 0xff); gfx_mem_wr1(destination, size, off + 1, (data >> 8) & 0xff); gfx_mem_wr1(destination, size, off + 2, data & 0xff); break; case 4: gfx_mem_wr4(destination, size, off, data); break; default: return (EINVAL); } off += bpp; } } return (0); } static int gfxfb_blt_video_to_buffer(void *BltBuffer, uint32_t SourceX, uint32_t SourceY, uint32_t DestinationX, uint32_t DestinationY, uint32_t Width, uint32_t Height, uint32_t Delta) { #if defined(EFI) EFI_GRAPHICS_OUTPUT_BLT_PIXEL *p; #else struct paletteentry *p; #endif uint32_t x, sy, dy; uint32_t bpp, pitch, copybytes; off_t off; uint8_t *source, *destination, *sb; uint8_t rm, rp, gm, gp, bm, bp; bool bgra; if (BltBuffer == NULL) return (EINVAL); if (SourceY + Height > gfx_state.tg_fb.fb_height) return (EINVAL); if (SourceX + Width > gfx_state.tg_fb.fb_width) return (EINVAL); if (Width == 0 || Height == 0) return (EINVAL); if (Delta == 0) Delta = Width * sizeof (*p); bpp = roundup2(gfx_state.tg_fb.fb_bpp, 8) >> 3; pitch = gfx_state.tg_fb.fb_stride * bpp; copybytes = Width * bpp; rp = ffs(gfx_state.tg_fb.fb_mask_red) - 1; gp = ffs(gfx_state.tg_fb.fb_mask_green) - 1; bp = ffs(gfx_state.tg_fb.fb_mask_blue) - 1; rm = gfx_state.tg_fb.fb_mask_red >> rp; gm = gfx_state.tg_fb.fb_mask_green >> gp; bm = gfx_state.tg_fb.fb_mask_blue >> bp; /* If FB pixel format is BGRA, we can use direct copy. */ bgra = bpp == 4 && ffs(rm) - 1 == 8 && rp == 16 && ffs(gm) - 1 == 8 && gp == 8 && ffs(bm) - 1 == 8 && bp == 0; for (sy = SourceY, dy = DestinationY; dy < Height + DestinationY; sy++, dy++) { off = sy * pitch + SourceX * bpp; source = gfx_get_fb_address() + off; destination = (uint8_t *)BltBuffer + dy * Delta + DestinationX * sizeof (*p); if (bgra) { bcopy(source, destination, copybytes); } else { for (x = 0; x < Width; x++) { uint32_t c = 0; p = (void *)(destination + x * sizeof (*p)); sb = source + x * bpp; switch (bpp) { case 1: c = *sb; break; case 2: c = *(uint16_t *)sb; break; case 3: c = sb[0] << 16 | sb[1] << 8 | sb[2]; break; case 4: c = *(uint32_t *)sb; break; default: return (EINVAL); } if (bpp == 1) { *(uint32_t *)p = gfx_fb_color_map( (c < 16) ? vga_to_cons_colors[c] : c); } else { p->Red = (c >> rp) & rm; p->Green = (c >> gp) & gm; p->Blue = (c >> bp) & bm; p->Reserved = 0; } } } } return (0); } static int gfxfb_blt_buffer_to_video(void *BltBuffer, uint32_t SourceX, uint32_t SourceY, uint32_t DestinationX, uint32_t DestinationY, uint32_t Width, uint32_t Height, uint32_t Delta) { #if defined(EFI) EFI_GRAPHICS_OUTPUT_BLT_PIXEL *p; #else struct paletteentry *p; #endif uint32_t x, sy, dy; uint32_t bpp, pitch, copybytes; off_t off; uint8_t *source, *destination; uint8_t rm, rp, gm, gp, bm, bp; bool bgra; if (BltBuffer == NULL) return (EINVAL); if (DestinationY + Height > gfx_state.tg_fb.fb_height) return (EINVAL); if (DestinationX + Width > gfx_state.tg_fb.fb_width) return (EINVAL); if (Width == 0 || Height == 0) return (EINVAL); if (Delta == 0) Delta = Width * sizeof (*p); bpp = roundup2(gfx_state.tg_fb.fb_bpp, 8) >> 3; pitch = gfx_state.tg_fb.fb_stride * bpp; copybytes = Width * bpp; rp = ffs(gfx_state.tg_fb.fb_mask_red) - 1; gp = ffs(gfx_state.tg_fb.fb_mask_green) - 1; bp = ffs(gfx_state.tg_fb.fb_mask_blue) - 1; rm = gfx_state.tg_fb.fb_mask_red >> rp; gm = gfx_state.tg_fb.fb_mask_green >> gp; bm = gfx_state.tg_fb.fb_mask_blue >> bp; /* If FB pixel format is BGRA, we can use direct copy. */ bgra = bpp == 4 && ffs(rm) - 1 == 8 && rp == 16 && ffs(gm) - 1 == 8 && gp == 8 && ffs(bm) - 1 == 8 && bp == 0; for (sy = SourceY, dy = DestinationY; sy < Height + SourceY; sy++, dy++) { off = dy * pitch + DestinationX * bpp; destination = gfx_get_fb_address() + off; if (bgra) { source = (uint8_t *)BltBuffer + sy * Delta + SourceX * sizeof (*p); bcopy(source, destination, copybytes); } else { for (x = 0; x < Width; x++) { uint32_t c; p = (void *)((uint8_t *)BltBuffer + sy * Delta + (SourceX + x) * sizeof (*p)); if (bpp == 1) { c = rgb_to_color_index(p->Red, p->Green, p->Blue); } else { c = (p->Red & rm) << rp | (p->Green & gm) << gp | (p->Blue & bm) << bp; } off = x * bpp; switch (bpp) { case 1: gfx_mem_wr1(destination, copybytes, off, (c < 16) ? cons_to_vga_colors[c] : c); break; case 2: gfx_mem_wr2(destination, copybytes, off, c); break; case 3: gfx_mem_wr1(destination, copybytes, off, (c >> 16) & 0xff); gfx_mem_wr1(destination, copybytes, off + 1, (c >> 8) & 0xff); gfx_mem_wr1(destination, copybytes, off + 2, c & 0xff); break; case 4: gfx_mem_wr4(destination, copybytes, x * bpp, c); break; default: return (EINVAL); } } } } return (0); } static int gfxfb_blt_video_to_video(uint32_t SourceX, uint32_t SourceY, uint32_t DestinationX, uint32_t DestinationY, uint32_t Width, uint32_t Height) { uint32_t bpp, copybytes; int pitch; uint8_t *source, *destination; off_t off; if (SourceY + Height > gfx_state.tg_fb.fb_height) return (EINVAL); if (SourceX + Width > gfx_state.tg_fb.fb_width) return (EINVAL); if (DestinationY + Height > gfx_state.tg_fb.fb_height) return (EINVAL); if (DestinationX + Width > gfx_state.tg_fb.fb_width) return (EINVAL); if (Width == 0 || Height == 0) return (EINVAL); bpp = roundup2(gfx_state.tg_fb.fb_bpp, 8) >> 3; pitch = gfx_state.tg_fb.fb_stride * bpp; copybytes = Width * bpp; off = SourceY * pitch + SourceX * bpp; source = gfx_get_fb_address() + off; off = DestinationY * pitch + DestinationX * bpp; destination = gfx_get_fb_address() + off; if ((uintptr_t)destination > (uintptr_t)source) { source += Height * pitch; destination += Height * pitch; pitch = -pitch; } while (Height-- > 0) { bcopy(source, destination, copybytes); source += pitch; destination += pitch; } return (0); } static void gfxfb_shadow_fill(uint32_t *BltBuffer, uint32_t DestinationX, uint32_t DestinationY, uint32_t Width, uint32_t Height) { uint32_t fbX, fbY; if (gfx_state.tg_shadow_fb == NULL) return; fbX = gfx_state.tg_fb.fb_width; fbY = gfx_state.tg_fb.fb_height; if (BltBuffer == NULL) return; if (DestinationX + Width > fbX) Width = fbX - DestinationX; if (DestinationY + Height > fbY) Height = fbY - DestinationY; uint32_t y2 = Height + DestinationY; for (uint32_t y1 = DestinationY; y1 < y2; y1++) { uint32_t off = y1 * fbX + DestinationX; for (uint32_t x = 0; x < Width; x++) { gfx_state.tg_shadow_fb[off + x] = *BltBuffer; } } } int gfxfb_blt(void *BltBuffer, GFXFB_BLT_OPERATION BltOperation, uint32_t SourceX, uint32_t SourceY, uint32_t DestinationX, uint32_t DestinationY, uint32_t Width, uint32_t Height, uint32_t Delta) { int rv; #if defined(EFI) EFI_STATUS status; EFI_GRAPHICS_OUTPUT *gop = gfx_state.tg_private; EFI_TPL tpl; /* * We assume Blt() does work, if not, we will need to build exception * list case by case. We only have boot services during part of our * exectution. Once terminate boot services, these operations cannot be * done as they are provided by protocols that disappear when exit * boot services. */ if (gop != NULL && boot_services_active) { tpl = BS->RaiseTPL(TPL_NOTIFY); switch (BltOperation) { case GfxFbBltVideoFill: gfxfb_shadow_fill(BltBuffer, DestinationX, DestinationY, Width, Height); status = gop->Blt(gop, BltBuffer, EfiBltVideoFill, SourceX, SourceY, DestinationX, DestinationY, Width, Height, Delta); break; case GfxFbBltVideoToBltBuffer: status = gop->Blt(gop, BltBuffer, EfiBltVideoToBltBuffer, SourceX, SourceY, DestinationX, DestinationY, Width, Height, Delta); break; case GfxFbBltBufferToVideo: status = gop->Blt(gop, BltBuffer, EfiBltBufferToVideo, SourceX, SourceY, DestinationX, DestinationY, Width, Height, Delta); break; case GfxFbBltVideoToVideo: status = gop->Blt(gop, BltBuffer, EfiBltVideoToVideo, SourceX, SourceY, DestinationX, DestinationY, Width, Height, Delta); break; default: status = EFI_INVALID_PARAMETER; break; } switch (status) { case EFI_SUCCESS: rv = 0; break; case EFI_INVALID_PARAMETER: rv = EINVAL; break; case EFI_DEVICE_ERROR: default: rv = EIO; break; } BS->RestoreTPL(tpl); return (rv); } #endif switch (BltOperation) { case GfxFbBltVideoFill: gfxfb_shadow_fill(BltBuffer, DestinationX, DestinationY, Width, Height); rv = gfxfb_blt_fill(BltBuffer, DestinationX, DestinationY, Width, Height); break; case GfxFbBltVideoToBltBuffer: rv = gfxfb_blt_video_to_buffer(BltBuffer, SourceX, SourceY, DestinationX, DestinationY, Width, Height, Delta); break; case GfxFbBltBufferToVideo: rv = gfxfb_blt_buffer_to_video(BltBuffer, SourceX, SourceY, DestinationX, DestinationY, Width, Height, Delta); break; case GfxFbBltVideoToVideo: rv = gfxfb_blt_video_to_video(SourceX, SourceY, DestinationX, DestinationY, Width, Height); break; default: rv = EINVAL; break; } return (rv); } void gfx_bitblt_bitmap(teken_gfx_t *state, const uint8_t *glyph, const teken_attr_t *a, uint32_t alpha, bool cursor) { uint32_t width, height; uint32_t fgc, bgc, bpl, cc, o; int bpp, bit, byte; bool invert = false; bpp = 4; /* We only generate BGRA */ width = state->tg_font.vf_width; height = state->tg_font.vf_height; bpl = (width + 7) / 8; /* Bytes per source line. */ fgc = a->ta_fgcolor; bgc = a->ta_bgcolor; if (a->ta_format & TF_BOLD) fgc |= TC_LIGHT; if (a->ta_format & TF_BLINK) bgc |= TC_LIGHT; fgc = gfx_fb_color_map(fgc); bgc = gfx_fb_color_map(bgc); if (a->ta_format & TF_REVERSE) invert = !invert; if (cursor) invert = !invert; if (invert) { uint32_t tmp; tmp = fgc; fgc = bgc; bgc = tmp; } alpha = alpha << 24; fgc |= alpha; bgc |= alpha; for (uint32_t y = 0; y < height; y++) { for (uint32_t x = 0; x < width; x++) { byte = y * bpl + x / 8; bit = 0x80 >> (x % 8); o = y * width * bpp + x * bpp; cc = glyph[byte] & bit ? fgc : bgc; gfx_mem_wr4(state->tg_glyph, state->tg_glyph_size, o, cc); } } } /* * Draw prepared glyph on terminal point p. */ static void gfx_fb_printchar(teken_gfx_t *state, const teken_pos_t *p) { unsigned x, y, width, height; width = state->tg_font.vf_width; height = state->tg_font.vf_height; x = state->tg_origin.tp_col + p->tp_col * width; y = state->tg_origin.tp_row + p->tp_row * height; gfx_fb_cons_display(x, y, width, height, state->tg_glyph); } /* * Store char with its attribute to buffer and put it on screen. */ void gfx_fb_putchar(void *arg, const teken_pos_t *p, teken_char_t c, const teken_attr_t *a) { teken_gfx_t *state = arg; const uint8_t *glyph; int idx; idx = p->tp_col + p->tp_row * state->tg_tp.tp_col; if (idx >= state->tg_tp.tp_col * state->tg_tp.tp_row) return; /* remove the cursor */ if (state->tg_cursor_visible) gfx_fb_cursor_draw(state, &state->tg_cursor, false); screen_buffer[idx].c = c; screen_buffer[idx].a = *a; glyph = font_lookup(&state->tg_font, c, a); gfx_bitblt_bitmap(state, glyph, a, 0xff, false); gfx_fb_printchar(state, p); /* display the cursor */ if (state->tg_cursor_visible) { const teken_pos_t *c; c = teken_get_cursor(&state->tg_teken); gfx_fb_cursor_draw(state, c, true); } } void gfx_fb_fill(void *arg, const teken_rect_t *r, teken_char_t c, const teken_attr_t *a) { teken_gfx_t *state = arg; const uint8_t *glyph; teken_pos_t p; struct text_pixel *row; /* remove the cursor */ if (state->tg_cursor_visible) gfx_fb_cursor_draw(state, &state->tg_cursor, false); glyph = font_lookup(&state->tg_font, c, a); gfx_bitblt_bitmap(state, glyph, a, 0xff, false); for (p.tp_row = r->tr_begin.tp_row; p.tp_row < r->tr_end.tp_row; p.tp_row++) { row = &screen_buffer[p.tp_row * state->tg_tp.tp_col]; for (p.tp_col = r->tr_begin.tp_col; p.tp_col < r->tr_end.tp_col; p.tp_col++) { row[p.tp_col].c = c; row[p.tp_col].a = *a; gfx_fb_printchar(state, &p); } } /* display the cursor */ if (state->tg_cursor_visible) { const teken_pos_t *c; c = teken_get_cursor(&state->tg_teken); gfx_fb_cursor_draw(state, c, true); } } static void gfx_fb_cursor_draw(teken_gfx_t *state, const teken_pos_t *pos, bool on) { const uint8_t *glyph; teken_pos_t p; int idx; p = *pos; if (p.tp_col >= state->tg_tp.tp_col) p.tp_col = state->tg_tp.tp_col - 1; if (p.tp_row >= state->tg_tp.tp_row) p.tp_row = state->tg_tp.tp_row - 1; idx = p.tp_col + p.tp_row * state->tg_tp.tp_col; if (idx >= state->tg_tp.tp_col * state->tg_tp.tp_row) return; glyph = font_lookup(&state->tg_font, screen_buffer[idx].c, &screen_buffer[idx].a); gfx_bitblt_bitmap(state, glyph, &screen_buffer[idx].a, 0xff, on); gfx_fb_printchar(state, &p); state->tg_cursor = p; } void gfx_fb_cursor(void *arg, const teken_pos_t *p) { teken_gfx_t *state = arg; /* Switch cursor off in old location and back on in new. */ if (state->tg_cursor_visible) { gfx_fb_cursor_draw(state, &state->tg_cursor, false); gfx_fb_cursor_draw(state, p, true); } } void gfx_fb_param(void *arg, int cmd, unsigned int value) { teken_gfx_t *state = arg; const teken_pos_t *c; switch (cmd) { case TP_SETLOCALCURSOR: /* * 0 means normal (usually block), 1 means hidden, and * 2 means blinking (always block) for compatibility with * syscons. We don't support any changes except hiding, * so must map 2 to 0. */ value = (value == 1) ? 0 : 1; /* FALLTHROUGH */ case TP_SHOWCURSOR: c = teken_get_cursor(&state->tg_teken); gfx_fb_cursor_draw(state, c, true); if (value != 0) state->tg_cursor_visible = true; else state->tg_cursor_visible = false; break; default: /* Not yet implemented */ break; } } bool is_same_pixel(struct text_pixel *px1, struct text_pixel *px2) { if (px1->c != px2->c) return (false); /* Is there image stored? */ if ((px1->a.ta_format & TF_IMAGE) || (px2->a.ta_format & TF_IMAGE)) return (false); if (px1->a.ta_format != px2->a.ta_format) return (false); if (px1->a.ta_fgcolor != px2->a.ta_fgcolor) return (false); if (px1->a.ta_bgcolor != px2->a.ta_bgcolor) return (false); return (true); } static void gfx_fb_copy_area(teken_gfx_t *state, const teken_rect_t *s, const teken_pos_t *d) { uint32_t sx, sy, dx, dy, width, height; uint32_t pitch, bytes; int step; width = state->tg_font.vf_width; height = state->tg_font.vf_height; sx = s->tr_begin.tp_col * width; sy = s->tr_begin.tp_row * height; dx = d->tp_col * width; dy = d->tp_row * height; width *= (s->tr_end.tp_col - s->tr_begin.tp_col + 1); /* * With no shadow fb, use video to video copy. */ if (state->tg_shadow_fb == NULL) { (void) gfxfb_blt(NULL, GfxFbBltVideoToVideo, sx + state->tg_origin.tp_col, sy + state->tg_origin.tp_row, dx + state->tg_origin.tp_col, dy + state->tg_origin.tp_row, width, height, 0); return; } /* * With shadow fb, we need to copy data on both shadow and video, * to preserve the consistency. We only read data from shadow fb. */ step = 1; pitch = state->tg_fb.fb_width; bytes = width * sizeof (*state->tg_shadow_fb); /* * To handle overlapping areas, set up reverse copy here. */ if (dy * pitch + dx > sy * pitch + sx) { sy += height; dy += height; step = -step; } while (height-- > 0) { uint32_t *source = &state->tg_shadow_fb[sy * pitch + sx]; uint32_t *destination = &state->tg_shadow_fb[dy * pitch + dx]; bcopy(source, destination, bytes); (void) gfxfb_blt(destination, GfxFbBltBufferToVideo, 0, 0, dx + state->tg_origin.tp_col, dy + state->tg_origin.tp_row, width, 1, 0); sy += step; dy += step; } } static void gfx_fb_copy_line(teken_gfx_t *state, int ncol, teken_pos_t *s, teken_pos_t *d) { teken_rect_t sr; teken_pos_t dp; unsigned soffset, doffset; bool mark = false; int x; soffset = s->tp_col + s->tp_row * state->tg_tp.tp_col; doffset = d->tp_col + d->tp_row * state->tg_tp.tp_col; for (x = 0; x < ncol; x++) { if (is_same_pixel(&screen_buffer[soffset + x], &screen_buffer[doffset + x])) { if (mark) { gfx_fb_copy_area(state, &sr, &dp); mark = false; } } else { screen_buffer[doffset + x] = screen_buffer[soffset + x]; if (mark) { /* update end point */ sr.tr_end.tp_col = s->tp_col + x; } else { /* set up new rectangle */ mark = true; sr.tr_begin.tp_col = s->tp_col + x; sr.tr_begin.tp_row = s->tp_row; sr.tr_end.tp_col = s->tp_col + x; sr.tr_end.tp_row = s->tp_row; dp.tp_col = d->tp_col + x; dp.tp_row = d->tp_row; } } } if (mark) { gfx_fb_copy_area(state, &sr, &dp); } } void gfx_fb_copy(void *arg, const teken_rect_t *r, const teken_pos_t *p) { teken_gfx_t *state = arg; unsigned doffset, soffset; teken_pos_t d, s; int nrow, ncol, y; /* Has to be signed - >= 0 comparison */ /* * Copying is a little tricky. We must make sure we do it in * correct order, to make sure we don't overwrite our own data. */ nrow = r->tr_end.tp_row - r->tr_begin.tp_row; ncol = r->tr_end.tp_col - r->tr_begin.tp_col; if (p->tp_row + nrow > state->tg_tp.tp_row || p->tp_col + ncol > state->tg_tp.tp_col) return; soffset = r->tr_begin.tp_col + r->tr_begin.tp_row * state->tg_tp.tp_col; doffset = p->tp_col + p->tp_row * state->tg_tp.tp_col; /* remove the cursor */ if (state->tg_cursor_visible) gfx_fb_cursor_draw(state, &state->tg_cursor, false); /* * Copy line by line. */ if (doffset <= soffset) { s = r->tr_begin; d = *p; for (y = 0; y < nrow; y++) { s.tp_row = r->tr_begin.tp_row + y; d.tp_row = p->tp_row + y; gfx_fb_copy_line(state, ncol, &s, &d); } } else { for (y = nrow - 1; y >= 0; y--) { s.tp_row = r->tr_begin.tp_row + y; d.tp_row = p->tp_row + y; gfx_fb_copy_line(state, ncol, &s, &d); } } /* display the cursor */ if (state->tg_cursor_visible) { const teken_pos_t *c; c = teken_get_cursor(&state->tg_teken); gfx_fb_cursor_draw(state, c, true); } } /* * Implements alpha blending for RGBA data, could use pixels for arguments, * but byte stream seems more generic. * The generic alpha blending is: * blend = alpha * fg + (1.0 - alpha) * bg. * Since our alpha is not from range [0..1], we scale appropriately. */ static uint8_t alpha_blend(uint8_t fg, uint8_t bg, uint8_t alpha) { uint16_t blend, h, l; /* trivial corner cases */ if (alpha == 0) return (bg); if (alpha == 0xFF) return (fg); blend = (alpha * fg + (0xFF - alpha) * bg); /* Division by 0xFF */ h = blend >> 8; l = blend & 0xFF; if (h + l >= 0xFF) h++; return (h); } /* * Implements alpha blending for RGBA data, could use pixels for arguments, * but byte stream seems more generic. * The generic alpha blending is: * blend = alpha * fg + (1.0 - alpha) * bg. * Since our alpha is not from range [0..1], we scale appropriately. */ static void bitmap_cpy(void *dst, void *src, uint32_t size) { #if defined(EFI) EFI_GRAPHICS_OUTPUT_BLT_PIXEL *ps, *pd; #else struct paletteentry *ps, *pd; #endif uint32_t i; uint8_t a; ps = src; pd = dst; /* * we only implement alpha blending for depth 32. */ for (i = 0; i < size; i ++) { a = ps[i].Reserved; pd[i].Red = alpha_blend(ps[i].Red, pd[i].Red, a); pd[i].Green = alpha_blend(ps[i].Green, pd[i].Green, a); pd[i].Blue = alpha_blend(ps[i].Blue, pd[i].Blue, a); pd[i].Reserved = a; } } static void * allocate_glyphbuffer(uint32_t width, uint32_t height) { size_t size; size = sizeof (*GlyphBuffer) * width * height; if (size != GlyphBufferSize) { free(GlyphBuffer); GlyphBuffer = malloc(size); if (GlyphBuffer == NULL) return (NULL); GlyphBufferSize = size; } return (GlyphBuffer); } void gfx_fb_cons_display(uint32_t x, uint32_t y, uint32_t width, uint32_t height, void *data) { #if defined(EFI) EFI_GRAPHICS_OUTPUT_BLT_PIXEL *buf, *p; #else struct paletteentry *buf, *p; #endif size_t size; /* * If we do have shadow fb, we will use shadow to render data, * and copy shadow to video. */ if (gfx_state.tg_shadow_fb != NULL) { uint32_t pitch = gfx_state.tg_fb.fb_width; /* Copy rectangle line by line. */ p = data; for (uint32_t sy = 0; sy < height; sy++) { buf = (void *)(gfx_state.tg_shadow_fb + (y - gfx_state.tg_origin.tp_row) * pitch + x - gfx_state.tg_origin.tp_col); bitmap_cpy(buf, &p[sy * width], width); (void) gfxfb_blt(buf, GfxFbBltBufferToVideo, 0, 0, x, y, width, 1, 0); y++; } return; } /* * Common data to display is glyph, use preallocated * glyph buffer. */ if (gfx_state.tg_glyph_size != GlyphBufferSize) (void) allocate_glyphbuffer(width, height); size = width * height * sizeof(*buf); if (size == GlyphBufferSize) buf = GlyphBuffer; else buf = malloc(size); if (buf == NULL) return; if (gfxfb_blt(buf, GfxFbBltVideoToBltBuffer, x, y, 0, 0, width, height, 0) == 0) { bitmap_cpy(buf, data, width * height); (void) gfxfb_blt(buf, GfxFbBltBufferToVideo, 0, 0, x, y, width, height, 0); } if (buf != GlyphBuffer) free(buf); } /* * Public graphics primitives. */ static int isqrt(int num) { int res = 0; int bit = 1 << 30; /* "bit" starts at the highest power of four <= the argument. */ while (bit > num) bit >>= 2; while (bit != 0) { if (num >= res + bit) { num -= res + bit; res = (res >> 1) + bit; } else { res >>= 1; } bit >>= 2; } return (res); } static uint32_t gfx_fb_getcolor(void) { uint32_t c; const teken_attr_t *ap; ap = teken_get_curattr(&gfx_state.tg_teken); if (ap->ta_format & TF_REVERSE) { c = ap->ta_bgcolor; if (ap->ta_format & TF_BLINK) c |= TC_LIGHT; } else { c = ap->ta_fgcolor; if (ap->ta_format & TF_BOLD) c |= TC_LIGHT; } return (gfx_fb_color_map(c)); } /* set pixel in framebuffer using gfx coordinates */ void gfx_fb_setpixel(uint32_t x, uint32_t y) { uint32_t c; if (gfx_state.tg_fb_type == FB_TEXT) return; c = gfx_fb_getcolor(); if (x >= gfx_state.tg_fb.fb_width || y >= gfx_state.tg_fb.fb_height) return; gfxfb_blt(&c, GfxFbBltVideoFill, 0, 0, x, y, 1, 1, 0); } /* * draw rectangle in framebuffer using gfx coordinates. */ void gfx_fb_drawrect(uint32_t x1, uint32_t y1, uint32_t x2, uint32_t y2, uint32_t fill) { uint32_t c; if (gfx_state.tg_fb_type == FB_TEXT) return; c = gfx_fb_getcolor(); if (fill != 0) { gfxfb_blt(&c, GfxFbBltVideoFill, 0, 0, x1, y1, x2 - x1, y2 - y1, 0); } else { gfxfb_blt(&c, GfxFbBltVideoFill, 0, 0, x1, y1, x2 - x1, 1, 0); gfxfb_blt(&c, GfxFbBltVideoFill, 0, 0, x1, y2, x2 - x1, 1, 0); gfxfb_blt(&c, GfxFbBltVideoFill, 0, 0, x1, y1, 1, y2 - y1, 0); gfxfb_blt(&c, GfxFbBltVideoFill, 0, 0, x2, y1, 1, y2 - y1, 0); } } void gfx_fb_line(uint32_t x0, uint32_t y0, uint32_t x1, uint32_t y1, uint32_t wd) { int dx, sx, dy, sy; int err, e2, x2, y2, ed, width; if (gfx_state.tg_fb_type == FB_TEXT) return; width = wd; sx = x0 < x1? 1 : -1; sy = y0 < y1? 1 : -1; dx = x1 > x0? x1 - x0 : x0 - x1; dy = y1 > y0? y1 - y0 : y0 - y1; err = dx + dy; ed = dx + dy == 0 ? 1: isqrt(dx * dx + dy * dy); for (;;) { gfx_fb_setpixel(x0, y0); e2 = err; x2 = x0; if ((e2 << 1) >= -dx) { /* x step */ e2 += dy; y2 = y0; while (e2 < ed * width && (y1 != (uint32_t)y2 || dx > dy)) { y2 += sy; gfx_fb_setpixel(x0, y2); e2 += dx; } if (x0 == x1) break; e2 = err; err -= dy; x0 += sx; } if ((e2 << 1) <= dy) { /* y step */ e2 = dx-e2; while (e2 < ed * width && (x1 != (uint32_t)x2 || dx < dy)) { x2 += sx; gfx_fb_setpixel(x2, y0); e2 += dy; } if (y0 == y1) break; err += dx; y0 += sy; } } } /* * quadratic Bézier curve limited to gradients without sign change. */ void gfx_fb_bezier(uint32_t x0, uint32_t y0, uint32_t x1, uint32_t y1, uint32_t x2, uint32_t y2, uint32_t wd) { int sx, sy, xx, yy, xy, width; int dx, dy, err, curvature; int i; if (gfx_state.tg_fb_type == FB_TEXT) return; width = wd; sx = x2 - x1; sy = y2 - y1; xx = x0 - x1; yy = y0 - y1; curvature = xx*sy - yy*sx; if (sx*sx + sy*sy > xx*xx+yy*yy) { x2 = x0; x0 = sx + x1; y2 = y0; y0 = sy + y1; curvature = -curvature; } if (curvature != 0) { xx += sx; sx = x0 < x2? 1 : -1; xx *= sx; yy += sy; sy = y0 < y2? 1 : -1; yy *= sy; xy = (xx*yy) << 1; xx *= xx; yy *= yy; if (curvature * sx * sy < 0) { xx = -xx; yy = -yy; xy = -xy; curvature = -curvature; } dx = 4 * sy * curvature * (x1 - x0) + xx - xy; dy = 4 * sx * curvature * (y0 - y1) + yy - xy; xx += xx; yy += yy; err = dx + dy + xy; do { for (i = 0; i <= width; i++) gfx_fb_setpixel(x0 + i, y0); if (x0 == x2 && y0 == y2) return; /* last pixel -> curve finished */ y1 = 2 * err < dx; if (2 * err > dy) { x0 += sx; dx -= xy; dy += yy; err += dy; } if (y1 != 0) { y0 += sy; dy -= xy; dx += xx; err += dx; } } while (dy < dx); /* gradient negates -> algorithm fails */ } gfx_fb_line(x0, y0, x2, y2, width); } /* * draw rectangle using terminal coordinates and current foreground color. */ void gfx_term_drawrect(uint32_t ux1, uint32_t uy1, uint32_t ux2, uint32_t uy2) { int x1, y1, x2, y2; int xshift, yshift; int width, i; uint32_t vf_width, vf_height; teken_rect_t r; if (gfx_state.tg_fb_type == FB_TEXT) return; vf_width = gfx_state.tg_font.vf_width; vf_height = gfx_state.tg_font.vf_height; width = vf_width / 4; /* line width */ xshift = (vf_width - width) / 2; yshift = (vf_height - width) / 2; /* Shift coordinates */ if (ux1 != 0) ux1--; if (uy1 != 0) uy1--; ux2--; uy2--; /* mark area used in terminal */ r.tr_begin.tp_col = ux1; r.tr_begin.tp_row = uy1; r.tr_end.tp_col = ux2 + 1; r.tr_end.tp_row = uy2 + 1; term_image_display(&gfx_state, &r); /* * Draw horizontal lines width points thick, shifted from outer edge. */ x1 = (ux1 + 1) * vf_width + gfx_state.tg_origin.tp_col; y1 = uy1 * vf_height + gfx_state.tg_origin.tp_row + yshift; x2 = ux2 * vf_width + gfx_state.tg_origin.tp_col; gfx_fb_drawrect(x1, y1, x2, y1 + width, 1); y2 = uy2 * vf_height + gfx_state.tg_origin.tp_row; y2 += vf_height - yshift - width; gfx_fb_drawrect(x1, y2, x2, y2 + width, 1); /* * Draw vertical lines width points thick, shifted from outer edge. */ x1 = ux1 * vf_width + gfx_state.tg_origin.tp_col + xshift; y1 = uy1 * vf_height + gfx_state.tg_origin.tp_row; y1 += vf_height; y2 = uy2 * vf_height + gfx_state.tg_origin.tp_row; gfx_fb_drawrect(x1, y1, x1 + width, y2, 1); x1 = ux2 * vf_width + gfx_state.tg_origin.tp_col; x1 += vf_width - xshift - width; gfx_fb_drawrect(x1, y1, x1 + width, y2, 1); /* Draw upper left corner. */ x1 = ux1 * vf_width + gfx_state.tg_origin.tp_col + xshift; y1 = uy1 * vf_height + gfx_state.tg_origin.tp_row; y1 += vf_height; x2 = ux1 * vf_width + gfx_state.tg_origin.tp_col; x2 += vf_width; y2 = uy1 * vf_height + gfx_state.tg_origin.tp_row + yshift; for (i = 0; i <= width; i++) gfx_fb_bezier(x1 + i, y1, x1 + i, y2 + i, x2, y2 + i, width-i); /* Draw lower left corner. */ x1 = ux1 * vf_width + gfx_state.tg_origin.tp_col; x1 += vf_width; y1 = uy2 * vf_height + gfx_state.tg_origin.tp_row; y1 += vf_height - yshift; x2 = ux1 * vf_width + gfx_state.tg_origin.tp_col + xshift; y2 = uy2 * vf_height + gfx_state.tg_origin.tp_row; for (i = 0; i <= width; i++) gfx_fb_bezier(x1, y1 - i, x2 + i, y1 - i, x2 + i, y2, width-i); /* Draw upper right corner. */ x1 = ux2 * vf_width + gfx_state.tg_origin.tp_col; y1 = uy1 * vf_height + gfx_state.tg_origin.tp_row + yshift; x2 = ux2 * vf_width + gfx_state.tg_origin.tp_col; x2 += vf_width - xshift - width; y2 = uy1 * vf_height + gfx_state.tg_origin.tp_row; y2 += vf_height; for (i = 0; i <= width; i++) gfx_fb_bezier(x1, y1 + i, x2 + i, y1 + i, x2 + i, y2, width-i); /* Draw lower right corner. */ x1 = ux2 * vf_width + gfx_state.tg_origin.tp_col; y1 = uy2 * vf_height + gfx_state.tg_origin.tp_row; y1 += vf_height - yshift; x2 = ux2 * vf_width + gfx_state.tg_origin.tp_col; x2 += vf_width - xshift - width; y2 = uy2 * vf_height + gfx_state.tg_origin.tp_row; for (i = 0; i <= width; i++) gfx_fb_bezier(x1, y1 - i, x2 + i, y1 - i, x2 + i, y2, width-i); } int gfx_fb_putimage(png_t *png, uint32_t ux1, uint32_t uy1, uint32_t ux2, uint32_t uy2, uint32_t flags) { #if defined(EFI) EFI_GRAPHICS_OUTPUT_BLT_PIXEL *p; #else struct paletteentry *p; #endif uint8_t *data; uint32_t i, j, x, y, fheight, fwidth; int rs, gs, bs; uint8_t r, g, b, a; bool scale = false; bool trace = false; teken_rect_t rect; trace = (flags & FL_PUTIMAGE_DEBUG) != 0; if (gfx_state.tg_fb_type == FB_TEXT) { if (trace) printf("Framebuffer not active.\n"); return (1); } if (png->color_type != PNG_TRUECOLOR_ALPHA) { if (trace) printf("Not truecolor image.\n"); return (1); } if (ux1 > gfx_state.tg_fb.fb_width || uy1 > gfx_state.tg_fb.fb_height) { if (trace) printf("Top left coordinate off screen.\n"); return (1); } if (png->width > UINT16_MAX || png->height > UINT16_MAX) { if (trace) printf("Image too large.\n"); return (1); } if (png->width < 1 || png->height < 1) { if (trace) printf("Image too small.\n"); return (1); } /* * If 0 was passed for either ux2 or uy2, then calculate the missing * part of the bottom right coordinate. */ scale = true; if (ux2 == 0 && uy2 == 0) { /* Both 0, use the native resolution of the image */ ux2 = ux1 + png->width; uy2 = uy1 + png->height; scale = false; } else if (ux2 == 0) { /* Set ux2 from uy2/uy1 to maintain aspect ratio */ ux2 = ux1 + (png->width * (uy2 - uy1)) / png->height; } else if (uy2 == 0) { /* Set uy2 from ux2/ux1 to maintain aspect ratio */ uy2 = uy1 + (png->height * (ux2 - ux1)) / png->width; } if (ux2 > gfx_state.tg_fb.fb_width || uy2 > gfx_state.tg_fb.fb_height) { if (trace) printf("Bottom right coordinate off screen.\n"); return (1); } fwidth = ux2 - ux1; fheight = uy2 - uy1; /* * If the original image dimensions have been passed explicitly, * disable scaling. */ if (fwidth == png->width && fheight == png->height) scale = false; if (ux1 == 0) { /* * No top left X co-ordinate (real coordinates start at 1), * place as far right as it will fit. */ ux2 = gfx_state.tg_fb.fb_width - gfx_state.tg_origin.tp_col; ux1 = ux2 - fwidth; } if (uy1 == 0) { /* * No top left Y co-ordinate (real coordinates start at 1), * place as far down as it will fit. */ uy2 = gfx_state.tg_fb.fb_height - gfx_state.tg_origin.tp_row; uy1 = uy2 - fheight; } if (ux1 >= ux2 || uy1 >= uy2) { if (trace) printf("Image dimensions reversed.\n"); return (1); } if (fwidth < 2 || fheight < 2) { if (trace) printf("Target area too small\n"); return (1); } if (trace) printf("Image %ux%u -> %ux%u @%ux%u\n", png->width, png->height, fwidth, fheight, ux1, uy1); rect.tr_begin.tp_col = ux1 / gfx_state.tg_font.vf_width; rect.tr_begin.tp_row = uy1 / gfx_state.tg_font.vf_height; rect.tr_end.tp_col = (ux1 + fwidth) / gfx_state.tg_font.vf_width; rect.tr_end.tp_row = (uy1 + fheight) / gfx_state.tg_font.vf_height; /* * mark area used in terminal */ if (!(flags & FL_PUTIMAGE_NOSCROLL)) term_image_display(&gfx_state, &rect); if ((flags & FL_PUTIMAGE_BORDER)) gfx_fb_drawrect(ux1, uy1, ux2, uy2, 0); data = malloc(fwidth * fheight * sizeof(*p)); p = (void *)data; if (data == NULL) { if (trace) printf("Out of memory.\n"); return (1); } /* * Build image for our framebuffer. */ /* Helper to calculate the pixel index from the source png */ #define GETPIXEL(xx, yy) (((yy) * png->width + (xx)) * png->bpp) /* * For each of the x and y directions, calculate the number of pixels * in the source image that correspond to a single pixel in the target. * Use fixed-point arithmetic with 16-bits for each of the integer and * fractional parts. */ const uint32_t wcstep = ((png->width - 1) << 16) / (fwidth - 1); const uint32_t hcstep = ((png->height - 1) << 16) / (fheight - 1); rs = 8 - (fls(gfx_state.tg_fb.fb_mask_red) - ffs(gfx_state.tg_fb.fb_mask_red) + 1); gs = 8 - (fls(gfx_state.tg_fb.fb_mask_green) - ffs(gfx_state.tg_fb.fb_mask_green) + 1); bs = 8 - (fls(gfx_state.tg_fb.fb_mask_blue) - ffs(gfx_state.tg_fb.fb_mask_blue) + 1); uint32_t hc = 0; for (y = 0; y < fheight; y++) { uint32_t hc2 = (hc >> 9) & 0x7f; uint32_t hc1 = 0x80 - hc2; uint32_t offset_y = hc >> 16; uint32_t offset_y1 = offset_y + 1; uint32_t wc = 0; for (x = 0; x < fwidth; x++) { uint32_t wc2 = (wc >> 9) & 0x7f; uint32_t wc1 = 0x80 - wc2; uint32_t offset_x = wc >> 16; uint32_t offset_x1 = offset_x + 1; /* Target pixel index */ j = y * fwidth + x; if (!scale) { i = GETPIXEL(x, y); r = png->image[i]; g = png->image[i + 1]; b = png->image[i + 2]; a = png->image[i + 3]; } else { uint8_t pixel[4]; uint32_t p00 = GETPIXEL(offset_x, offset_y); uint32_t p01 = GETPIXEL(offset_x, offset_y1); uint32_t p10 = GETPIXEL(offset_x1, offset_y); uint32_t p11 = GETPIXEL(offset_x1, offset_y1); /* * Given a 2x2 array of pixels in the source * image, combine them to produce a single * value for the pixel in the target image. * Each column of pixels is combined using * a weighted average where the top and bottom * pixels contribute hc1 and hc2 respectively. * The calculation for bottom pixel pB and * top pixel pT is: * (pT * hc1 + pB * hc2) / (hc1 + hc2) * Once the values are determined for the two * columns of pixels, then the columns are * averaged together in the same way but using * wc1 and wc2 for the weightings. * * Since hc1 and hc2 are chosen so that * hc1 + hc2 == 128 (and same for wc1 + wc2), * the >> 14 below is a quick way to divide by * (hc1 + hc2) * (wc1 + wc2) */ for (i = 0; i < 4; i++) pixel[i] = ( (png->image[p00 + i] * hc1 + png->image[p01 + i] * hc2) * wc1 + (png->image[p10 + i] * hc1 + png->image[p11 + i] * hc2) * wc2) >> 14; r = pixel[0]; g = pixel[1]; b = pixel[2]; a = pixel[3]; } if (trace) printf("r/g/b: %x/%x/%x\n", r, g, b); /* * Rough colorspace reduction for 15/16 bit colors. */ p[j].Red = r >> rs; p[j].Green = g >> gs; p[j].Blue = b >> bs; p[j].Reserved = a; wc += wcstep; } hc += hcstep; } gfx_fb_cons_display(ux1, uy1, fwidth, fheight, data); free(data); return (0); } /* * Reset font flags to FONT_AUTO. */ void reset_font_flags(void) { struct fontlist *fl; STAILQ_FOREACH(fl, &fonts, font_next) { fl->font_flags = FONT_AUTO; } } /* Return w^2 + h^2 or 0, if the dimensions are unknown */ static unsigned edid_diagonal_squared(void) { unsigned w, h; if (edid_info == NULL) return (0); w = edid_info->display.max_horizontal_image_size; h = edid_info->display.max_vertical_image_size; /* If either one is 0, we have aspect ratio, not size */ if (w == 0 || h == 0) return (0); /* * some monitors encode the aspect ratio instead of the physical size. */ if ((w == 16 && h == 9) || (w == 16 && h == 10) || (w == 4 && h == 3) || (w == 5 && h == 4)) return (0); /* * translate cm to inch, note we scale by 100 here. */ w = w * 100 / 254; h = h * 100 / 254; /* Return w^2 + h^2 */ return (w * w + h * h); } /* * calculate pixels per inch. */ static unsigned gfx_get_ppi(void) { unsigned dp, di; di = edid_diagonal_squared(); if (di == 0) return (0); dp = gfx_state.tg_fb.fb_width * gfx_state.tg_fb.fb_width + gfx_state.tg_fb.fb_height * gfx_state.tg_fb.fb_height; return (isqrt(dp / di)); } /* * Calculate font size from density independent pixels (dp): * ((16dp * ppi) / 160) * display_factor. * Here we are using fixed constants: 1dp == 160 ppi and * display_factor 2. * * We are rounding font size up and are searching for font which is * not smaller than calculated size value. */ static vt_font_bitmap_data_t * gfx_get_font(void) { unsigned ppi, size; vt_font_bitmap_data_t *font = NULL; struct fontlist *fl, *next; /* Text mode is not supported here. */ if (gfx_state.tg_fb_type == FB_TEXT) return (NULL); ppi = gfx_get_ppi(); if (ppi == 0) return (NULL); /* * We will search for 16dp font. * We are using scale up by 10 for roundup. */ size = (16 * ppi * 10) / 160; /* Apply display factor 2. */ size = roundup(size * 2, 10) / 10; STAILQ_FOREACH(fl, &fonts, font_next) { next = STAILQ_NEXT(fl, font_next); /* * If this is last font or, if next font is smaller, * we have our font. Make sure, it actually is loaded. */ if (next == NULL || next->font_data->vfbd_height < size) { font = fl->font_data; if (font->vfbd_font == NULL || fl->font_flags == FONT_RELOAD) { if (fl->font_load != NULL && fl->font_name != NULL) font = fl->font_load(fl->font_name); } break; } } return (font); } static vt_font_bitmap_data_t * set_font(teken_unit_t *rows, teken_unit_t *cols, teken_unit_t h, teken_unit_t w) { vt_font_bitmap_data_t *font = NULL; struct fontlist *fl; unsigned height = h; unsigned width = w; /* * First check for manually loaded font. */ STAILQ_FOREACH(fl, &fonts, font_next) { if (fl->font_flags == FONT_MANUAL) { font = fl->font_data; if (font->vfbd_font == NULL && fl->font_load != NULL && fl->font_name != NULL) { font = fl->font_load(fl->font_name); } if (font == NULL || font->vfbd_font == NULL) font = NULL; break; } } if (font == NULL) font = gfx_get_font(); if (font != NULL) { *rows = height / font->vfbd_height; *cols = width / font->vfbd_width; return (font); } /* * Find best font for these dimensions, or use default. * If height >= VT_FB_MAX_HEIGHT and width >= VT_FB_MAX_WIDTH, * do not use smaller font than our DEFAULT_FONT_DATA. */ STAILQ_FOREACH(fl, &fonts, font_next) { font = fl->font_data; if ((*rows * font->vfbd_height <= height && *cols * font->vfbd_width <= width) || (height >= VT_FB_MAX_HEIGHT && width >= VT_FB_MAX_WIDTH && font->vfbd_height == DEFAULT_FONT_DATA.vfbd_height && font->vfbd_width == DEFAULT_FONT_DATA.vfbd_width)) { if (font->vfbd_font == NULL || fl->font_flags == FONT_RELOAD) { if (fl->font_load != NULL && fl->font_name != NULL) { font = fl->font_load(fl->font_name); } if (font == NULL) continue; } *rows = height / font->vfbd_height; *cols = width / font->vfbd_width; break; } font = NULL; } if (font == NULL) { /* * We have fonts sorted smallest last, try it before * falling back to builtin. */ fl = STAILQ_LAST(&fonts, fontlist, font_next); if (fl != NULL && fl->font_load != NULL && fl->font_name != NULL) { font = fl->font_load(fl->font_name); } if (font == NULL) font = &DEFAULT_FONT_DATA; *rows = height / font->vfbd_height; *cols = width / font->vfbd_width; } return (font); } static void cons_clear(void) { char clear[] = { '\033', 'c' }; /* Reset terminal */ teken_input(&gfx_state.tg_teken, clear, sizeof(clear)); gfx_state.tg_functions->tf_param(&gfx_state, TP_SHOWCURSOR, 0); } void setup_font(teken_gfx_t *state, teken_unit_t height, teken_unit_t width) { vt_font_bitmap_data_t *font_data; teken_pos_t *tp = &state->tg_tp; char env[8]; int i; /* * set_font() will select a appropriate sized font for * the number of rows and columns selected. If we don't * have a font that will fit, then it will use the * default builtin font and adjust the rows and columns * to fit on the screen. */ font_data = set_font(&tp->tp_row, &tp->tp_col, height, width); if (font_data == NULL) panic("out of memory"); for (i = 0; i < VFNT_MAPS; i++) { state->tg_font.vf_map[i] = font_data->vfbd_font->vf_map[i]; state->tg_font.vf_map_count[i] = font_data->vfbd_font->vf_map_count[i]; } state->tg_font.vf_bytes = font_data->vfbd_font->vf_bytes; state->tg_font.vf_height = font_data->vfbd_font->vf_height; state->tg_font.vf_width = font_data->vfbd_font->vf_width; snprintf(env, sizeof (env), "%ux%u", state->tg_font.vf_width, state->tg_font.vf_height); env_setenv("screen.font", EV_VOLATILE | EV_NOHOOK, env, font_set, env_nounset); } /* Binary search for the glyph. Return 0 if not found. */ static uint16_t font_bisearch(const vfnt_map_t *map, uint32_t len, teken_char_t src) { unsigned min, mid, max; min = 0; max = len - 1; /* Empty font map. */ if (len == 0) return (0); /* Character below minimal entry. */ if (src < map[0].vfm_src) return (0); /* Optimization: ASCII characters occur very often. */ if (src <= map[0].vfm_src + map[0].vfm_len) return (src - map[0].vfm_src + map[0].vfm_dst); /* Character above maximum entry. */ if (src > map[max].vfm_src + map[max].vfm_len) return (0); /* Binary search. */ while (max >= min) { mid = (min + max) / 2; if (src < map[mid].vfm_src) max = mid - 1; else if (src > map[mid].vfm_src + map[mid].vfm_len) min = mid + 1; else return (src - map[mid].vfm_src + map[mid].vfm_dst); } return (0); } /* * Return glyph bitmap. If glyph is not found, we will return bitmap * for the first (offset 0) glyph. */ uint8_t * font_lookup(const struct vt_font *vf, teken_char_t c, const teken_attr_t *a) { uint16_t dst; size_t stride; /* Substitute bold with normal if not found. */ if (a->ta_format & TF_BOLD) { dst = font_bisearch(vf->vf_map[VFNT_MAP_BOLD], vf->vf_map_count[VFNT_MAP_BOLD], c); if (dst != 0) goto found; } dst = font_bisearch(vf->vf_map[VFNT_MAP_NORMAL], vf->vf_map_count[VFNT_MAP_NORMAL], c); found: stride = howmany(vf->vf_width, 8) * vf->vf_height; return (&vf->vf_bytes[dst * stride]); } static int load_mapping(int fd, struct vt_font *fp, int n) { size_t i, size; ssize_t rv; vfnt_map_t *mp; if (fp->vf_map_count[n] == 0) return (0); size = fp->vf_map_count[n] * sizeof(*mp); mp = malloc(size); if (mp == NULL) return (ENOMEM); fp->vf_map[n] = mp; rv = read(fd, mp, size); if (rv < 0 || (size_t)rv != size) { free(fp->vf_map[n]); fp->vf_map[n] = NULL; return (EIO); } for (i = 0; i < fp->vf_map_count[n]; i++) { mp[i].vfm_src = be32toh(mp[i].vfm_src); mp[i].vfm_dst = be16toh(mp[i].vfm_dst); mp[i].vfm_len = be16toh(mp[i].vfm_len); } return (0); } static int builtin_mapping(struct vt_font *fp, int n) { size_t size; struct vfnt_map *mp; if (n >= VFNT_MAPS) return (EINVAL); if (fp->vf_map_count[n] == 0) return (0); size = fp->vf_map_count[n] * sizeof(*mp); mp = malloc(size); if (mp == NULL) return (ENOMEM); fp->vf_map[n] = mp; memcpy(mp, DEFAULT_FONT_DATA.vfbd_font->vf_map[n], size); return (0); } /* * Load font from builtin or from file. * We do need special case for builtin because the builtin font glyphs * are compressed and we do need to uncompress them. * Having single load_font() for both cases will help us to simplify * font switch handling. */ static vt_font_bitmap_data_t * load_font(char *path) { int fd, i; uint32_t glyphs; struct font_header fh; struct fontlist *fl; vt_font_bitmap_data_t *bp; struct vt_font *fp; size_t size; ssize_t rv; /* Get our entry from the font list. */ STAILQ_FOREACH(fl, &fonts, font_next) { if (strcmp(fl->font_name, path) == 0) break; } if (fl == NULL) return (NULL); /* Should not happen. */ bp = fl->font_data; if (bp->vfbd_font != NULL && fl->font_flags != FONT_RELOAD) return (bp); fd = -1; /* * Special case for builtin font. * Builtin font is the very first font we load, we do not have * previous loads to be released. */ if (fl->font_flags == FONT_BUILTIN) { if ((fp = calloc(1, sizeof(struct vt_font))) == NULL) return (NULL); fp->vf_width = DEFAULT_FONT_DATA.vfbd_width; fp->vf_height = DEFAULT_FONT_DATA.vfbd_height; fp->vf_bytes = malloc(DEFAULT_FONT_DATA.vfbd_uncompressed_size); if (fp->vf_bytes == NULL) { free(fp); return (NULL); } bp->vfbd_uncompressed_size = DEFAULT_FONT_DATA.vfbd_uncompressed_size; bp->vfbd_compressed_size = DEFAULT_FONT_DATA.vfbd_compressed_size; if (lz4_decompress(DEFAULT_FONT_DATA.vfbd_compressed_data, fp->vf_bytes, DEFAULT_FONT_DATA.vfbd_compressed_size, DEFAULT_FONT_DATA.vfbd_uncompressed_size, 0) != 0) { free(fp->vf_bytes); free(fp); return (NULL); } for (i = 0; i < VFNT_MAPS; i++) { fp->vf_map_count[i] = DEFAULT_FONT_DATA.vfbd_font->vf_map_count[i]; if (builtin_mapping(fp, i) != 0) goto free_done; } bp->vfbd_font = fp; return (bp); } fd = open(path, O_RDONLY); if (fd < 0) return (NULL); size = sizeof(fh); rv = read(fd, &fh, size); if (rv < 0 || (size_t)rv != size) { bp = NULL; goto done; } if (memcmp(fh.fh_magic, FONT_HEADER_MAGIC, sizeof(fh.fh_magic)) != 0) { bp = NULL; goto done; } if ((fp = calloc(1, sizeof(struct vt_font))) == NULL) { bp = NULL; goto done; } for (i = 0; i < VFNT_MAPS; i++) fp->vf_map_count[i] = be32toh(fh.fh_map_count[i]); glyphs = be32toh(fh.fh_glyph_count); fp->vf_width = fh.fh_width; fp->vf_height = fh.fh_height; size = howmany(fp->vf_width, 8) * fp->vf_height * glyphs; bp->vfbd_uncompressed_size = size; if ((fp->vf_bytes = malloc(size)) == NULL) goto free_done; rv = read(fd, fp->vf_bytes, size); if (rv < 0 || (size_t)rv != size) goto free_done; for (i = 0; i < VFNT_MAPS; i++) { if (load_mapping(fd, fp, i) != 0) goto free_done; } /* * Reset builtin flag now as we have full font loaded. */ if (fl->font_flags == FONT_BUILTIN) fl->font_flags = FONT_AUTO; /* * Release previously loaded entries. We can do this now, as * the new font is loaded. Note, there can be no console * output till the new font is in place and teken is notified. * We do need to keep fl->font_data for glyph dimensions. */ STAILQ_FOREACH(fl, &fonts, font_next) { if (fl->font_data->vfbd_font == NULL) continue; for (i = 0; i < VFNT_MAPS; i++) free(fl->font_data->vfbd_font->vf_map[i]); free(fl->font_data->vfbd_font->vf_bytes); free(fl->font_data->vfbd_font); fl->font_data->vfbd_font = NULL; } bp->vfbd_font = fp; bp->vfbd_compressed_size = 0; done: if (fd != -1) close(fd); return (bp); free_done: for (i = 0; i < VFNT_MAPS; i++) free(fp->vf_map[i]); free(fp->vf_bytes); free(fp); bp = NULL; goto done; } struct name_entry { char *n_name; SLIST_ENTRY(name_entry) n_entry; }; SLIST_HEAD(name_list, name_entry); /* Read font names from index file. */ static struct name_list * read_list(char *fonts) { struct name_list *nl; struct name_entry *np; char *dir, *ptr; char buf[PATH_MAX]; int fd, len; TSENTER(); dir = strdup(fonts); if (dir == NULL) return (NULL); ptr = strrchr(dir, '/'); *ptr = '\0'; fd = open(fonts, O_RDONLY); if (fd < 0) return (NULL); nl = malloc(sizeof(*nl)); if (nl == NULL) { close(fd); return (nl); } SLIST_INIT(nl); while ((len = fgetstr(buf, sizeof (buf), fd)) >= 0) { if (*buf == '#' || *buf == '\0') continue; if (bcmp(buf, "MENU", 4) == 0) continue; if (bcmp(buf, "FONT", 4) == 0) continue; ptr = strchr(buf, ':'); if (ptr == NULL) continue; else *ptr = '\0'; np = malloc(sizeof(*np)); if (np == NULL) { close(fd); return (nl); /* return what we have */ } if (asprintf(&np->n_name, "%s/%s", dir, buf) < 0) { free(np); close(fd); return (nl); /* return what we have */ } SLIST_INSERT_HEAD(nl, np, n_entry); } close(fd); TSEXIT(); return (nl); } /* * Read the font properties and insert new entry into the list. * The font list is built in descending order. */ static bool insert_font(char *name, FONT_FLAGS flags) { struct font_header fh; struct fontlist *fp, *previous, *entry, *next; size_t size; ssize_t rv; int fd; char *font_name; TSENTER(); font_name = NULL; if (flags == FONT_BUILTIN) { /* * We only install builtin font once, while setting up * initial console. Since this will happen very early, * we assume asprintf will not fail. Once we have access to * files, the builtin font will be replaced by font loaded * from file. */ if (!STAILQ_EMPTY(&fonts)) return (false); fh.fh_width = DEFAULT_FONT_DATA.vfbd_width; fh.fh_height = DEFAULT_FONT_DATA.vfbd_height; (void) asprintf(&font_name, "%dx%d", DEFAULT_FONT_DATA.vfbd_width, DEFAULT_FONT_DATA.vfbd_height); } else { fd = open(name, O_RDONLY); if (fd < 0) return (false); rv = read(fd, &fh, sizeof(fh)); close(fd); if (rv < 0 || (size_t)rv != sizeof(fh)) return (false); if (memcmp(fh.fh_magic, FONT_HEADER_MAGIC, sizeof(fh.fh_magic)) != 0) return (false); font_name = strdup(name); } if (font_name == NULL) return (false); /* * If we have an entry with the same glyph dimensions, replace * the file name and mark us. We only support unique dimensions. */ STAILQ_FOREACH(entry, &fonts, font_next) { if (fh.fh_width == entry->font_data->vfbd_width && fh.fh_height == entry->font_data->vfbd_height) { free(entry->font_name); entry->font_name = font_name; entry->font_flags = FONT_RELOAD; TSEXIT(); return (true); } } fp = calloc(sizeof(*fp), 1); if (fp == NULL) { free(font_name); return (false); } fp->font_data = calloc(sizeof(*fp->font_data), 1); if (fp->font_data == NULL) { free(font_name); free(fp); return (false); } fp->font_name = font_name; fp->font_flags = flags; fp->font_load = load_font; fp->font_data->vfbd_width = fh.fh_width; fp->font_data->vfbd_height = fh.fh_height; if (STAILQ_EMPTY(&fonts)) { STAILQ_INSERT_HEAD(&fonts, fp, font_next); TSEXIT(); return (true); } previous = NULL; size = fp->font_data->vfbd_width * fp->font_data->vfbd_height; STAILQ_FOREACH(entry, &fonts, font_next) { vt_font_bitmap_data_t *bd; bd = entry->font_data; /* Should fp be inserted before the entry? */ if (size > bd->vfbd_width * bd->vfbd_height) { if (previous == NULL) { STAILQ_INSERT_HEAD(&fonts, fp, font_next); } else { STAILQ_INSERT_AFTER(&fonts, previous, fp, font_next); } TSEXIT(); return (true); } next = STAILQ_NEXT(entry, font_next); if (next == NULL || size > next->font_data->vfbd_width * next->font_data->vfbd_height) { STAILQ_INSERT_AFTER(&fonts, entry, fp, font_next); TSEXIT(); return (true); } previous = entry; } TSEXIT(); return (true); } static int font_set(struct env_var *ev __unused, int flags __unused, const void *value) { struct fontlist *fl; char *eptr; unsigned long x = 0, y = 0; /* * Attempt to extract values from "XxY" string. In case of error, * we have unmaching glyph dimensions and will just output the * available values. */ if (value != NULL) { x = strtoul(value, &eptr, 10); if (*eptr == 'x') y = strtoul(eptr + 1, &eptr, 10); } STAILQ_FOREACH(fl, &fonts, font_next) { if (fl->font_data->vfbd_width == x && fl->font_data->vfbd_height == y) break; } if (fl != NULL) { /* Reset any FONT_MANUAL flag. */ reset_font_flags(); /* Mark this font manually loaded */ fl->font_flags = FONT_MANUAL; cons_update_mode(gfx_state.tg_fb_type != FB_TEXT); return (CMD_OK); } printf("Available fonts:\n"); STAILQ_FOREACH(fl, &fonts, font_next) { printf(" %dx%d\n", fl->font_data->vfbd_width, fl->font_data->vfbd_height); } return (CMD_OK); } void bios_text_font(bool use_vga_font) { if (use_vga_font) (void) insert_font(VGA_8X16_FONT, FONT_MANUAL); else (void) insert_font(DEFAULT_8X16_FONT, FONT_MANUAL); } void autoload_font(bool bios) { struct name_list *nl; struct name_entry *np; TSENTER(); nl = read_list("/boot/fonts/INDEX.fonts"); if (nl == NULL) return; while (!SLIST_EMPTY(nl)) { np = SLIST_FIRST(nl); SLIST_REMOVE_HEAD(nl, n_entry); if (insert_font(np->n_name, FONT_AUTO) == false) printf("failed to add font: %s\n", np->n_name); free(np->n_name); free(np); } /* * If vga text mode was requested, load vga.font (8x16 bold) font. */ if (bios) { bios_text_font(true); } (void) cons_update_mode(gfx_state.tg_fb_type != FB_TEXT); TSEXIT(); } COMMAND_SET(load_font, "loadfont", "load console font from file", command_font); static int command_font(int argc, char *argv[]) { int i, c, rc; struct fontlist *fl; vt_font_bitmap_data_t *bd; bool list; list = false; optind = 1; optreset = 1; rc = CMD_OK; while ((c = getopt(argc, argv, "l")) != -1) { switch (c) { case 'l': list = true; break; case '?': default: return (CMD_ERROR); } } argc -= optind; argv += optind; if (argc > 1 || (list && argc != 0)) { printf("Usage: loadfont [-l] | [file.fnt]\n"); return (CMD_ERROR); } if (list) { STAILQ_FOREACH(fl, &fonts, font_next) { printf("font %s: %dx%d%s\n", fl->font_name, fl->font_data->vfbd_width, fl->font_data->vfbd_height, fl->font_data->vfbd_font == NULL? "" : " loaded"); } return (CMD_OK); } /* Clear scren */ cons_clear(); if (argc == 1) { char *name = argv[0]; if (insert_font(name, FONT_MANUAL) == false) { printf("loadfont error: failed to load: %s\n", name); return (CMD_ERROR); } (void) cons_update_mode(gfx_state.tg_fb_type != FB_TEXT); return (CMD_OK); } if (argc == 0) { /* * Walk entire font list, release any loaded font, and set * autoload flag. The font list does have at least the builtin * default font. */ STAILQ_FOREACH(fl, &fonts, font_next) { if (fl->font_data->vfbd_font != NULL) { bd = fl->font_data; /* * Note the setup_font() is releasing * font bytes. */ for (i = 0; i < VFNT_MAPS; i++) free(bd->vfbd_font->vf_map[i]); free(fl->font_data->vfbd_font); fl->font_data->vfbd_font = NULL; fl->font_data->vfbd_uncompressed_size = 0; fl->font_flags = FONT_AUTO; } } (void) cons_update_mode(gfx_state.tg_fb_type != FB_TEXT); } return (rc); } bool gfx_get_edid_resolution(struct vesa_edid_info *edid, edid_res_list_t *res) { struct resolution *rp, *p; /* * Walk detailed timings tables (4). */ if ((edid->display.supported_features & EDID_FEATURE_PREFERRED_TIMING_MODE) != 0) { /* Walk detailed timing descriptors (4) */ for (int i = 0; i < DET_TIMINGS; i++) { /* * Reserved value 0 is not used for display descriptor. */ if (edid->detailed_timings[i].pixel_clock == 0) continue; if ((rp = malloc(sizeof(*rp))) == NULL) continue; rp->width = GET_EDID_INFO_WIDTH(edid, i); rp->height = GET_EDID_INFO_HEIGHT(edid, i); if (rp->width > 0 && rp->width <= EDID_MAX_PIXELS && rp->height > 0 && rp->height <= EDID_MAX_LINES) TAILQ_INSERT_TAIL(res, rp, next); else free(rp); } } /* * Walk standard timings list (8). */ for (int i = 0; i < STD_TIMINGS; i++) { /* Is this field unused? */ if (edid->standard_timings[i] == 0x0101) continue; if ((rp = malloc(sizeof(*rp))) == NULL) continue; rp->width = HSIZE(edid->standard_timings[i]); switch (RATIO(edid->standard_timings[i])) { case RATIO1_1: rp->height = HSIZE(edid->standard_timings[i]); if (edid->header.version > 1 || edid->header.revision > 2) { rp->height = rp->height * 10 / 16; } break; case RATIO4_3: rp->height = HSIZE(edid->standard_timings[i]) * 3 / 4; break; case RATIO5_4: rp->height = HSIZE(edid->standard_timings[i]) * 4 / 5; break; case RATIO16_9: rp->height = HSIZE(edid->standard_timings[i]) * 9 / 16; break; } /* * Create resolution list in decreasing order, except keep * first entry (preferred timing mode). */ TAILQ_FOREACH(p, res, next) { if (p->width * p->height < rp->width * rp->height) { /* Keep preferred mode first */ if (TAILQ_FIRST(res) == p) TAILQ_INSERT_AFTER(res, p, rp, next); else TAILQ_INSERT_BEFORE(p, rp, next); break; } if (TAILQ_NEXT(p, next) == NULL) { TAILQ_INSERT_TAIL(res, rp, next); break; } } } return (!TAILQ_EMPTY(res)); } diff --git a/stand/common/install.c b/stand/common/install.c index 418799822e4e..b827b1fe9da2 100644 --- a/stand/common/install.c +++ b/stand/common/install.c @@ -1,403 +1,402 @@ /*- * 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 #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://[host]/ * or file://[devname[:fstype]]/ */ static int install(char *pkgname) { static char buf[256]; struct fs_ops *proto; struct preloaded_file *fp; char *e, *s, *currdev; char *devname; size_t devnamelen; int error, fd, i, local; s = strstr(pkgname, "://"); if (s == NULL) goto invalid_url; i = s - pkgname; s += 3; if (*s == '\0') goto invalid_url; devname = NULL; devnamelen = 0; proto = NULL; local = 0; if (i == 4 && !strncasecmp(pkgname, "tftp", i)) { devname = "net0"; devnamelen = 4; proto = &tftp_fsops; } else if (i == 4 && !strncasecmp(pkgname, "file", i)) { currdev = getenv("currdev"); local = 1; if (*s == '/') { /* file:/// */ if (devname == NULL) devname = currdev; if (devname == NULL) devname = "disk1"; } else { /* file://devname[:fstype]/ */ devname = s; e = strchr(devname, '/'); if (!e) goto invalid_url; devnamelen = e - devname; s = e; /* consume devname */ } if ((e = strchr(devname, ':')) != NULL) { /* could be :fstype */ devnamelen = e - devname; switch (e[1]) { case '\0': /* just currdev */ break; case 'd': proto = &dosfs_fsops; break; #ifdef HOSTPROG case 'h': { extern struct fs_ops host_fsops; proto = &host_fsops; } break; #endif case 'u': proto = &ufs_fsops; break; } } if (proto == NULL && strncmp(devname, "disk", 4) == 0) { proto = &dosfs_fsops; } } if (devname == NULL) goto invalid_url; if (devnamelen == 0) { /* default is currdev which ends with ':' */ devnamelen = strlen(devname); if (devname[devnamelen - 1] == ':') devnamelen--; } 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; i = snprintf(buf, sizeof(buf), "%.*s:%s", (int) devnamelen, devname, pkgname); if (i >= (int) sizeof(buf)) { command_errmsg = "package name too long"; return (CMD_ERROR); } 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 = interp_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_forth.c b/stand/common/interp_forth.c index 0320e081d406..854addb22d5c 100644 --- a/stand/common/interp_forth.c +++ b/stand/common/interp_forth.c @@ -1,455 +1,454 @@ /*- * 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 #include /* to pick up __FreeBSD_version */ #include #include #include "bootstrap.h" #include "ficl.h" INTERP_DEFINE("4th"); /* #define BFORTH_DEBUG */ #ifdef BFORTH_DEBUG #define DPRINTF(fmt, args...) printf("%s: " fmt "\n" , __func__ , ## args) #else #define DPRINTF(fmt, args...) ((void)0) #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); } } DPRINTF("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); command_errmsg = NULL; break; case CMD_FATAL: panic("%s", 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(void) { 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 ((fd = open("/boot/boot.4th", O_RDONLY)) != -1) { #ifdef LOADER_VERIEXEC if (verify_file(fd, "/boot/boot.4th", 0, VE_GUESS, __func__) < 0) { close(fd); return; } #endif (void)ficlExecFD(bf_vm, fd); close(fd); } } /* * Feed a line of user input to the Forth interpreter */ static int bf_run(const char *line) { int result; /* * ficl would require extensive changes to accept a const char * * interface. Instead, cast it away here and hope for the best. * We know at the present time the caller for us in the boot * forth loader can tolerate the string being modified because * the string is passed in here and then not touched again. */ result = ficlExec(bf_vm, __DECONST(char *, line)); DPRINTF("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); } void interp_init(void) { setenv("script.lang", "forth", 1); bf_init(); /* Read our default configuration. */ interp_include("/boot/loader.rc"); } int interp_run(const char *input) { bf_vm->sourceID.i = 0; return bf_run(input); } /* * 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; char text[0]; }; int interp_include(const char *filename) { struct includeline *script, *se, *sp; char input[256]; /* big enough? */ int res; char *cp; int prevsrcid, fd, line; if (((fd = open(filename, O_RDONLY)) == -1)) { snprintf(command_errbuf, sizeof(command_errbuf), "can't open '%s': %s", filename, strerror(errno)); return(CMD_ERROR); } #ifdef LOADER_VERIEXEC if (verify_file(fd, filename, 0, VE_GUESS, __func__) < 0) { close(fd); sprintf(command_errbuf,"can't verify '%s'", filename); return(CMD_ERROR); } #endif /* * Read the script into memory. */ script = se = NULL; line = 0; while (fgetstr(input, sizeof(input), fd) >= 0) { line++; cp = input; /* 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); close(fd); return (CMD_ERROR); } strcpy(sp->text, cp); sp->next = NULL; if (script == NULL) { script = sp; } else { se->next = sp; } se = sp; } close(fd); /* * Execute the script */ prevsrcid = bf_vm->sourceID.i; bf_vm->sourceID.i = fd; res = CMD_OK; for (sp = script; sp != NULL; sp = sp->next) { 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; } bf_vm->sourceID.i = prevsrcid; while (script != NULL) { se = script; script = script->next; free(se); } return(res); } diff --git a/stand/common/load_elf.c b/stand/common/load_elf.c index 9f3b015fef7e..d53c0f8b5b84 100644 --- a/stand/common/load_elf.c +++ b/stand/common/load_elf.c @@ -1,1325 +1,1324 @@ /*- * 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 #include #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; uint64_t off; #ifdef LOADER_VERIEXEC_VECTX struct vectx *vctx; #endif } *elf_file_t; #ifdef LOADER_VERIEXEC_VECTX #define VECTX_HANDLE(ef) (ef)->vctx #else #define VECTX_HANDLE(ef) (ef)->fd #endif static int __elfN(loadimage)(struct preloaded_file *mp, elf_file_t ef, uint64_t loadaddr); static int __elfN(lookup_symbol)(elf_file_t ef, const char* name, Elf_Sym *sym, unsigned char type); 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"; uint64_t __elfN(relocation_offset) = 0; #ifdef __powerpc__ extern void elf_wrong_field_size(void); #define CONVERT_FIELD(b, f, e) \ switch (sizeof((b)->f)) { \ case 2: \ (b)->f = e ## 16toh((b)->f); \ break; \ case 4: \ (b)->f = e ## 32toh((b)->f); \ break; \ case 8: \ (b)->f = e ## 64toh((b)->f); \ break; \ default: \ /* Force a link time error. */ \ elf_wrong_field_size(); \ break; \ } #define CONVERT_SWITCH(h, d, f) \ switch ((h)->e_ident[EI_DATA]) { \ case ELFDATA2MSB: \ f(d, be); \ break; \ case ELFDATA2LSB: \ f(d, le); \ break; \ default: \ return (EINVAL); \ } static int elf_header_convert(Elf_Ehdr *ehdr) { /* * Fixup ELF header endianness. * * The Xhdr structure was loaded using block read call to optimize file * accesses. It might happen, that the endianness of the system memory * is different that endianness of the ELF header. Swap fields here to * guarantee that Xhdr always contain valid data regardless of * architecture. */ #define HEADER_FIELDS(b, e) \ CONVERT_FIELD(b, e_type, e); \ CONVERT_FIELD(b, e_machine, e); \ CONVERT_FIELD(b, e_version, e); \ CONVERT_FIELD(b, e_entry, e); \ CONVERT_FIELD(b, e_phoff, e); \ CONVERT_FIELD(b, e_shoff, e); \ CONVERT_FIELD(b, e_flags, e); \ CONVERT_FIELD(b, e_ehsize, e); \ CONVERT_FIELD(b, e_phentsize, e); \ CONVERT_FIELD(b, e_phnum, e); \ CONVERT_FIELD(b, e_shentsize, e); \ CONVERT_FIELD(b, e_shnum, e); \ CONVERT_FIELD(b, e_shstrndx, e) CONVERT_SWITCH(ehdr, ehdr, HEADER_FIELDS); #undef HEADER_FIELDS return (0); } static int elf_program_header_convert(const Elf_Ehdr *ehdr, Elf_Phdr *phdr) { #define PROGRAM_HEADER_FIELDS(b, e) \ CONVERT_FIELD(b, p_type, e); \ CONVERT_FIELD(b, p_flags, e); \ CONVERT_FIELD(b, p_offset, e); \ CONVERT_FIELD(b, p_vaddr, e); \ CONVERT_FIELD(b, p_paddr, e); \ CONVERT_FIELD(b, p_filesz, e); \ CONVERT_FIELD(b, p_memsz, e); \ CONVERT_FIELD(b, p_align, e) CONVERT_SWITCH(ehdr, phdr, PROGRAM_HEADER_FIELDS); #undef PROGRAM_HEADER_FIELDS return (0); } static int elf_section_header_convert(const Elf_Ehdr *ehdr, Elf_Shdr *shdr) { #define SECTION_HEADER_FIELDS(b, e) \ CONVERT_FIELD(b, sh_name, e); \ CONVERT_FIELD(b, sh_type, e); \ CONVERT_FIELD(b, sh_link, e); \ CONVERT_FIELD(b, sh_info, e); \ CONVERT_FIELD(b, sh_flags, e); \ CONVERT_FIELD(b, sh_addr, e); \ CONVERT_FIELD(b, sh_offset, e); \ CONVERT_FIELD(b, sh_size, e); \ CONVERT_FIELD(b, sh_addralign, e); \ CONVERT_FIELD(b, sh_entsize, e) CONVERT_SWITCH(ehdr, shdr, SECTION_HEADER_FIELDS); #undef SECTION_HEADER_FIELDS return (0); } #undef CONVERT_SWITCH #undef CONVERT_FIELD #else static int elf_header_convert(Elf_Ehdr *ehdr) { return (0); } static int elf_program_header_convert(const Elf_Ehdr *ehdr, Elf_Phdr *phdr) { return (0); } static int elf_section_header_convert(const Elf_Ehdr *ehdr, Elf_Shdr *shdr) { return (0); } #endif #ifdef __amd64__ static bool is_kernphys_relocatable(elf_file_t ef) { Elf_Sym sym; return (__elfN(lookup_symbol)(ef, "kernphys", &sym, STT_OBJECT) == 0); } #endif #ifdef __i386__ static bool is_tg_kernel_support(struct preloaded_file *fp, elf_file_t ef) { Elf_Sym sym; Elf_Addr p_start, p_end, v, p; char vd_name[16]; int error; if (__elfN(lookup_symbol)(ef, "__start_set_vt_drv_set", &sym, STT_NOTYPE) != 0) return (false); p_start = sym.st_value + ef->off; if (__elfN(lookup_symbol)(ef, "__stop_set_vt_drv_set", &sym, STT_NOTYPE) != 0) return (false); p_end = sym.st_value + ef->off; /* * Walk through vt_drv_set, each vt driver structure starts with * static 16 chars for driver name. If we have "vbefb", return true. */ for (p = p_start; p < p_end; p += sizeof(Elf_Addr)) { 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 (false); COPYOUT(v, &vd_name, sizeof(vd_name)); if (strncmp(vd_name, "vbefb", sizeof(vd_name)) == 0) return (true); } return (false); } #endif 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); } preload(ef->fd); #ifdef LOADER_VERIEXEC_VECTX { int verror; ef->vctx = vectx_open(ef->fd, filename, 0L, NULL, &verror, __func__); if (verror) { printf("Unverified %s: %s\n", filename, ve_error_get()); close(ef->fd); free(ef->vctx); return (EAUTH); } } #endif bytes_read = VECTX_READ(VECTX_HANDLE(ef), 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 ? */ { err = EFTYPE; goto error; } err = elf_header_convert(ehdr); if (err) goto error; if (ehdr->e_version != EV_CURRENT || ehdr->e_machine != ELF_TARG_MACH) { /* Machine ? */ err = EFTYPE; goto error; } #if defined(LOADER_VERIEXEC) && !defined(LOADER_VERIEXEC_VECTX) if (verify_file(ef->fd, filename, bytes_read, VE_MUST, __func__) < 0) { err = EAUTH; goto error; } #endif return (0); error: if (ef->firstpage != NULL) { free(ef->firstpage); ef->firstpage = NULL; } if (ef->fd != -1) { #ifdef LOADER_VERIEXEC_VECTX free(ef->vctx); #endif 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, uint64_t dest, struct preloaded_file **result) { return (__elfN(loadfile_raw)(filename, dest, result, 0)); } int __elfN(loadfile_raw)(char *filename, uint64_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"); if (module_verbose >= MODULE_VERBOSE_FULL) { if (ef.kernel) printf("%s entry at 0x%jx\n", filename, (uintmax_t)ehdr->e_entry); } else if (module_verbose > MODULE_VERBOSE_SILENT) printf("%s ", filename); 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; #ifdef __amd64__ fp->f_kernphys_relocatable = multiboot || is_kernphys_relocatable(&ef); #endif #ifdef __i386__ fp->f_tg_kernel_support = is_tg_kernel_support(fp, &ef); #endif goto out; ioerr: err = EIO; oerr: file_discard(fp); out: if (ef.firstpage) free(ef.firstpage); if (ef.fd != -1) { #ifdef LOADER_VERIEXEC_VECTX if (!err && ef.vctx) { int verror; verror = vectx_close(ef.vctx, VE_MUST, __func__); if (verror) { err = EAUTH; file_discard(fp); } } #endif 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, uint64_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; #ifdef __powerpc__ if (ef->kernel) { #else if (ehdr->e_type == ET_EXEC) { #endif #if defined(__i386__) || defined(__amd64__) #if __ELF_WORD_SIZE == 64 /* x86_64 relocates after locore */ off = - (off & 0xffffffffff000000ull); #else /* i386 relocates after locore */ off = - (off & 0xff000000u); #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; if (module_verbose >= MODULE_VERBOSE_FULL) printf("Converted entry 0x%jx\n", (uintmax_t)ehdr->e_entry); #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; if (module_verbose >= MODULE_VERBOSE_FULL) printf("ehdr->e_entry 0x%jx, va<->pa off %llx\n", (uintmax_t)ehdr->e_entry, off); #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++) { if (elf_program_header_convert(ehdr, phdr)) continue; /* We want to load PT_LOAD segments only.. */ if (phdr[i].p_type != PT_LOAD) continue; if (module_verbose >= MODULE_VERBOSE_FULL) { 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 (module_verbose > MODULE_VERBOSE_SILENT) { 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(" "); } } 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(VECTX_HANDLE(ef), 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) { if (module_verbose >= MODULE_VERBOSE_FULL) { 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)); } kern_bzero(phdr[i].p_vaddr + off + phdr[i].p_filesz, phdr[i].p_memsz - phdr[i].p_filesz); } if (module_verbose >= MODULE_VERBOSE_FULL) printf("\n"); 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 = (size_t)ehdr->e_shnum * (size_t)ehdr->e_shentsize; if (chunk == 0 || ehdr->e_shoff == 0) goto nosyms; shdr = alloc_pread(VECTX_HANDLE(ef), ehdr->e_shoff, chunk); if (shdr == NULL) { printf("\nelf" __XSTRING(__ELF_WORD_SIZE) "_loadimage: failed to read section headers"); goto nosyms; } for (i = 0; i < ehdr->e_shnum; i++) elf_section_header_convert(ehdr, &shdr[i]); 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(VECTX_HANDLE(ef), 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. */ if (module_verbose >= MODULE_VERBOSE_FULL) printf("syms=["); ssym = lastaddr; for (i = symtabindex; i >= 0; i = symstrindex) { 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; } size = shdr[i].sh_size; archsw.arch_copyin(&size, lastaddr, sizeof(size)); lastaddr += sizeof(size); if (module_verbose >= MODULE_VERBOSE_FULL) { 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 (module_verbose > MODULE_VERBOSE_SILENT) { if (i == symstrindex) printf("+"); printf("0x%lx+0x%lx", (long)sizeof(size), (long)size); } if (VECTX_LSEEK(VECTX_HANDLE(ef), (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(VECTX_HANDLE(ef), 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; if (module_verbose >= MODULE_VERBOSE_FULL) printf("]"); file_addmetadata(fp, MODINFOMD_SSYM, sizeof(ssym), &ssym); file_addmetadata(fp, MODINFOMD_ESYM, sizeof(esym), &esym); nosyms: if (module_verbose > MODULE_VERBOSE_SILENT) 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)(ef, "__start_set_modmetadata_set", &sym, STT_NOTYPE) != 0) return 0; p_start = sym.st_value + ef->off; if (__elfN(lookup_symbol)(ef, "__stop_set_modmetadata_set", &sym, STT_NOTYPE) != 0) return 0; 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(sp, '.'); if (ep == NULL) { ep = sp + strlen(sp); } if (ep == sp) { sp = invalid_name; ep = invalid_name + sizeof(invalid_name) - 1; } 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_* */ uint64_t md_data; /* specific data */ uint64_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_* */ uint32_t md_data; /* specific data */ uint32_t md_cval; /* common string label */ }; #endif int __elfN(load_modmetadata)(struct preloaded_file *fp, uint64_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 = (size_t)ef.ehdr->e_shnum * (size_t)ef.ehdr->e_shentsize; shdr = alloc_pread(VECTX_HANDLE(&ef), ef.ehdr->e_shoff, size); if (shdr == NULL) { err = ENOMEM; goto out; } /* Load shstrtab. */ shstrtab = alloc_pread(VECTX_HANDLE(&ef), 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(VECTX_HANDLE(&ef), 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(VECTX_HANDLE(&ef), 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(VECTX_HANDLE(&ef), 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) { #ifdef LOADER_VERIEXEC_VECTX if (!err && ef.vctx) { int verror; verror = vectx_close(ef.vctx, VE_MUST, __func__); if (verror) { err = EAUTH; file_discard(fp); } } #endif 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)(elf_file_t ef, const char* name, Elf_Sym *symp, unsigned char type) { Elf_Hashelt symnum; Elf_Sym sym; char *strp; unsigned long hash; if (ef->nbuckets == 0) { printf(__elfN(bad_symtable)); return ENOENT; } 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) == type) { *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_elf_obj.c b/stand/common/load_elf_obj.c index 86e0fb2b5d22..263553df5e90 100644 --- a/stand/common/load_elf_obj.c +++ b/stand/common/load_elf_obj.c @@ -1,582 +1,581 @@ /*- * 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 #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; #ifdef LOADER_VERIEXEC_VECTX struct vectx *vctx; #endif } *elf_file_t; #ifdef LOADER_VERIEXEC_VECTX #define VECTX_HANDLE(ef) (ef)->vctx #else #define VECTX_HANDLE(ef) (ef)->fd #endif static int __elfN(obj_loadimage)(struct preloaded_file *mp, elf_file_t ef, uint64_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, uint64_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); #ifdef LOADER_VERIEXEC_VECTX { int verror; ef.vctx = vectx_open(ef.fd, filename, 0L, NULL, &verror, __func__); if (verror) { printf("Unverified %s: %s\n", filename, ve_error_get()); close(ef.fd); free(ef.vctx); return (EAUTH); } } #endif hdr = &ef.hdr; bytes_read = VECTX_READ(VECTX_HANDLE(&ef), 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; } #if defined(LOADER_VERIEXEC) && !defined(LOADER_VERIEXEC_VECTX) if (verify_file(ef.fd, filename, bytes_read, VE_MUST, __func__) < 0) { err = EAUTH; goto oerr; } #endif 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)); if (module_verbose > MODULE_VERBOSE_SILENT) 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: #ifdef LOADER_VERIEXEC_VECTX if (!err && ef.vctx) { int verror; verror = vectx_close(ef.vctx, VE_MUST, __func__); if (verror) { err = EAUTH; file_discard(fp); } } #endif 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, uint64_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(VECTX_HANDLE(ef), (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 case SHT_INIT_ARRAY: case SHT_FINI_ARRAY: if ((shdr[i].sh_flags & SHF_ALLOC) == 0) break; 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; 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: if ((shdr[shdr[i].sh_info].sh_flags & SHF_ALLOC) == 0) break; 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(VECTX_HANDLE(ef), (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; if (module_verbose > MODULE_VERBOSE_SILENT) printf("size 0x%lx at 0x%lx", (u_long)ret, (u_long)firstaddr); out: if (module_verbose > MODULE_VERBOSE_SILENT) 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_* */ uint64_t md_data; /* specific data */ uint64_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 index daeecfa488da..0cf57f7e281c 100644 --- a/stand/common/ls.c +++ b/stand/common/ls.c @@ -1,210 +1,209 @@ /* * $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 #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/reloc_elf.c b/stand/common/reloc_elf.c index a7243bc634fd..cf5010a06ccd 100644 --- a/stand/common/reloc_elf.c +++ b/stand/common/reloc_elf.c @@ -1,226 +1,225 @@ /*- * 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 #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) { #if (defined(__aarch64__) || defined(__amd64__) || defined(__i386__)) && \ __ELF_WORD_SIZE == 64 Elf64_Addr *where, val; Elf_Addr addend, addr; Elf_Size rtype; #if defined(__amd64__) || defined(__i386__) Elf_Size symidx; #endif 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); #if defined(__amd64__) || defined(__i386__) symidx = ELF_R_SYM(rel->r_info); #endif 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); #if defined(__amd64__) || defined(__i386__) symidx = ELF_R_SYM(rela->r_info); #endif break; default: return (EINVAL); } if ((char *)where < (char *)data || (char *)where >= (char *)data + len) return (0); if (reltype == ELF_RELOC_REL) addend = *where; #if defined(__aarch64__) #define RELOC_RELATIVE R_AARCH64_RELATIVE #define RELOC_IRELATIVE R_AARCH64_IRELATIVE #elif defined(__amd64__) || defined(__i386__) /* XXX, definitions not available on i386. */ #define R_X86_64_64 1 #define R_X86_64_RELATIVE 8 #define R_X86_64_IRELATIVE 37 #define RELOC_RELATIVE R_X86_64_RELATIVE #define RELOC_IRELATIVE R_X86_64_IRELATIVE #endif switch (rtype) { case RELOC_RELATIVE: addr = (Elf_Addr)addend + relbase; val = addr; memcpy(where, &val, sizeof(val)); break; case RELOC_IRELATIVE: /* leave it to kernel */ break; #if defined(__amd64__) || defined(__i386__) case R_X86_64_64: /* S + A */ addr = symaddr(ef, symidx); if (addr == 0) return (ESRCH); val = addr + addend; *where = val; break; #endif 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__) || defined(__riscv) 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)) { #if defined(__powerpc__) case R_PPC_RELATIVE: #elif defined(__riscv) case R_RISCV_RELATIVE: #endif 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/self_reloc.c b/stand/common/self_reloc.c index 39dad510b0b0..a4faeeace63f 100644 --- a/stand/common/self_reloc.c +++ b/stand/common/self_reloc.c @@ -1,125 +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 #include #include #include #if defined(__aarch64__) || defined(__amd64__) || defined(__riscv) #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 #elif defined(__riscv) #define RELOC_TYPE_NONE R_RISCV_NONE #define RELOC_TYPE_RELATIVE R_RISCV_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); } } diff --git a/stand/common/tslog.c b/stand/common/tslog.c index 6f27b9ebc316..8a17b0ca110c 100644 --- a/stand/common/tslog.c +++ b/stand/common/tslog.c @@ -1,79 +1,78 @@ /*- * Copyright (c) 2021 Colin Percival * 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 #include #include "bootstrap.h" /* Buffer for holding tslog data in string format. */ #ifndef LOADER_TSLOGSIZE #define LOADER_TSLOGSIZE (2 * 1024 * 1024) #endif int tslog_init(void) { void * tslog_buf; /* Allocate buffer and pass to libsa tslog code. */ if ((tslog_buf = malloc(LOADER_TSLOGSIZE)) == NULL) return (-1); tslog_setbuf(tslog_buf, LOADER_TSLOGSIZE); /* Record this as when we entered the loader. */ TSRAW("ENTER", "loader", NULL); return (0); } /* * Pass our tslog buffer as a preloaded "module" to the kernel. This should * be called as late as possible prior to the kernel being executed, since * any timestamps logged after this is called will not be visible to the * kernel. */ int tslog_publish(void) { void * tslog_buf; size_t tslog_bufpos; /* Record a log entry for ending logging. */ TSRAW("EXIT", "loader", NULL); /* Get the buffer and its current length. */ tslog_getbuf(&tslog_buf, &tslog_bufpos); /* If we never allocated a buffer, return an error. */ if (tslog_buf == NULL) return (-1); /* Store the buffer where the kernel can read it. */ return (file_addbuf("TSLOG", "TSLOG data", tslog_bufpos, tslog_buf)); } diff --git a/stand/efi/boot1/boot1.c b/stand/efi/boot1/boot1.c index 19627e870201..908baf400972 100644 --- a/stand/efi/boot1/boot1.c +++ b/stand/efi/boot1/boot1.c @@ -1,342 +1,341 @@ /*- * Copyright (c) 1998 Robert Nordier * All rights reserved. * Copyright (c) 2001 Robert Drehmel * All rights reserved. * Copyright (c) 2014 Nathan Whitehorn * All rights reserved. * Copyright (c) 2015 Eric McCorkle * All rights reserved. * * Redistribution and use in source and binary forms are freely * permitted provided that the above copyright notice and this * paragraph and the following disclaimer are duplicated in all * such forms. * * This software is provided "AS IS" and without any express or * implied warranties, including, without limitation, the implied * warranties of merchantability and fitness for a particular * purpose. */ -#include #include #include #include #include #include #include #include #include "boot_module.h" #include "paths.h" #include "proto.h" static void efi_panic(EFI_STATUS s, const char *fmt, ...) __dead2 __printflike(2, 3); const boot_module_t *boot_modules[] = { #ifdef EFI_ZFS_BOOT &zfs_module, #endif #ifdef EFI_UFS_BOOT &ufs_module #endif }; const UINTN num_boot_modules = nitems(boot_modules); static EFI_GUID BlockIoProtocolGUID = BLOCK_IO_PROTOCOL; static EFI_GUID DevicePathGUID = DEVICE_PATH_PROTOCOL; static EFI_GUID LoadedImageGUID = LOADED_IMAGE_PROTOCOL; static EFI_GUID ConsoleControlGUID = EFI_CONSOLE_CONTROL_PROTOCOL_GUID; static EFI_PHYSICAL_ADDRESS heap; static UINTN heapsize; /* * try_boot only returns if it fails to load the loader. If it succeeds * it simply boots, otherwise it returns the status of last EFI call. */ EFI_STATUS try_boot(const boot_module_t *mod, dev_info_t *dev, void *loaderbuf, size_t loadersize) { size_t bufsize, cmdsize; void *buf; char *cmd; EFI_HANDLE loaderhandle; EFI_LOADED_IMAGE *loaded_image; EFI_STATUS status; /* * Read in and parse the command line from /boot.config or /boot/config, * if present. We'll pass it the next stage via a simple ASCII * string. loader.efi has a hack for ASCII strings, so we'll use that to * keep the size down here. We only try to read the alternate file if * we get EFI_NOT_FOUND because all other errors mean that the boot_module * had troubles with the filesystem. We could return early, but we'll let * loading the actual kernel sort all that out. Since these files are * optional, we don't report errors in trying to read them. */ cmd = NULL; cmdsize = 0; status = mod->load(PATH_DOTCONFIG, dev, &buf, &bufsize); if (status == EFI_NOT_FOUND) status = mod->load(PATH_CONFIG, dev, &buf, &bufsize); if (status == EFI_SUCCESS) { cmdsize = bufsize + 1; cmd = malloc(cmdsize); if (cmd == NULL) goto errout; memcpy(cmd, buf, bufsize); cmd[bufsize] = '\0'; free(buf); buf = NULL; } /* * See if there's any env variables the module wants to set. If so, * append it to any config present. */ if (mod->extra_env != NULL) { const char *env = mod->extra_env(); if (env != NULL) { size_t newlen = cmdsize + strlen(env) + 1; cmd = realloc(cmd, newlen); if (cmd == NULL) goto errout; if (cmdsize > 0) strlcat(cmd, " ", newlen); strlcat(cmd, env, newlen); cmdsize = strlen(cmd); free(__DECONST(char *, env)); } } if ((status = BS->LoadImage(TRUE, IH, efi_devpath_last_node(dev->devpath), loaderbuf, loadersize, &loaderhandle)) != EFI_SUCCESS) { printf("Failed to load image provided by %s, size: %zu, (%lu)\n", mod->name, loadersize, EFI_ERROR_CODE(status)); goto errout; } status = OpenProtocolByHandle(loaderhandle, &LoadedImageGUID, (void **)&loaded_image); if (status != EFI_SUCCESS) { printf("Failed to query LoadedImage provided by %s (%lu)\n", mod->name, EFI_ERROR_CODE(status)); goto errout; } if (cmd != NULL) printf(" command args: %s\n", cmd); loaded_image->DeviceHandle = dev->devhandle; loaded_image->LoadOptionsSize = cmdsize; loaded_image->LoadOptions = cmd; DPRINTF("Starting '%s' in 5 seconds...", PATH_LOADER_EFI); DSTALL(1000000); DPRINTF("."); DSTALL(1000000); DPRINTF("."); DSTALL(1000000); DPRINTF("."); DSTALL(1000000); DPRINTF("."); DSTALL(1000000); DPRINTF(".\n"); if ((status = BS->StartImage(loaderhandle, NULL, NULL)) != EFI_SUCCESS) { printf("Failed to start image provided by %s (%lu)\n", mod->name, EFI_ERROR_CODE(status)); loaded_image->LoadOptionsSize = 0; loaded_image->LoadOptions = NULL; } errout: if (cmd != NULL) free(cmd); if (buf != NULL) free(buf); if (loaderbuf != NULL) free(loaderbuf); return (status); } EFI_STATUS efi_main(EFI_HANDLE Ximage, EFI_SYSTEM_TABLE *Xsystab) { EFI_HANDLE *handles; EFI_LOADED_IMAGE *img; EFI_DEVICE_PATH *imgpath; EFI_STATUS status; EFI_CONSOLE_CONTROL_PROTOCOL *ConsoleControl = NULL; SIMPLE_TEXT_OUTPUT_INTERFACE *conout = NULL; UINTN i, hsize, nhandles; CHAR16 *text; /* Basic initialization*/ ST = Xsystab; IH = Ximage; BS = ST->BootServices; RS = ST->RuntimeServices; heapsize = 64 * 1024 * 1024; status = BS->AllocatePages(AllocateAnyPages, EfiLoaderData, EFI_SIZE_TO_PAGES(heapsize), &heap); if (status != EFI_SUCCESS) { ST->ConOut->OutputString(ST->ConOut, __DECONST(CHAR16 *, L"Failed to allocate memory for heap.\r\n")); BS->Exit(IH, status, 0, NULL); } setheap((void *)(uintptr_t)heap, (void *)(uintptr_t)(heap + heapsize)); /* Set up the console, so printf works. */ status = BS->LocateProtocol(&ConsoleControlGUID, NULL, (VOID **)&ConsoleControl); if (status == EFI_SUCCESS) (void)ConsoleControl->SetMode(ConsoleControl, EfiConsoleControlScreenText); /* * Reset the console enable the cursor. Later we'll choose a better * console size through GOP/UGA. */ conout = ST->ConOut; conout->Reset(conout, TRUE); /* Explicitly set conout to mode 0, 80x25 */ conout->SetMode(conout, 0); conout->EnableCursor(conout, TRUE); conout->ClearScreen(conout); printf("\n>> FreeBSD EFI boot block\n"); printf(" Loader path: %s\n\n", PATH_LOADER_EFI); printf(" Initializing modules:"); for (i = 0; i < num_boot_modules; i++) { printf(" %s", boot_modules[i]->name); if (boot_modules[i]->init != NULL) boot_modules[i]->init(); } putchar('\n'); /* Fetch all the block I/O handles, we have to search through them later */ hsize = 0; BS->LocateHandle(ByProtocol, &BlockIoProtocolGUID, NULL, &hsize, NULL); handles = malloc(hsize); if (handles == NULL) efi_panic(EFI_OUT_OF_RESOURCES, "Failed to allocate %d handles\n", hsize); status = BS->LocateHandle(ByProtocol, &BlockIoProtocolGUID, NULL, &hsize, handles); if (status != EFI_SUCCESS) efi_panic(status, "Failed to get device handles\n"); nhandles = hsize / sizeof(*handles); /* Determine the devpath of our image so we can prefer it. */ status = OpenProtocolByHandle(IH, &LoadedImageGUID, (void **)&img); imgpath = NULL; if (status == EFI_SUCCESS) { text = efi_devpath_name(img->FilePath); if (text != NULL) { printf(" Load Path: %S\n", text); efi_setenv_freebsd_wcs("Boot1Path", text); efi_free_devpath_name(text); } status = OpenProtocolByHandle(img->DeviceHandle, &DevicePathGUID, (void **)&imgpath); if (status != EFI_SUCCESS) { DPRINTF("Failed to get image DevicePath (%lu)\n", EFI_ERROR_CODE(status)); } else { text = efi_devpath_name(imgpath); if (text != NULL) { printf(" Load Device: %S\n", text); efi_setenv_freebsd_wcs("Boot1Dev", text); efi_free_devpath_name(text); } } } choice_protocol(handles, nhandles, imgpath); /* If we get here, we're out of luck... */ efi_panic(EFI_LOAD_ERROR, "No bootable partitions found!"); } /* * add_device adds a device to the passed devinfo list. */ void add_device(dev_info_t **devinfop, dev_info_t *devinfo) { dev_info_t *dev; if (*devinfop == NULL) { *devinfop = devinfo; return; } for (dev = *devinfop; dev->next != NULL; dev = dev->next) ; dev->next = devinfo; } void efi_exit(EFI_STATUS s) { BS->FreePages(heap, EFI_SIZE_TO_PAGES(heapsize)); BS->Exit(IH, s, 0, NULL); } void exit(int error __unused) { efi_exit(EFI_LOAD_ERROR); } /* * OK. We totally give up. Exit back to EFI with a sensible status so * it can try the next option on the list. */ static void efi_panic(EFI_STATUS s, const char *fmt, ...) { va_list ap; printf("panic: "); va_start(ap, fmt); vprintf(fmt, ap); va_end(ap); printf("\n"); efi_exit(s); } int getchar(void) { return (-1); } void putchar(int c) { CHAR16 buf[2]; if (c == '\n') { buf[0] = '\r'; buf[1] = 0; ST->ConOut->OutputString(ST->ConOut, buf); } buf[0] = c; buf[1] = 0; ST->ConOut->OutputString(ST->ConOut, buf); } diff --git a/stand/efi/boot1/proto.c b/stand/efi/boot1/proto.c index cec4e2d1942c..6660d39a921b 100644 --- a/stand/efi/boot1/proto.c +++ b/stand/efi/boot1/proto.c @@ -1,223 +1,222 @@ /*- * Copyright (c) 1998 Robert Nordier * All rights reserved. * Copyright (c) 2001 Robert Drehmel * All rights reserved. * Copyright (c) 2014 Nathan Whitehorn * All rights reserved. * Copyright (c) 2015 Eric McCorkle * All rights reserved. * * Redistribution and use in source and binary forms are freely * permitted provided that the above copyright notice and this * paragraph and the following disclaimer are duplicated in all * such forms. * * This software is provided "AS IS" and without any express or * implied warranties, including, without limitation, the implied * warranties of merchantability and fitness for a particular * purpose. */ -#include #include #include #include #include #include #include #include #include "boot_module.h" #include "paths.h" #include "proto.h" static EFI_GUID BlockIoProtocolGUID = BLOCK_IO_PROTOCOL; static EFI_GUID DevicePathGUID = DEVICE_PATH_PROTOCOL; #ifndef EFI_DEBUG static const char *prio_str[] = { "error", "not supported", "good", "better" }; #endif /* * probe_handle determines if the passed handle represents a logical partition * if it does it uses each module in order to probe it and if successful it * returns EFI_SUCCESS. */ static int probe_handle(EFI_HANDLE h, EFI_DEVICE_PATH *imgpath) { dev_info_t *devinfo; EFI_BLOCK_IO *blkio; EFI_DEVICE_PATH *devpath; EFI_STATUS status; UINTN i; int preferred; /* Figure out if we're dealing with an actual partition. */ status = OpenProtocolByHandle(h, &DevicePathGUID, (void **)&devpath); if (status == EFI_UNSUPPORTED) return (0); if (status != EFI_SUCCESS) { DPRINTF("\nFailed to query DevicePath (%lu)\n", EFI_ERROR_CODE(status)); return (-1); } #ifdef EFI_DEBUG { CHAR16 *text = efi_devpath_name(devpath); DPRINTF("probing: %S ", text); efi_free_devpath_name(text); } #endif status = OpenProtocolByHandle(h, &BlockIoProtocolGUID, (void **)&blkio); if (status == EFI_UNSUPPORTED) return (0); if (status != EFI_SUCCESS) { DPRINTF("\nFailed to query BlockIoProtocol (%lu)\n", EFI_ERROR_CODE(status)); return (-1); } if (!blkio->Media->LogicalPartition) return (0); preferred = efi_devpath_same_disk(imgpath, devpath); /* Run through each module, see if it can load this partition */ devinfo = malloc(sizeof(*devinfo)); if (devinfo == NULL) { DPRINTF("\nFailed to allocate devinfo\n"); return (-1); } devinfo->dev = blkio; devinfo->devpath = devpath; devinfo->devhandle = h; devinfo->preferred = preferred; devinfo->next = NULL; for (i = 0; i < num_boot_modules; i++) { devinfo->devdata = NULL; status = boot_modules[i]->probe(devinfo); if (status == EFI_SUCCESS) return (preferred + 1); } free(devinfo); return (0); } /* * load_loader attempts to load the loader image data. * * It tries each module and its respective devices, identified by mod->probe, * in order until a successful load occurs at which point it returns EFI_SUCCESS * and EFI_NOT_FOUND otherwise. * * Only devices which have preferred matching the preferred parameter are tried. */ static EFI_STATUS load_loader(const boot_module_t **modp, dev_info_t **devinfop, void **bufp, size_t *bufsize, int preferred) { UINTN i; dev_info_t *dev; const boot_module_t *mod; for (i = 0; i < num_boot_modules; i++) { mod = boot_modules[i]; for (dev = mod->devices(); dev != NULL; dev = dev->next) { if (dev->preferred != preferred) continue; if (mod->load(PATH_LOADER_EFI, dev, bufp, bufsize) == EFI_SUCCESS) { *devinfop = dev; *modp = mod; return (EFI_SUCCESS); } } } return (EFI_NOT_FOUND); } void choice_protocol(EFI_HANDLE *handles, UINTN nhandles, EFI_DEVICE_PATH *imgpath) { UINT16 boot_current; size_t sz; UINT16 boot_order[100]; unsigned i; int rv; EFI_STATUS status; const boot_module_t *mod; dev_info_t *dev; void *loaderbuf; size_t loadersize; /* Report UEFI Boot Manager Protocol details */ boot_current = 0; sz = sizeof(boot_current); if (efi_global_getenv("BootCurrent", &boot_current, &sz) == EFI_SUCCESS) { printf(" BootCurrent: %04x\n", boot_current); sz = sizeof(boot_order); if (efi_global_getenv("BootOrder", &boot_order, &sz) == EFI_SUCCESS) { printf(" BootOrder:"); for (i = 0; i < sz / sizeof(boot_order[0]); i++) printf(" %04x%s", boot_order[i], boot_order[i] == boot_current ? "[*]" : ""); printf("\n"); } } #ifdef TEST_FAILURE /* * For testing failover scenarios, it's nice to be able to fail fast. * Define TEST_FAILURE to create a boot1.efi that always fails after * reporting the boot manager protocol details. */ BS->Exit(IH, EFI_OUT_OF_RESOURCES, 0, NULL); #endif /* Scan all partitions, probing with all modules. */ printf(" Probing %zu block devices...", nhandles); DPRINTF("\n"); for (i = 0; i < nhandles; i++) { rv = probe_handle(handles[i], imgpath); #ifdef EFI_DEBUG printf("%c", "x.+*"[rv + 1]); #else printf("%s\n", prio_str[rv + 1]); #endif } printf(" done\n"); /* Status summary. */ for (i = 0; i < num_boot_modules; i++) { printf(" "); boot_modules[i]->status(); } status = load_loader(&mod, &dev, &loaderbuf, &loadersize, 1); if (status != EFI_SUCCESS) { status = load_loader(&mod, &dev, &loaderbuf, &loadersize, 0); if (status != EFI_SUCCESS) { printf("Failed to load '%s'\n", PATH_LOADER_EFI); return; } } try_boot(mod, dev, loaderbuf, loadersize); } diff --git a/stand/efi/boot1/ufs_module.c b/stand/efi/boot1/ufs_module.c index 6848dd79eec0..0bdd5c018b06 100644 --- a/stand/efi/boot1/ufs_module.c +++ b/stand/efi/boot1/ufs_module.c @@ -1,226 +1,226 @@ /*- * Copyright (c) 1998 Robert Nordier * All rights reserved. * Copyright (c) 2001 Robert Drehmel * All rights reserved. * Copyright (c) 2014 Nathan Whitehorn * All rights reserved. * Copyright (c) 2015 Eric McCorkle * All rights reverved. * * 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 -#include + #include #include #include #include "boot_module.h" #define BSD_LABEL_BUFFER 8192 #define BSD_LABEL_OFFSET DEV_BSIZE static dev_info_t *devinfo; static dev_info_t *devices; static int dskread(void *buf, uint64_t lba, int nblk) { int size; EFI_STATUS status; lba += devinfo->partoff; lba = lba / (devinfo->dev->Media->BlockSize / DEV_BSIZE); size = nblk * DEV_BSIZE; status = devinfo->dev->ReadBlocks(devinfo->dev, devinfo->dev->Media->MediaId, lba, size, buf); if (status != EFI_SUCCESS) { DPRINTF("dskread: failed dev: %p, id: %u, lba: %ju, size: %d, " "status: %lu\n", devinfo->dev, devinfo->dev->Media->MediaId, (uintmax_t)lba, size, EFI_ERROR_CODE(status)); return (-1); } return (0); } #include "ufsread.c" static struct dmadat __dmadat __aligned(512); static char ufs_buffer[BSD_LABEL_BUFFER] __aligned(512); static int init_dev(dev_info_t* dev) { struct disklabel *dl; uint64_t bs; int ok; devinfo = dev; dmadat = &__dmadat; /* * First try offset 0. This is the typical GPT case where we have no * further partitioning (as well as the degenerate MBR case where * the bsdlabel has a 0 offset). */ devinfo->partoff = 0; ok = fsread(0, NULL, 0); if (ok >= 0) return (ok); /* * Next, we look for a bsdlabel. This is technically located in sector * 1. For 4k sectors, this offset is 4096, for 512b sectors it's * 512. However, we have to fall back to 512 here because we create * images that assume 512 byte blocks, but these can be put on devices * who have 4k (or other) block sizes. If there's a crazy block size, we * skip the 'at one sector' and go stright to checking at 512 bytes. * There are other offsets that are historic, but we don't probe those * since they were never used for MBR disks on FreeBSD on systems that * could boot UEFI. UEFI is little endian only, as are BSD labels. We * will retry fsread(0) only if there's a label found with a non-zero * offset. */ if (dskread(ufs_buffer, 0, BSD_LABEL_BUFFER / DEV_BSIZE) != 0) return (-1); dl = NULL; bs = devinfo->dev->Media->BlockSize; if (bs != 0 && bs <= BSD_LABEL_BUFFER / 2) dl = (struct disklabel *)&ufs_buffer[bs]; if (dl == NULL || dl->d_magic != BSD_MAGIC || dl->d_magic2 != BSD_MAGIC) dl = (struct disklabel *)&ufs_buffer[BSD_LABEL_OFFSET]; if (dl->d_magic != BSD_MAGIC || dl->d_magic2 != BSD_MAGIC || dl->d_partitions[0].p_offset == 0) return (-1); devinfo->partoff = dl->d_partitions[0].p_offset; return (fsread(0, NULL, 0)); } static EFI_STATUS probe(dev_info_t* dev) { if (init_dev(dev) < 0) return (EFI_UNSUPPORTED); add_device(&devices, dev); return (EFI_SUCCESS); } static EFI_STATUS load(const char *filepath, dev_info_t *dev, void **bufp, size_t *bufsize) { ufs_ino_t ino; size_t size; ssize_t read; void *buf; #ifdef EFI_DEBUG { CHAR16 *text = efi_devpath_name(dev->devpath); DPRINTF("UFS Loading '%s' from %S\n", filepath, text); efi_free_devpath_name(text); } #endif if (init_dev(dev) < 0) { DPRINTF("Failed to init device\n"); return (EFI_UNSUPPORTED); } if ((ino = lookup(filepath)) == 0) { DPRINTF("Failed to lookup '%s' (file not found?)\n", filepath); return (EFI_NOT_FOUND); } if (fsread_size(ino, NULL, 0, &size) < 0 || size <= 0) { printf("Failed to read size of '%s' ino: %d\n", filepath, ino); return (EFI_INVALID_PARAMETER); } buf = malloc(size); if (buf == NULL) { printf("Failed to allocate read buffer %zu for '%s'\n", size, filepath); return (EFI_OUT_OF_RESOURCES); } read = fsread(ino, buf, size); if ((size_t)read != size) { printf("Failed to read '%s' (%zd != %zu)\n", filepath, read, size); free(buf); return (EFI_INVALID_PARAMETER); } DPRINTF("Load complete\n"); *bufp = buf; *bufsize = size; return (EFI_SUCCESS); } static void status(void) { int i; dev_info_t *dev; for (dev = devices, i = 0; dev != NULL; dev = dev->next, i++) ; printf("%s found ", ufs_module.name); switch (i) { case 0: printf("no partitions\n"); break; case 1: printf("%d partition\n", i); break; default: printf("%d partitions\n", i); } } static dev_info_t * _devices(void) { return (devices); } const boot_module_t ufs_module = { .name = "UFS", .probe = probe, .load = load, .status = status, .devices = _devices }; diff --git a/stand/efi/boot1/zfs_module.c b/stand/efi/boot1/zfs_module.c index 01b95e06e38f..16722b33f0b9 100644 --- a/stand/efi/boot1/zfs_module.c +++ b/stand/efi/boot1/zfs_module.c @@ -1,298 +1,298 @@ /*- * Copyright (c) 2015 Eric McCorkle * 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 #include -#include + #include #include #include #include "boot_module.h" #include "libzfs.h" #include "zfsimpl.c" static dev_info_t *devices; static char zfs_bootonce[VDEV_PAD_SIZE]; uint64_t ldi_get_size(void *priv) { dev_info_t *devinfo = priv; return (devinfo->dev->Media->BlockSize * (devinfo->dev->Media->LastBlock + 1)); } static int vdev_read(vdev_t *vdev, void *priv, off_t off, void *buf, size_t bytes) { dev_info_t *devinfo; uint64_t lba; size_t size, remainder, rb_size, blksz; char *bouncebuf = NULL, *rb_buf; EFI_STATUS status; devinfo = (dev_info_t *)priv; lba = off / devinfo->dev->Media->BlockSize; remainder = off % devinfo->dev->Media->BlockSize; rb_buf = buf; rb_size = bytes; /* * If we have remainder from off, we need to add remainder part. * Since buffer must be multiple of the BlockSize, round it all up. */ size = roundup2(bytes + remainder, devinfo->dev->Media->BlockSize); blksz = size; if (remainder != 0 || size != bytes) { rb_size = devinfo->dev->Media->BlockSize; bouncebuf = malloc(rb_size); if (bouncebuf == NULL) { printf("vdev_read: out of memory\n"); return (-1); } rb_buf = bouncebuf; blksz = rb_size - remainder; } while (bytes > 0) { status = devinfo->dev->ReadBlocks(devinfo->dev, devinfo->dev->Media->MediaId, lba, rb_size, rb_buf); if (EFI_ERROR(status)) goto error; if (bytes < blksz) blksz = bytes; if (bouncebuf != NULL) memcpy(buf, rb_buf + remainder, blksz); buf = (void *)((uintptr_t)buf + blksz); bytes -= blksz; lba++; remainder = 0; blksz = rb_size; } free(bouncebuf); return (0); error: free(bouncebuf); DPRINTF("vdev_read: failed dev: %p, id: %u, lba: %ju, size: %zu," " rb_size: %zu, status: %lu\n", devinfo->dev, devinfo->dev->Media->MediaId, (uintmax_t)lba, bytes, rb_size, EFI_ERROR_CODE(status)); return (-1); } static EFI_STATUS probe(dev_info_t *dev) { spa_t *spa; dev_info_t *tdev; /* ZFS consumes the dev on success so we need a copy. */ tdev = malloc(sizeof(*dev)); if (tdev == NULL) { DPRINTF("Failed to allocate tdev\n"); return (EFI_OUT_OF_RESOURCES); } memcpy(tdev, dev, sizeof(*dev)); if (vdev_probe(vdev_read, NULL, tdev, &spa) != 0) { free(tdev); return (EFI_UNSUPPORTED); } dev->devdata = spa; add_device(&devices, dev); return (EFI_SUCCESS); } static EFI_STATUS load(const char *filepath, dev_info_t *devinfo, void **bufp, size_t *bufsize) { spa_t *spa; struct zfsmount zmount; dnode_phys_t dn; struct stat st; uint64_t rootobj; int err; void *buf; spa = devinfo->devdata; #ifdef EFI_DEBUG { CHAR16 *text = efi_devpath_name(devinfo->devpath); DPRINTF("load: '%s' spa: '%s', devpath: %S\n", filepath, spa->spa_name, text); efi_free_devpath_name(text); } #endif if ((err = zfs_spa_init(spa)) != 0) { DPRINTF("Failed to load pool '%s' (%d)\n", spa->spa_name, err); return (EFI_NOT_FOUND); } if (zfs_get_bootonce_spa(spa, OS_BOOTONCE, zfs_bootonce, sizeof(zfs_bootonce)) == 0) { /* * If bootonce attribute is present, use it as root dataset. * Any attempt to use it should clear the 'once' flag. Prior * to now, we'd not be able to clear it anyway. We don't care * if we can't find the files to boot, or if there's a problem * with it: we've tried to use it once we're able to mount the * ZFS dataset. * * Note: the attribute is prefixed with "zfs:" and suffixed * with ":". */ char *dname, *end; if (zfs_bootonce[0] != 'z' || zfs_bootonce[1] != 'f' || zfs_bootonce[2] != 's' || zfs_bootonce[3] != ':' || (dname = strchr(&zfs_bootonce[4], '/')) == NULL || (end = strrchr(&zfs_bootonce[4], ':')) == NULL) { printf("INVALID zfs bootonce: %s\n", zfs_bootonce); *zfs_bootonce = '\0'; rootobj = 0; } else { dname += 1; *end = '\0'; if (zfs_lookup_dataset(spa, dname, &rootobj) != 0) { printf("zfs bootonce dataset %s NOT FOUND\n", dname); *zfs_bootonce = '\0'; rootobj = 0; } else printf("zfs bootonce: %s\n", zfs_bootonce); *end = ':'; } } else { *zfs_bootonce = '\0'; rootobj = 0; } if ((err = zfs_mount_impl(spa, rootobj, &zmount)) != 0) { printf("Failed to mount pool '%s' (%d)\n", spa->spa_name, err); return (EFI_NOT_FOUND); } if ((err = zfs_lookup(&zmount, filepath, &dn)) != 0) { if (err == ENOENT) { DPRINTF("Failed to find '%s' on pool '%s' (%d)\n", filepath, spa->spa_name, err); return (EFI_NOT_FOUND); } printf("Failed to lookup '%s' on pool '%s' (%d)\n", filepath, spa->spa_name, err); return (EFI_INVALID_PARAMETER); } if ((err = zfs_dnode_stat(spa, &dn, &st)) != 0) { printf("Failed to stat '%s' on pool '%s' (%d)\n", filepath, spa->spa_name, err); return (EFI_INVALID_PARAMETER); } buf = malloc(st.st_size); if (buf == NULL) { printf("Failed to allocate load buffer %jd for pool '%s' for '%s' ", (intmax_t)st.st_size, spa->spa_name, filepath); return (EFI_INVALID_PARAMETER); } if ((err = dnode_read(spa, &dn, 0, buf, st.st_size)) != 0) { printf("Failed to read node from %s (%d)\n", spa->spa_name, err); free(buf); return (EFI_INVALID_PARAMETER); } *bufsize = st.st_size; *bufp = buf; return (EFI_SUCCESS); } static void status(void) { spa_t *spa; spa = STAILQ_FIRST(&zfs_pools); if (spa == NULL) { printf("%s found no pools\n", zfs_module.name); return; } printf("%s found the following pools:", zfs_module.name); STAILQ_FOREACH(spa, &zfs_pools, spa_link) printf(" %s", spa->spa_name); printf("\n"); } static const char * extra_env(void) { char *rv = NULL; /* So we return NULL if asprintf fails */ if (*zfs_bootonce == '\0') return NULL; asprintf(&rv, "zfs-bootonce=%s", zfs_bootonce); return (rv); } static void init(void) { zfs_init(); } static dev_info_t * _devices(void) { return (devices); } const boot_module_t zfs_module = { .name = "ZFS", .init = init, .probe = probe, .load = load, .status = status, .devices = _devices, .extra_env = extra_env, }; diff --git a/stand/efi/fdt/efi_fdt.c b/stand/efi/fdt/efi_fdt.c index 7010a272d9db..adf830e44182 100644 --- a/stand/efi/fdt/efi_fdt.c +++ b/stand/efi/fdt/efi_fdt.c @@ -1,67 +1,66 @@ /*- * Copyright (c) 2014 The FreeBSD Foundation * * This software was developed by Andrew Turner under * sponsorship from the FreeBSD Foundation. * * 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 #include #include #include #include #include "bootstrap.h" static EFI_GUID fdtdtb = FDT_TABLE_GUID; int fdt_platform_load_dtb(void) { struct fdt_header *hdr; hdr = efi_get_table(&fdtdtb); if (hdr == NULL) return (1); if (fdt_load_dtb_addr(hdr) != 0) return (1); printf("Using DTB provided by EFI at %p.\n", hdr); return (0); } void fdt_platform_load_overlays(void) { fdt_load_dtb_overlays(NULL); } void fdt_platform_fixups(void) { fdt_apply_overlays(); } diff --git a/stand/efi/gptboot/proto.c b/stand/efi/gptboot/proto.c index 54a229db7bca..e2face6c23e5 100644 --- a/stand/efi/gptboot/proto.c +++ b/stand/efi/gptboot/proto.c @@ -1,277 +1,276 @@ /*- * Copyright (c) 2019 Netflix, Inc * * 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 #include #include #include #include #include #include #include "boot_module.h" #include "paths.h" #include "proto.h" #include "gpt.h" #include static const uuid_t freebsd_ufs_uuid = GPT_ENT_TYPE_FREEBSD_UFS; static char secbuf[4096] __aligned(4096); static struct dsk dsk; static dev_info_t *devices = NULL; static dev_info_t *raw_device = NULL; static EFI_GUID BlockIoProtocolGUID = BLOCK_IO_PROTOCOL; static EFI_GUID DevicePathGUID = DEVICE_PATH_PROTOCOL; /* * Shim routine for the gpt code to read in the gpt table. The * devinfo is always going to be for the raw device. */ int drvread(struct dsk *dskp, void *buf, daddr_t lba, unsigned nblk) { int size; EFI_STATUS status; dev_info_t *devinfo = (dev_info_t *)dskp->devinfo; EFI_BLOCK_IO *dev = devinfo->dev; lba = lba / (dev->Media->BlockSize / DEV_BSIZE); size = nblk * DEV_BSIZE; status = dev->ReadBlocks(dev, dev->Media->MediaId, lba, size, buf); if (status != EFI_SUCCESS) { DPRINTF("dskread: failed dev: %p, id: %u, lba: %ju, size: %d, " "status: %lu\n", devinfo->dev, dev->Media->MediaId, (uintmax_t)lba, size, EFI_ERROR_CODE(status)); return (-1); } return (0); } /* * Shim routine for the gpt code to write in the gpt table. The * devinfo is always going to be for the raw device. */ int drvwrite(struct dsk *dskp, void *buf, daddr_t lba, unsigned nblk) { int size; EFI_STATUS status; dev_info_t *devinfo = (dev_info_t *)dskp->devinfo; EFI_BLOCK_IO *dev = devinfo->dev; if (dev->Media->ReadOnly) return -1; lba = lba / (dev->Media->BlockSize / DEV_BSIZE); size = nblk * DEV_BSIZE; status = dev->WriteBlocks(dev, dev->Media->MediaId, lba, size, buf); if (status != EFI_SUCCESS) { DPRINTF("dskread: failed dev: %p, id: %u, lba: %ju, size: %d, " "status: %lu\n", devinfo->dev, dev->Media->MediaId, (uintmax_t)lba, size, EFI_ERROR_CODE(status)); return (-1); } return (0); } /* * Return the number of LBAs the drive has. */ uint64_t drvsize(struct dsk *dskp) { dev_info_t *devinfo = (dev_info_t *)dskp->devinfo; EFI_BLOCK_IO *dev = devinfo->dev; return (dev->Media->LastBlock + 1); } static int partition_number(EFI_DEVICE_PATH *devpath) { EFI_DEVICE_PATH *md; HARDDRIVE_DEVICE_PATH *hd; md = efi_devpath_last_node(devpath); if (md == NULL) return (-1); if (DevicePathSubType(md) != MEDIA_HARDDRIVE_DP) return (-1); hd = (HARDDRIVE_DEVICE_PATH *)md; return (hd->PartitionNumber); } /* * Find the raw partition for the imgpath and save it */ static void probe_handle(EFI_HANDLE h, EFI_DEVICE_PATH *imgpath) { dev_info_t *devinfo; EFI_BLOCK_IO *blkio; EFI_DEVICE_PATH *devpath, *trimmed = NULL; EFI_STATUS status; /* Figure out if we're dealing with an actual partition. */ status = OpenProtocolByHandle(h, &DevicePathGUID, (void **)&devpath); if (status != EFI_SUCCESS) return; #ifdef EFI_DEBUG { CHAR16 *text = efi_devpath_name(devpath); DPRINTF("probing: %S ", text); efi_free_devpath_name(text); } #endif /* * The RAW device is the same as the imgpath with the last * MEDIA_DEVICE bit trimmed off. imgpath will end with the * MEDIA_DEVICE for the ESP we booted off of. */ if (!efi_devpath_same_disk(imgpath, devpath)) { trimmed = efi_devpath_trim(imgpath); if (!efi_devpath_match(trimmed, devpath)) { free(trimmed); DPRINTF("Not the same disk\n"); return; } } status = OpenProtocolByHandle(h, &BlockIoProtocolGUID, (void **)&blkio); if (status != EFI_SUCCESS) { DPRINTF("Can't get the block I/O protocol block\n"); return; } devinfo = malloc(sizeof(*devinfo)); if (devinfo == NULL) { DPRINTF("Failed to allocate devinfo\n"); return; } devinfo->dev = blkio; devinfo->devpath = devpath; devinfo->devhandle = h; devinfo->preferred = 1; devinfo->next = NULL; devinfo->devdata = NULL; if (trimmed == NULL) { DPRINTF("Found partition %d\n", partition_number(devpath)); add_device(&devices, devinfo); } else { free(trimmed); DPRINTF("Found raw device\n"); if (raw_device) { printf(BOOTPROG": Found two raw devices, inconceivable?\n"); return; } raw_device = devinfo; } } static void probe_handles(EFI_HANDLE *handles, UINTN nhandles, EFI_DEVICE_PATH *imgpath) { UINTN i; for (i = 0; i < nhandles; i++) probe_handle(handles[i], imgpath); } static dev_info_t * find_partition(int part) { dev_info_t *dev; if (part == 0) return (NULL); for (dev = devices; dev != NULL; dev = dev->next) if (partition_number(dev->devpath) == part) break; return (dev); } void choice_protocol(EFI_HANDLE *handles, UINTN nhandles, EFI_DEVICE_PATH *imgpath) { const boot_module_t *mod = &ufs_module; dev_info_t *bootdev; void *loaderbuf; size_t loadersize; int parts; const char *fn = PATH_LOADER_EFI; /* * Probe the provided handles to find the partitions that * are on the same drive. */ probe_handles(handles, nhandles, imgpath); dsk.devinfo = raw_device; if (dsk.devinfo == NULL) { printf(BOOTPROG": unable to find raw disk to read gpt\n"); return; } /* * Read in the GPT table, and then find the right partition. * gptread, gptfind and gptfaileboot are shared with the * BIOS version of the gptboot program. */ if (gptread(&dsk, secbuf) == -1) { printf(BOOTPROG ": unable to load GPT\n"); return; } // XXX: // real gptboot can parse a command line before trying this loop. // But since we don't parse anything at all, hard wire the partition // to be -1 (meaning look for the next one). parts = 0; while (gptfind(&freebsd_ufs_uuid, &dsk, -1) != -1) { parts++; bootdev = find_partition(dsk.part); if (bootdev == NULL) { printf(BOOTPROG": Can't find partition %d\n", dsk.part); goto next; } if (mod->load(fn, bootdev, &loaderbuf, &loadersize) != EFI_SUCCESS) { printf(BOOTPROG": Can't load %s from partition %d\n", fn, dsk.part); goto next; } try_boot(mod, bootdev, loaderbuf, loadersize); next: gptbootfailed(&dsk); } if (parts == 0) printf("%s: no UFS partition was found\n", BOOTPROG); } diff --git a/stand/efi/libefi/efi_console.c b/stand/efi/libefi/efi_console.c index 1758cd9d4d6d..4a3219ef3017 100644 --- a/stand/efi/libefi/efi_console.c +++ b/stand/efi/libefi/efi_console.c @@ -1,1428 +1,1427 @@ /*- * Copyright (c) 2000 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. */ -#include #include #include #include #include #include #include #include #include #include "bootstrap.h" extern EFI_GUID gop_guid; bool boot_services_active = true; /* boot services active first thing in main */ static EFI_GUID simple_input_ex_guid = EFI_SIMPLE_TEXT_INPUT_EX_PROTOCOL_GUID; static SIMPLE_TEXT_OUTPUT_INTERFACE *conout; static SIMPLE_INPUT_INTERFACE *conin; static EFI_SIMPLE_TEXT_INPUT_EX_PROTOCOL *coninex; static bool efi_started; static int mode; /* Does ConOut have serial console? */ static uint32_t utf8_left; static uint32_t utf8_partial; #ifdef TERM_EMU #define DEFAULT_FGCOLOR EFI_LIGHTGRAY #define DEFAULT_BGCOLOR EFI_BLACK #define MAXARGS 8 static int args[MAXARGS], argc; static int fg_c, bg_c, curx, cury; static int esc; void get_pos(int *x, int *y); void curs_move(int *_x, int *_y, int x, int y); static void CL(int); void HO(void); void end_term(void); #endif #define TEXT_ROWS 24 #define TEXT_COLS 80 static tf_bell_t efi_cons_bell; static tf_cursor_t efi_text_cursor; static tf_putchar_t efi_text_putchar; static tf_fill_t efi_text_fill; static tf_copy_t efi_text_copy; static tf_param_t efi_text_param; static tf_respond_t efi_cons_respond; static teken_funcs_t tf = { .tf_bell = efi_cons_bell, .tf_cursor = efi_text_cursor, .tf_putchar = efi_text_putchar, .tf_fill = efi_text_fill, .tf_copy = efi_text_copy, .tf_param = efi_text_param, .tf_respond = efi_cons_respond, }; static teken_funcs_t tfx = { .tf_bell = efi_cons_bell, .tf_cursor = gfx_fb_cursor, .tf_putchar = gfx_fb_putchar, .tf_fill = gfx_fb_fill, .tf_copy = gfx_fb_copy, .tf_param = gfx_fb_param, .tf_respond = efi_cons_respond, }; #define KEYBUFSZ 10 static unsigned keybuf[KEYBUFSZ]; /* keybuf for extended codes */ static int key_pending; static const unsigned char teken_color_to_efi_color[16] = { EFI_BLACK, EFI_RED, EFI_GREEN, EFI_BROWN, EFI_BLUE, EFI_MAGENTA, EFI_CYAN, EFI_LIGHTGRAY, EFI_DARKGRAY, EFI_LIGHTRED, EFI_LIGHTGREEN, EFI_YELLOW, EFI_LIGHTBLUE, EFI_LIGHTMAGENTA, EFI_LIGHTCYAN, EFI_WHITE }; static void efi_cons_probe(struct console *); static int efi_cons_init(int); void efi_cons_putchar(int); int efi_cons_getchar(void); void efi_cons_efiputchar(int); int efi_cons_poll(void); static void cons_draw_frame(teken_attr_t *); struct console efi_console = { "efi", "EFI console", C_WIDEOUT, efi_cons_probe, efi_cons_init, efi_cons_putchar, efi_cons_getchar, efi_cons_poll }; /* * This function is used to mark a rectangular image area so the scrolling * will know we need to copy the data from there. */ void term_image_display(teken_gfx_t *state, const teken_rect_t *r) { teken_pos_t p; int idx; if (screen_buffer == NULL) return; for (p.tp_row = r->tr_begin.tp_row; p.tp_row < r->tr_end.tp_row; p.tp_row++) { for (p.tp_col = r->tr_begin.tp_col; p.tp_col < r->tr_end.tp_col; p.tp_col++) { idx = p.tp_col + p.tp_row * state->tg_tp.tp_col; if (idx >= state->tg_tp.tp_col * state->tg_tp.tp_row) return; screen_buffer[idx].a.ta_format |= TF_IMAGE; } } } /* * Not implemented. */ static void efi_cons_bell(void *s __unused) { } static void efi_text_cursor(void *arg, const teken_pos_t *p) { teken_gfx_t *state = arg; UINTN col, row; if (!boot_services_active) return; row = p->tp_row; if (p->tp_row >= state->tg_tp.tp_row) row = state->tg_tp.tp_row - 1; col = p->tp_col; if (p->tp_col >= state->tg_tp.tp_col) col = state->tg_tp.tp_col - 1; conout->SetCursorPosition(conout, col, row); } static void efi_text_printchar(teken_gfx_t *state, const teken_pos_t *p, bool autoscroll) { UINTN a, attr; struct text_pixel *px; teken_color_t fg, bg, tmp; px = screen_buffer + p->tp_col + p->tp_row * state->tg_tp.tp_col; a = conout->Mode->Attribute; fg = teken_256to16(px->a.ta_fgcolor); bg = teken_256to16(px->a.ta_bgcolor); if (px->a.ta_format & TF_BOLD) fg |= TC_LIGHT; if (px->a.ta_format & TF_BLINK) bg |= TC_LIGHT; if (px->a.ta_format & TF_REVERSE) { tmp = fg; fg = bg; bg = tmp; } attr = EFI_TEXT_ATTR(teken_color_to_efi_color[fg], teken_color_to_efi_color[bg] & 0x7); conout->SetCursorPosition(conout, p->tp_col, p->tp_row); /* to prevent autoscroll, skip print of lower right char */ if (!autoscroll && p->tp_row == state->tg_tp.tp_row - 1 && p->tp_col == state->tg_tp.tp_col - 1) return; (void) conout->SetAttribute(conout, attr); efi_cons_efiputchar(px->c); (void) conout->SetAttribute(conout, a); } static void efi_text_putchar(void *s, const teken_pos_t *p, teken_char_t c, const teken_attr_t *a) { teken_gfx_t *state = s; EFI_STATUS status; int idx; if (!boot_services_active) return; idx = p->tp_col + p->tp_row * state->tg_tp.tp_col; if (idx >= state->tg_tp.tp_col * state->tg_tp.tp_row) return; screen_buffer[idx].c = c; screen_buffer[idx].a = *a; efi_text_printchar(s, p, false); } static void efi_text_fill(void *arg, const teken_rect_t *r, teken_char_t c, const teken_attr_t *a) { teken_gfx_t *state = arg; teken_pos_t p; if (!boot_services_active) return; if (state->tg_cursor_visible) conout->EnableCursor(conout, FALSE); for (p.tp_row = r->tr_begin.tp_row; p.tp_row < r->tr_end.tp_row; p.tp_row++) for (p.tp_col = r->tr_begin.tp_col; p.tp_col < r->tr_end.tp_col; p.tp_col++) efi_text_putchar(state, &p, c, a); if (state->tg_cursor_visible) conout->EnableCursor(conout, TRUE); } static void efi_text_copy_line(teken_gfx_t *state, int ncol, teken_pos_t *s, teken_pos_t *d, bool scroll) { unsigned soffset, doffset; teken_pos_t sp, dp; int x; soffset = s->tp_col + s->tp_row * state->tg_tp.tp_col; doffset = d->tp_col + d->tp_row * state->tg_tp.tp_col; sp = *s; dp = *d; for (x = 0; x < ncol; x++) { sp.tp_col = s->tp_col + x; dp.tp_col = d->tp_col + x; if (!is_same_pixel(&screen_buffer[soffset + x], &screen_buffer[doffset + x])) { screen_buffer[doffset + x] = screen_buffer[soffset + x]; if (!scroll) efi_text_printchar(state, &dp, false); } else if (scroll) { /* Draw last char and trigger scroll. */ if (dp.tp_col + 1 == state->tg_tp.tp_col && dp.tp_row + 1 == state->tg_tp.tp_row) { efi_text_printchar(state, &dp, true); } } } } static void efi_text_copy(void *arg, const teken_rect_t *r, const teken_pos_t *p) { teken_gfx_t *state = arg; unsigned doffset, soffset; teken_pos_t d, s; int nrow, ncol, x, y; /* Has to be signed - >= 0 comparison */ bool scroll = false; if (!boot_services_active) return; /* * Copying is a little tricky. We must make sure we do it in * correct order, to make sure we don't overwrite our own data. */ nrow = r->tr_end.tp_row - r->tr_begin.tp_row; ncol = r->tr_end.tp_col - r->tr_begin.tp_col; /* * Check if we do copy whole screen. */ if (p->tp_row == 0 && p->tp_col == 0 && nrow == state->tg_tp.tp_row - 2 && ncol == state->tg_tp.tp_col - 2) scroll = true; soffset = r->tr_begin.tp_col + r->tr_begin.tp_row * state->tg_tp.tp_col; doffset = p->tp_col + p->tp_row * state->tg_tp.tp_col; /* remove the cursor */ if (state->tg_cursor_visible) conout->EnableCursor(conout, FALSE); /* * Copy line by line. */ if (doffset <= soffset) { s = r->tr_begin; d = *p; for (y = 0; y < nrow; y++) { s.tp_row = r->tr_begin.tp_row + y; d.tp_row = p->tp_row + y; efi_text_copy_line(state, ncol, &s, &d, scroll); } } else { for (y = nrow - 1; y >= 0; y--) { s.tp_row = r->tr_begin.tp_row + y; d.tp_row = p->tp_row + y; efi_text_copy_line(state, ncol, &s, &d, false); } } /* display the cursor */ if (state->tg_cursor_visible) conout->EnableCursor(conout, TRUE); } static void efi_text_param(void *arg, int cmd, unsigned int value) { teken_gfx_t *state = arg; if (!boot_services_active) return; switch (cmd) { case TP_SETLOCALCURSOR: /* * 0 means normal (usually block), 1 means hidden, and * 2 means blinking (always block) for compatibility with * syscons. We don't support any changes except hiding, * so must map 2 to 0. */ value = (value == 1) ? 0 : 1; /* FALLTHROUGH */ case TP_SHOWCURSOR: if (value != 0) { conout->EnableCursor(conout, TRUE); state->tg_cursor_visible = true; } else { conout->EnableCursor(conout, FALSE); state->tg_cursor_visible = false; } break; default: /* Not yet implemented */ break; } } /* * Not implemented. */ static void efi_cons_respond(void *s __unused, const void *buf __unused, size_t len __unused) { } /* * Set up conin/conout/coninex to make sure we have input ready. */ static void efi_cons_probe(struct console *cp) { EFI_STATUS status; conout = ST->ConOut; conin = ST->ConIn; /* * Call SetMode to work around buggy firmware. */ status = conout->SetMode(conout, conout->Mode->Mode); if (coninex == NULL) { status = BS->OpenProtocol(ST->ConsoleInHandle, &simple_input_ex_guid, (void **)&coninex, IH, NULL, EFI_OPEN_PROTOCOL_GET_PROTOCOL); if (status != EFI_SUCCESS) coninex = NULL; } cp->c_flags |= C_PRESENTIN | C_PRESENTOUT; } static bool color_name_to_teken(const char *name, int *val) { int light = 0; if (strncasecmp(name, "light", 5) == 0) { name += 5; light = TC_LIGHT; } else if (strncasecmp(name, "bright", 6) == 0) { name += 6; light = TC_LIGHT; } if (strcasecmp(name, "black") == 0) { *val = TC_BLACK | light; return (true); } if (strcasecmp(name, "red") == 0) { *val = TC_RED | light; return (true); } if (strcasecmp(name, "green") == 0) { *val = TC_GREEN | light; return (true); } if (strcasecmp(name, "yellow") == 0 || strcasecmp(name, "brown") == 0) { *val = TC_YELLOW | light; return (true); } if (strcasecmp(name, "blue") == 0) { *val = TC_BLUE | light; return (true); } if (strcasecmp(name, "magenta") == 0) { *val = TC_MAGENTA | light; return (true); } if (strcasecmp(name, "cyan") == 0) { *val = TC_CYAN | light; return (true); } if (strcasecmp(name, "white") == 0) { *val = TC_WHITE | light; return (true); } return (false); } static int efi_set_colors(struct env_var *ev, int flags, const void *value) { int val = 0; char buf[3]; const void *evalue; const teken_attr_t *ap; teken_attr_t a; if (value == NULL) return (CMD_OK); if (color_name_to_teken(value, &val)) { snprintf(buf, sizeof (buf), "%d", val); evalue = buf; } else { char *end; long lval; errno = 0; lval = strtol(value, &end, 0); if (errno != 0 || *end != '\0' || lval < 0 || lval > 15) { printf("Allowed values are either ansi color name or " "number from range [0-15].\n"); return (CMD_OK); } val = (int)lval; evalue = value; } ap = teken_get_defattr(&gfx_state.tg_teken); a = *ap; if (strcmp(ev->ev_name, "teken.fg_color") == 0) { /* is it already set? */ if (ap->ta_fgcolor == val) return (CMD_OK); a.ta_fgcolor = val; } if (strcmp(ev->ev_name, "teken.bg_color") == 0) { /* is it already set? */ if (ap->ta_bgcolor == val) return (CMD_OK); a.ta_bgcolor = val; } /* Improve visibility */ if (a.ta_bgcolor == TC_WHITE) a.ta_bgcolor |= TC_LIGHT; teken_set_defattr(&gfx_state.tg_teken, &a); cons_draw_frame(&a); env_setenv(ev->ev_name, flags | EV_NOHOOK, evalue, NULL, NULL); teken_input(&gfx_state.tg_teken, "\e[2J", 4); return (CMD_OK); } #ifdef TERM_EMU /* Get cursor position. */ void get_pos(int *x, int *y) { *x = conout->Mode->CursorColumn; *y = conout->Mode->CursorRow; } /* Move cursor to x rows and y cols (0-based). */ void curs_move(int *_x, int *_y, int x, int y) { conout->SetCursorPosition(conout, x, y); if (_x != NULL) *_x = conout->Mode->CursorColumn; if (_y != NULL) *_y = conout->Mode->CursorRow; } /* Clear internal state of the terminal emulation code. */ void end_term(void) { esc = 0; argc = -1; } #endif static void efi_cons_rawputchar(int c) { int i; UINTN x, y; conout->QueryMode(conout, conout->Mode->Mode, &x, &y); if (c == '\t') { int n; n = 8 - ((conout->Mode->CursorColumn + 8) % 8); for (i = 0; i < n; i++) efi_cons_rawputchar(' '); } else { #ifndef TERM_EMU if (c == '\n') efi_cons_efiputchar('\r'); efi_cons_efiputchar(c); #else switch (c) { case '\r': curx = 0; efi_cons_efiputchar('\r'); return; case '\n': efi_cons_efiputchar('\n'); efi_cons_efiputchar('\r'); cury++; if (cury >= y) cury--; curx = 0; return; case '\b': if (curx > 0) { efi_cons_efiputchar('\b'); curx--; } return; default: efi_cons_efiputchar(c); curx++; if (curx > x-1) { curx = 0; cury++; } if (cury > y-1) { curx = 0; cury--; } } #endif } conout->EnableCursor(conout, TRUE); } #ifdef TERM_EMU /* Gracefully exit ESC-sequence processing in case of misunderstanding. */ static void bail_out(int c) { char buf[16], *ch; int i; if (esc) { efi_cons_rawputchar('\033'); if (esc != '\033') efi_cons_rawputchar(esc); for (i = 0; i <= argc; ++i) { sprintf(buf, "%d", args[i]); ch = buf; while (*ch) efi_cons_rawputchar(*ch++); } } efi_cons_rawputchar(c); end_term(); } /* Clear display from current position to end of screen. */ static void CD(void) { int i; UINTN x, y; get_pos(&curx, &cury); if (curx == 0 && cury == 0) { conout->ClearScreen(conout); end_term(); return; } conout->QueryMode(conout, conout->Mode->Mode, &x, &y); CL(0); /* clear current line from cursor to end */ for (i = cury + 1; i < y-1; i++) { curs_move(NULL, NULL, 0, i); CL(0); } curs_move(NULL, NULL, curx, cury); end_term(); } /* * Absolute cursor move to args[0] rows and args[1] columns * (the coordinates are 1-based). */ static void CM(void) { if (args[0] > 0) args[0]--; if (args[1] > 0) args[1]--; curs_move(&curx, &cury, args[1], args[0]); end_term(); } /* Home cursor (left top corner), also called from mode command. */ void HO(void) { argc = 1; args[0] = args[1] = 1; CM(); } /* Clear line from current position to end of line */ static void CL(int direction) { int i, len; UINTN x, y; CHAR16 *line; conout->QueryMode(conout, conout->Mode->Mode, &x, &y); switch (direction) { case 0: /* from cursor to end */ len = x - curx + 1; break; case 1: /* from beginning to cursor */ len = curx; break; case 2: /* entire line */ len = x; break; default: /* NOTREACHED */ __unreachable(); } if (cury == y - 1) len--; line = malloc(len * sizeof (CHAR16)); if (line == NULL) { printf("out of memory\n"); return; } for (i = 0; i < len; i++) line[i] = ' '; line[len-1] = 0; if (direction != 0) curs_move(NULL, NULL, 0, cury); conout->OutputString(conout, line); /* restore cursor position */ curs_move(NULL, NULL, curx, cury); free(line); end_term(); } static void get_arg(int c) { if (argc < 0) argc = 0; args[argc] *= 10; args[argc] += c - '0'; } #endif /* Emulate basic capabilities of cons25 terminal */ static void efi_term_emu(int c) { if (!boot_services_active) return; #ifdef TERM_EMU static int ansi_col[] = { 0, 4, 2, 6, 1, 5, 3, 7 }; int t, i; EFI_STATUS status; switch (esc) { case 0: switch (c) { case '\033': esc = c; break; default: efi_cons_rawputchar(c); break; } break; case '\033': switch (c) { case '[': esc = c; args[0] = 0; argc = -1; break; default: bail_out(c); break; } break; case '[': switch (c) { case ';': if (argc < 0) argc = 0; else if (argc + 1 >= MAXARGS) bail_out(c); else args[++argc] = 0; break; case 'H': /* ho = \E[H */ if (argc < 0) HO(); else if (argc == 1) CM(); else bail_out(c); break; case 'J': /* cd = \E[J */ if (argc < 0) CD(); else bail_out(c); break; case 'm': if (argc < 0) { fg_c = DEFAULT_FGCOLOR; bg_c = DEFAULT_BGCOLOR; } for (i = 0; i <= argc; ++i) { switch (args[i]) { case 0: /* back to normal */ fg_c = DEFAULT_FGCOLOR; bg_c = DEFAULT_BGCOLOR; break; case 1: /* bold */ fg_c |= 0x8; break; case 4: /* underline */ case 5: /* blink */ bg_c |= 0x8; break; case 7: /* reverse */ t = fg_c; fg_c = bg_c; bg_c = t; break; case 22: /* normal intensity */ fg_c &= ~0x8; break; case 24: /* not underline */ case 25: /* not blinking */ bg_c &= ~0x8; break; case 30: case 31: case 32: case 33: case 34: case 35: case 36: case 37: fg_c = ansi_col[args[i] - 30]; break; case 39: /* normal */ fg_c = DEFAULT_FGCOLOR; break; case 40: case 41: case 42: case 43: case 44: case 45: case 46: case 47: bg_c = ansi_col[args[i] - 40]; break; case 49: /* normal */ bg_c = DEFAULT_BGCOLOR; break; } } conout->SetAttribute(conout, EFI_TEXT_ATTR(fg_c, bg_c)); end_term(); break; default: if (isdigit(c)) get_arg(c); else bail_out(c); break; } break; default: bail_out(c); break; } #else efi_cons_rawputchar(c); #endif } static int env_screen_nounset(struct env_var *ev __unused) { if (gfx_state.tg_fb_type == FB_TEXT) return (0); return (EPERM); } static void cons_draw_frame(teken_attr_t *a) { teken_attr_t attr = *a; teken_color_t fg = a->ta_fgcolor; attr.ta_fgcolor = attr.ta_bgcolor; teken_set_defattr(&gfx_state.tg_teken, &attr); gfx_fb_drawrect(0, 0, gfx_state.tg_fb.fb_width, gfx_state.tg_origin.tp_row, 1); gfx_fb_drawrect(0, gfx_state.tg_fb.fb_height - gfx_state.tg_origin.tp_row - 1, gfx_state.tg_fb.fb_width, gfx_state.tg_fb.fb_height, 1); gfx_fb_drawrect(0, gfx_state.tg_origin.tp_row, gfx_state.tg_origin.tp_col, gfx_state.tg_fb.fb_height - gfx_state.tg_origin.tp_row - 1, 1); gfx_fb_drawrect( gfx_state.tg_fb.fb_width - gfx_state.tg_origin.tp_col - 1, gfx_state.tg_origin.tp_row, gfx_state.tg_fb.fb_width, gfx_state.tg_fb.fb_height, 1); attr.ta_fgcolor = fg; teken_set_defattr(&gfx_state.tg_teken, &attr); } bool cons_update_mode(bool use_gfx_mode) { UINTN cols, rows; const teken_attr_t *a; teken_attr_t attr; EFI_STATUS status; char env[10], *ptr; if (!efi_started) return (false); /* * Despite the use_gfx_mode, we want to make sure we call * efi_find_framebuffer(). This will populate the fb data, * which will be passed to kernel. */ if (efi_find_framebuffer(&gfx_state) == 0 && use_gfx_mode) { int roff, goff, boff; roff = ffs(gfx_state.tg_fb.fb_mask_red) - 1; goff = ffs(gfx_state.tg_fb.fb_mask_green) - 1; boff = ffs(gfx_state.tg_fb.fb_mask_blue) - 1; (void) generate_cons_palette(cmap, COLOR_FORMAT_RGB, gfx_state.tg_fb.fb_mask_red >> roff, roff, gfx_state.tg_fb.fb_mask_green >> goff, goff, gfx_state.tg_fb.fb_mask_blue >> boff, boff); } else { /* * Either text mode was asked by user or we failed to * find frame buffer. */ gfx_state.tg_fb_type = FB_TEXT; } status = conout->QueryMode(conout, conout->Mode->Mode, &cols, &rows); if (EFI_ERROR(status) || cols * rows == 0) { cols = TEXT_COLS; rows = TEXT_ROWS; } /* * When we have serial port listed in ConOut, use pre-teken emulator, * if built with. * The problem is, we can not output text on efi and comconsole when * efi also has comconsole bound. But then again, we need to have * terminal emulator for efi text mode to support the menu. * While teken is too expensive to be used on serial console, the * pre-teken emulator is light enough to be used on serial console. * * When doing multiple consoles (both serial and video), * also just use the old emulator. RB_MULTIPLE also implies * we're using a serial console. */ mode = parse_uefi_con_out(); if ((mode & (RB_SERIAL | RB_MULTIPLE)) == 0) { conout->EnableCursor(conout, FALSE); gfx_state.tg_cursor_visible = false; if (gfx_state.tg_fb_type == FB_TEXT) { gfx_state.tg_functions = &tf; /* ensure the following are not set for text mode */ unsetenv("screen.height"); unsetenv("screen.width"); unsetenv("screen.depth"); } else { uint32_t fb_height, fb_width; fb_height = gfx_state.tg_fb.fb_height; fb_width = gfx_state.tg_fb.fb_width; /* * setup_font() can adjust terminal size. * We can see two kind of bad happening. * We either can get too small console font - requested * terminal size is large, display resolution is * large, and we get very small font. * Or, we can get too large font - requested * terminal size is small and this will cause large * font to be selected. * Now, the setup_font() is updated to consider * display density and this should give us mostly * acceptable font. However, the catch is, not all * display devices will give us display density. * Still, we do hope, external monitors do - this is * where the display size will matter the most. * And for laptop screens, we should still get good * results by requesting 80x25 terminal. */ gfx_state.tg_tp.tp_row = 25; gfx_state.tg_tp.tp_col = 80; setup_font(&gfx_state, fb_height, fb_width); rows = gfx_state.tg_tp.tp_row; cols = gfx_state.tg_tp.tp_col; /* Point of origin in pixels. */ gfx_state.tg_origin.tp_row = (fb_height - (rows * gfx_state.tg_font.vf_height)) / 2; gfx_state.tg_origin.tp_col = (fb_width - (cols * gfx_state.tg_font.vf_width)) / 2; /* UEFI gop has depth 32. */ gfx_state.tg_glyph_size = gfx_state.tg_font.vf_height * gfx_state.tg_font.vf_width * 4; free(gfx_state.tg_glyph); gfx_state.tg_glyph = malloc(gfx_state.tg_glyph_size); if (gfx_state.tg_glyph == NULL) return (false); gfx_state.tg_functions = &tfx; snprintf(env, sizeof (env), "%d", fb_height); env_setenv("screen.height", EV_VOLATILE | EV_NOHOOK, env, env_noset, env_screen_nounset); snprintf(env, sizeof (env), "%d", fb_width); env_setenv("screen.width", EV_VOLATILE | EV_NOHOOK, env, env_noset, env_screen_nounset); snprintf(env, sizeof (env), "%d", gfx_state.tg_fb.fb_bpp); env_setenv("screen.depth", EV_VOLATILE | EV_NOHOOK, env, env_noset, env_screen_nounset); } /* Record our terminal screen size. */ gfx_state.tg_tp.tp_row = rows; gfx_state.tg_tp.tp_col = cols; teken_init(&gfx_state.tg_teken, gfx_state.tg_functions, &gfx_state); free(screen_buffer); screen_buffer = malloc(rows * cols * sizeof(*screen_buffer)); if (screen_buffer != NULL) { teken_set_winsize(&gfx_state.tg_teken, &gfx_state.tg_tp); a = teken_get_defattr(&gfx_state.tg_teken); attr = *a; /* * On first run, we set up the efi_set_colors() * callback. If the env is already set, we * pick up fg and bg color values from the environment. */ ptr = getenv("teken.fg_color"); if (ptr != NULL) { attr.ta_fgcolor = strtol(ptr, NULL, 10); ptr = getenv("teken.bg_color"); attr.ta_bgcolor = strtol(ptr, NULL, 10); teken_set_defattr(&gfx_state.tg_teken, &attr); } else { snprintf(env, sizeof(env), "%d", attr.ta_fgcolor); env_setenv("teken.fg_color", EV_VOLATILE, env, efi_set_colors, env_nounset); snprintf(env, sizeof(env), "%d", attr.ta_bgcolor); env_setenv("teken.bg_color", EV_VOLATILE, env, efi_set_colors, env_nounset); } } } if (screen_buffer == NULL) { conout->EnableCursor(conout, TRUE); #ifdef TERM_EMU conout->SetAttribute(conout, EFI_TEXT_ATTR(DEFAULT_FGCOLOR, DEFAULT_BGCOLOR)); end_term(); get_pos(&curx, &cury); curs_move(&curx, &cury, curx, cury); fg_c = DEFAULT_FGCOLOR; bg_c = DEFAULT_BGCOLOR; #endif } else { /* Improve visibility */ if (attr.ta_bgcolor == TC_WHITE) attr.ta_bgcolor |= TC_LIGHT; teken_set_defattr(&gfx_state.tg_teken, &attr); /* Draw frame around terminal area. */ cons_draw_frame(&attr); /* * Erase display, this will also fill our screen * buffer. */ teken_input(&gfx_state.tg_teken, "\e[2J", 4); gfx_state.tg_functions->tf_param(&gfx_state, TP_SHOWCURSOR, 1); } snprintf(env, sizeof (env), "%u", (unsigned)rows); setenv("LINES", env, 1); snprintf(env, sizeof (env), "%u", (unsigned)cols); setenv("COLUMNS", env, 1); return (true); } static int efi_cons_init(int arg) { EFI_STATUS status; if (efi_started) return (0); efi_started = true; gfx_framework_init(); if (cons_update_mode(gfx_state.tg_fb_type != FB_TEXT)) return (0); return (1); } static void input_partial(void) { unsigned i; uint32_t c; if (utf8_left == 0) return; for (i = 0; i < sizeof(utf8_partial); i++) { c = (utf8_partial >> (24 - (i << 3))) & 0xff; if (c != 0) efi_term_emu(c); } utf8_left = 0; utf8_partial = 0; } static void input_byte(uint8_t c) { if ((c & 0x80) == 0x00) { /* One-byte sequence. */ input_partial(); efi_term_emu(c); return; } if ((c & 0xe0) == 0xc0) { /* Two-byte sequence. */ input_partial(); utf8_left = 1; utf8_partial = c; return; } if ((c & 0xf0) == 0xe0) { /* Three-byte sequence. */ input_partial(); utf8_left = 2; utf8_partial = c; return; } if ((c & 0xf8) == 0xf0) { /* Four-byte sequence. */ input_partial(); utf8_left = 3; utf8_partial = c; return; } if ((c & 0xc0) == 0x80) { /* Invalid state? */ if (utf8_left == 0) { efi_term_emu(c); return; } utf8_left--; utf8_partial = (utf8_partial << 8) | c; if (utf8_left == 0) { uint32_t v, u; uint8_t b; v = 0; u = utf8_partial; b = (u >> 24) & 0xff; if (b != 0) { /* Four-byte sequence */ v = b & 0x07; b = (u >> 16) & 0xff; v = (v << 6) | (b & 0x3f); b = (u >> 8) & 0xff; v = (v << 6) | (b & 0x3f); b = u & 0xff; v = (v << 6) | (b & 0x3f); } else if ((b = (u >> 16) & 0xff) != 0) { v = b & 0x0f; /* Three-byte sequence */ b = (u >> 8) & 0xff; v = (v << 6) | (b & 0x3f); b = u & 0xff; v = (v << 6) | (b & 0x3f); } else if ((b = (u >> 8) & 0xff) != 0) { v = b & 0x1f; /* Two-byte sequence */ b = u & 0xff; v = (v << 6) | (b & 0x3f); } /* Send unicode char directly to console. */ efi_cons_efiputchar(v); utf8_partial = 0; } return; } /* Anything left is illegal in UTF-8 sequence. */ input_partial(); efi_term_emu(c); } void efi_cons_putchar(int c) { unsigned char ch = c; /* * Don't use Teken when we're doing pure serial, or a multiple console * with video "primary" because that's also serial. */ if ((mode & (RB_SERIAL | RB_MULTIPLE)) != 0 || screen_buffer == NULL) { input_byte(ch); return; } teken_input(&gfx_state.tg_teken, &ch, sizeof (ch)); } static int keybuf_getchar(void) { int i, c = 0; for (i = 0; i < KEYBUFSZ; i++) { if (keybuf[i] != 0) { c = keybuf[i]; keybuf[i] = 0; break; } } return (c); } static bool keybuf_ischar(void) { int i; for (i = 0; i < KEYBUFSZ; i++) { if (keybuf[i] != 0) return (true); } return (false); } /* * We are not reading input before keybuf is empty, so we are safe * just to fill keybuf from the beginning. */ static void keybuf_inschar(EFI_INPUT_KEY *key) { switch (key->ScanCode) { case SCAN_UP: /* UP */ keybuf[0] = 0x1b; /* esc */ keybuf[1] = '['; keybuf[2] = 'A'; break; case SCAN_DOWN: /* DOWN */ keybuf[0] = 0x1b; /* esc */ keybuf[1] = '['; keybuf[2] = 'B'; break; case SCAN_RIGHT: /* RIGHT */ keybuf[0] = 0x1b; /* esc */ keybuf[1] = '['; keybuf[2] = 'C'; break; case SCAN_LEFT: /* LEFT */ keybuf[0] = 0x1b; /* esc */ keybuf[1] = '['; keybuf[2] = 'D'; break; case SCAN_DELETE: keybuf[0] = CHAR_BACKSPACE; break; case SCAN_ESC: keybuf[0] = 0x1b; /* esc */ break; default: keybuf[0] = key->UnicodeChar; break; } } static bool efi_readkey(void) { EFI_STATUS status; EFI_INPUT_KEY key; status = conin->ReadKeyStroke(conin, &key); if (status == EFI_SUCCESS) { keybuf_inschar(&key); return (true); } return (false); } static bool efi_readkey_ex(void) { EFI_STATUS status; EFI_INPUT_KEY *kp; EFI_KEY_DATA key_data; uint32_t kss; status = coninex->ReadKeyStrokeEx(coninex, &key_data); if (status == EFI_SUCCESS) { kss = key_data.KeyState.KeyShiftState; kp = &key_data.Key; if (kss & EFI_SHIFT_STATE_VALID) { /* * quick mapping to control chars, replace with * map lookup later. */ if (kss & EFI_RIGHT_CONTROL_PRESSED || kss & EFI_LEFT_CONTROL_PRESSED) { if (kp->UnicodeChar >= 'a' && kp->UnicodeChar <= 'z') { kp->UnicodeChar -= 'a'; kp->UnicodeChar++; } } } /* * The shift state and/or toggle state may not be valid, * but we still can have ScanCode or UnicodeChar. */ if (kp->ScanCode == 0 && kp->UnicodeChar == 0) return (false); keybuf_inschar(kp); return (true); } return (false); } int efi_cons_getchar(void) { int c; if ((c = keybuf_getchar()) != 0) return (c); if (!boot_services_active) return (-1); key_pending = 0; if (coninex == NULL) { if (efi_readkey()) return (keybuf_getchar()); } else { if (efi_readkey_ex()) return (keybuf_getchar()); } return (-1); } int efi_cons_poll(void) { EFI_STATUS status; if (keybuf_ischar() || key_pending) return (1); if (!boot_services_active) return (0); /* * Some EFI implementation (u-boot for example) do not support * WaitForKey(). * CheckEvent() can clear the signaled state. */ if (coninex != NULL) { if (coninex->WaitForKeyEx == NULL) { key_pending = efi_readkey_ex(); } else { status = BS->CheckEvent(coninex->WaitForKeyEx); key_pending = status == EFI_SUCCESS; } } else { if (conin->WaitForKey == NULL) { key_pending = efi_readkey(); } else { status = BS->CheckEvent(conin->WaitForKey); key_pending = status == EFI_SUCCESS; } } return (key_pending); } /* Plain direct access to EFI OutputString(). */ void efi_cons_efiputchar(int c) { CHAR16 buf[2]; EFI_STATUS status; buf[0] = c; buf[1] = 0; /* terminate string */ status = conout->TestString(conout, buf); if (EFI_ERROR(status)) buf[0] = '?'; conout->OutputString(conout, buf); } diff --git a/stand/efi/libefi/efichar.c b/stand/efi/libefi/efichar.c index 0703c167de47..86642d325754 100644 --- a/stand/efi/libefi/efichar.c +++ b/stand/efi/libefi/efichar.c @@ -1,202 +1,201 @@ /*- * Copyright (c) 2010 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 #include #include #ifdef _STANDALONE #include #else #include #include #include #include #include #include #endif #include "efichar.h" int ucs2len(const efi_char *str) { int i; i = 0; while (*str++) i++; return (i); } /* * If nm were converted to utf8, what what would strlen * return on the resulting string? */ static size_t utf8_len_of_ucs2(const efi_char *nm) { size_t len; efi_char c; len = 0; while (*nm) { c = *nm++; if (c > 0x7ff) len += 3; else if (c > 0x7f) len += 2; else len++; } return (len); } int ucs2_to_utf8(const efi_char *nm, char **name) { size_t len, sz; efi_char c; char *cp; int freeit = *name == NULL; sz = utf8_len_of_ucs2(nm) + 1; len = 0; if (*name != NULL) cp = *name; else cp = *name = malloc(sz); if (*name == NULL) return (ENOMEM); while (*nm) { c = *nm++; if (c > 0x7ff) { if (len++ < sz) *cp++ = (char)(0xE0 | (c >> 12)); if (len++ < sz) *cp++ = (char)(0x80 | ((c >> 6) & 0x3f)); if (len++ < sz) *cp++ = (char)(0x80 | (c & 0x3f)); } else if (c > 0x7f) { if (len++ < sz) *cp++ = (char)(0xC0 | ((c >> 6) & 0x1f)); if (len++ < sz) *cp++ = (char)(0x80 | (c & 0x3f)); } else { if (len++ < sz) *cp++ = (char)(c & 0x7f); } } if (len >= sz) { /* Absent bugs, we'll never return EOVERFLOW */ if (freeit) { free(*name); *name = NULL; } return (EOVERFLOW); } *cp++ = '\0'; return (0); } int utf8_to_ucs2(const char *name, efi_char **nmp, size_t *len) { efi_char *nm; size_t sz; uint32_t ucs4; int c, bytes; int freeit = *nmp == NULL; sz = strlen(name) * 2 + 2; if (*nmp == NULL) *nmp = malloc(sz); if (*nmp == NULL) return (ENOMEM); nm = *nmp; *len = sz; ucs4 = 0; bytes = 0; while (sz > 1 && *name != '\0') { c = *name++; /* * Conditionalize on the two major character types: * initial and followup characters. */ if ((c & 0xc0) != 0x80) { /* Initial characters. */ if (bytes != 0) goto ilseq; if ((c & 0xf8) == 0xf0) { ucs4 = c & 0x07; bytes = 3; } else if ((c & 0xf0) == 0xe0) { ucs4 = c & 0x0f; bytes = 2; } else if ((c & 0xe0) == 0xc0) { ucs4 = c & 0x1f; bytes = 1; } else { ucs4 = c & 0x7f; bytes = 0; } } else { /* Followup characters. */ if (bytes > 0) { ucs4 = (ucs4 << 6) + (c & 0x3f); bytes--; } else if (bytes == 0) goto ilseq; } if (bytes == 0) { if (ucs4 > 0xffff) goto ilseq; *nm++ = (efi_char)ucs4; sz -= 2; } } if (sz < 2) { if (freeit) { free(nm); *nmp = NULL; } return (EDOOFUS); } sz -= 2; *nm = 0; *len -= sz; return (0); ilseq: if (freeit) { free(nm); *nmp = NULL; } return (EILSEQ); } diff --git a/stand/efi/libefi/efihttp.c b/stand/efi/libefi/efihttp.c index 081b34336cb0..bcc0f7e4d79e 100644 --- a/stand/efi/libefi/efihttp.c +++ b/stand/efi/libefi/efihttp.c @@ -1,796 +1,795 @@ /*- * SPDX-License-Identifier: BSD-2-Clause * * Copyright (c) 2019 Intel Corporation * * 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 #include #include #include #include #include #include #include #include #include #include #include #include /* Poll timeout in milliseconds */ static const int EFIHTTP_POLL_TIMEOUT = 300000; static EFI_GUID http_guid = EFI_HTTP_PROTOCOL_GUID; static EFI_GUID httpsb_guid = EFI_HTTP_SERVICE_BINDING_PROTOCOL_GUID; static EFI_GUID ip4config2_guid = EFI_IP4_CONFIG2_PROTOCOL_GUID; static bool efihttp_init_done = false; static int efihttp_dev_init(void); static int efihttp_dev_strategy(void *devdata, int rw, daddr_t blk, size_t size, char *buf, size_t *rsize); static int efihttp_dev_open(struct open_file *f, ...); static int efihttp_dev_close(struct open_file *f); static int efihttp_fs_open(const char *path, struct open_file *f); static int efihttp_fs_close(struct open_file *f); static int efihttp_fs_read(struct open_file *f, void *buf, size_t size, size_t *resid); static int efihttp_fs_write(struct open_file *f, const void *buf, size_t size, size_t *resid); static off_t efihttp_fs_seek(struct open_file *f, off_t offset, int where); static int efihttp_fs_stat(struct open_file *f, struct stat *sb); static int efihttp_fs_readdir(struct open_file *f, struct dirent *d); struct open_efihttp { EFI_HTTP_PROTOCOL *http; EFI_HANDLE http_handle; EFI_HANDLE dev_handle; char *uri_base; }; struct file_efihttp { ssize_t size; off_t offset; char *path; bool is_dir; }; struct devsw efihttp_dev = { .dv_name = "http", .dv_type = DEVT_NET, .dv_init = efihttp_dev_init, .dv_strategy = efihttp_dev_strategy, .dv_open = efihttp_dev_open, .dv_close = efihttp_dev_close, .dv_ioctl = noioctl, .dv_print = NULL, .dv_cleanup = nullsys, }; struct fs_ops efihttp_fsops = { .fs_name = "efihttp", .fo_open = efihttp_fs_open, .fo_close = efihttp_fs_close, .fo_read = efihttp_fs_read, .fo_write = efihttp_fs_write, .fo_seek = efihttp_fs_seek, .fo_stat = efihttp_fs_stat, .fo_readdir = efihttp_fs_readdir, }; static void EFIAPI notify(EFI_EVENT event __unused, void *context) { bool *b; b = (bool *)context; *b = true; } static int setup_ipv4_config2(EFI_HANDLE handle, MAC_ADDR_DEVICE_PATH *mac, IPv4_DEVICE_PATH *ipv4, DNS_DEVICE_PATH *dns) { EFI_IP4_CONFIG2_PROTOCOL *ip4config2; EFI_STATUS status; status = BS->OpenProtocol(handle, &ip4config2_guid, (void **)&ip4config2, IH, NULL, EFI_OPEN_PROTOCOL_GET_PROTOCOL); if (EFI_ERROR(status)) return (efi_status_to_errno(status)); if (ipv4 != NULL) { if (mac != NULL) { setenv("boot.netif.hwaddr", ether_sprintf((u_char *)mac->MacAddress.Addr), 1); } setenv("boot.netif.ip", inet_ntoa(*(struct in_addr *)ipv4->LocalIpAddress.Addr), 1); setenv("boot.netif.netmask", intoa(*(n_long *)ipv4->SubnetMask.Addr), 1); setenv("boot.netif.gateway", inet_ntoa(*(struct in_addr *)ipv4->GatewayIpAddress.Addr), 1); status = ip4config2->SetData(ip4config2, Ip4Config2DataTypePolicy, sizeof(EFI_IP4_CONFIG2_POLICY), &(EFI_IP4_CONFIG2_POLICY) { Ip4Config2PolicyStatic }); if (EFI_ERROR(status)) return (efi_status_to_errno(status)); status = ip4config2->SetData(ip4config2, Ip4Config2DataTypeManualAddress, sizeof(EFI_IP4_CONFIG2_MANUAL_ADDRESS), &(EFI_IP4_CONFIG2_MANUAL_ADDRESS) { .Address = ipv4->LocalIpAddress, .SubnetMask = ipv4->SubnetMask }); if (EFI_ERROR(status)) return (efi_status_to_errno(status)); if (ipv4->GatewayIpAddress.Addr[0] != 0) { status = ip4config2->SetData(ip4config2, Ip4Config2DataTypeGateway, sizeof(EFI_IPv4_ADDRESS), &ipv4->GatewayIpAddress); if (EFI_ERROR(status)) return (efi_status_to_errno(status)); } if (dns != NULL) { status = ip4config2->SetData(ip4config2, Ip4Config2DataTypeDnsServer, sizeof(EFI_IPv4_ADDRESS), &dns->DnsServerIp); if (EFI_ERROR(status)) return (efi_status_to_errno(status)); } } else { status = ip4config2->SetData(ip4config2, Ip4Config2DataTypePolicy, sizeof(EFI_IP4_CONFIG2_POLICY), &(EFI_IP4_CONFIG2_POLICY) { Ip4Config2PolicyDhcp }); if (EFI_ERROR(status)) return (efi_status_to_errno(status)); } return (0); } static int efihttp_dev_init(void) { EFI_DEVICE_PATH *imgpath, *devpath; URI_DEVICE_PATH *uri; EFI_HANDLE handle; EFI_STATUS status; int err; bool found_http; imgpath = efi_lookup_image_devpath(IH); if (imgpath == NULL) return (ENXIO); devpath = imgpath; found_http = false; for (; !IsDevicePathEnd(devpath); devpath = NextDevicePathNode(devpath)) { if (DevicePathType(devpath) != MESSAGING_DEVICE_PATH || DevicePathSubType(devpath) != MSG_URI_DP) continue; uri = (URI_DEVICE_PATH *)devpath; if (strncmp("http", (const char *)uri->Uri, 4) == 0) found_http = true; } if (!found_http) return (ENXIO); status = BS->LocateDevicePath(&httpsb_guid, &imgpath, &handle); if (EFI_ERROR(status)) return (efi_status_to_errno(status)); err = efi_register_handles(&efihttp_dev, &handle, NULL, 1); if (!err) efihttp_init_done = true; return (err); } static int efihttp_dev_strategy(void *devdata __unused, int rw __unused, daddr_t blk __unused, size_t size __unused, char *buf __unused, size_t *rsize __unused) { return (EIO); } static int efihttp_dev_open(struct open_file *f, ...) { EFI_HTTP_CONFIG_DATA config; EFI_HTTPv4_ACCESS_POINT config_access; DNS_DEVICE_PATH *dns; EFI_DEVICE_PATH *devpath, *imgpath; EFI_SERVICE_BINDING_PROTOCOL *sb; IPv4_DEVICE_PATH *ipv4; MAC_ADDR_DEVICE_PATH *mac; URI_DEVICE_PATH *uri; struct devdesc *dev; struct open_efihttp *oh; char *c; EFI_HANDLE handle; EFI_STATUS status; int err, len; if (!efihttp_init_done) return (ENXIO); imgpath = efi_lookup_image_devpath(IH); if (imgpath == NULL) return (ENXIO); devpath = imgpath; status = BS->LocateDevicePath(&httpsb_guid, &devpath, &handle); if (EFI_ERROR(status)) return (efi_status_to_errno(status)); mac = NULL; ipv4 = NULL; dns = NULL; uri = NULL; for (; !IsDevicePathEnd(imgpath); imgpath = NextDevicePathNode(imgpath)) { if (DevicePathType(imgpath) != MESSAGING_DEVICE_PATH) continue; switch (DevicePathSubType(imgpath)) { case MSG_MAC_ADDR_DP: mac = (MAC_ADDR_DEVICE_PATH *)imgpath; break; case MSG_IPv4_DP: ipv4 = (IPv4_DEVICE_PATH *)imgpath; break; case MSG_DNS_DP: dns = (DNS_DEVICE_PATH *)imgpath; break; case MSG_URI_DP: uri = (URI_DEVICE_PATH *)imgpath; break; default: break; } } if (uri == NULL) return (ENXIO); err = setup_ipv4_config2(handle, mac, ipv4, dns); if (err) return (err); oh = calloc(1, sizeof(struct open_efihttp)); if (!oh) return (ENOMEM); oh->dev_handle = handle; dev = (struct devdesc *)f->f_devdata; dev->d_opendata = oh; status = BS->OpenProtocol(handle, &httpsb_guid, (void **)&sb, IH, NULL, EFI_OPEN_PROTOCOL_GET_PROTOCOL); if (EFI_ERROR(status)) { err = efi_status_to_errno(status); goto end; } status = sb->CreateChild(sb, &oh->http_handle); if (EFI_ERROR(status)) { err = efi_status_to_errno(status); goto end; } status = BS->OpenProtocol(oh->http_handle, &http_guid, (void **)&oh->http, IH, NULL, EFI_OPEN_PROTOCOL_GET_PROTOCOL); if (EFI_ERROR(status)) { sb->DestroyChild(sb, oh->http_handle); err = efi_status_to_errno(status); goto end; } config.HttpVersion = HttpVersion11; config.TimeOutMillisec = 0; config.LocalAddressIsIPv6 = FALSE; config.AccessPoint.IPv4Node = &config_access; config_access.UseDefaultAddress = TRUE; config_access.LocalPort = 0; status = oh->http->Configure(oh->http, &config); if (EFI_ERROR(status)) { sb->DestroyChild(sb, oh->http_handle); err = efi_status_to_errno(status); goto end; } /* * Here we make attempt to construct a "base" URI by stripping * the last two path components from the loaded URI under the * assumption that it is something like: * * http://127.0.0.1/foo/boot/loader.efi * * hoping to arriving at: * * http://127.0.0.1/foo/ */ len = DevicePathNodeLength(&uri->Header) - sizeof(URI_DEVICE_PATH); oh->uri_base = malloc(len + 1); if (oh->uri_base == NULL) { err = ENOMEM; goto end; } strncpy(oh->uri_base, (const char *)uri->Uri, len); oh->uri_base[len] = '\0'; c = strrchr(oh->uri_base, '/'); if (c != NULL) *c = '\0'; c = strrchr(oh->uri_base, '/'); if (c != NULL && *(c + 1) != '\0') *(c + 1) = '\0'; err = 0; end: if (err != 0) { free(dev->d_opendata); dev->d_opendata = NULL; } return (err); } static int efihttp_dev_close(struct open_file *f) { EFI_SERVICE_BINDING_PROTOCOL *sb; struct devdesc *dev; struct open_efihttp *oh; EFI_STATUS status; dev = (struct devdesc *)f->f_devdata; oh = (struct open_efihttp *)dev->d_opendata; status = BS->OpenProtocol(oh->dev_handle, &httpsb_guid, (void **)&sb, IH, NULL, EFI_OPEN_PROTOCOL_GET_PROTOCOL); if (EFI_ERROR(status)) return (efi_status_to_errno(status)); sb->DestroyChild(sb, oh->http_handle); free(oh->uri_base); free(oh); dev->d_opendata = NULL; return (0); } static int _efihttp_fs_open(const char *path, struct open_file *f) { EFI_HTTP_CONFIG_DATA config; EFI_HTTPv4_ACCESS_POINT config_access; EFI_HTTP_TOKEN token; EFI_HTTP_MESSAGE message; EFI_HTTP_REQUEST_DATA request; EFI_HTTP_RESPONSE_DATA response; EFI_HTTP_HEADER headers[3]; char *host, *hostp; char *c; struct devdesc *dev; struct open_efihttp *oh; struct file_efihttp *fh; EFI_STATUS status; UINTN i; int polltime; bool done; dev = (struct devdesc *)f->f_devdata; oh = (struct open_efihttp *)dev->d_opendata; fh = calloc(1, sizeof(struct file_efihttp)); if (fh == NULL) return (ENOMEM); f->f_fsdata = fh; fh->path = strdup(path); /* * Reset the HTTP state. * * EDK II's persistent HTTP connection handling is graceless, * assuming that all connections are persistent regardless of * any Connection: header or HTTP version reported by the * server, and failing to send requests when a more sane * implementation would seem to be just reestablishing the * closed connection. * * In the hopes of having some robustness, we indicate to the * server that we will close the connection by using a * Connection: close header. And then here we manually * unconfigure and reconfigure the http instance to force the * connection closed. */ memset(&config, 0, sizeof(config)); memset(&config_access, 0, sizeof(config_access)); config.AccessPoint.IPv4Node = &config_access; status = oh->http->GetModeData(oh->http, &config); if (EFI_ERROR(status)) return (efi_status_to_errno(status)); status = oh->http->Configure(oh->http, NULL); if (EFI_ERROR(status)) return (efi_status_to_errno(status)); status = oh->http->Configure(oh->http, &config); if (EFI_ERROR(status)) return (efi_status_to_errno(status)); /* Send the read request */ done = false; status = BS->CreateEvent(EVT_NOTIFY_SIGNAL, TPL_CALLBACK, notify, &done, &token.Event); if (EFI_ERROR(status)) return (efi_status_to_errno(status)); /* extract the host portion of the URL */ host = strdup(oh->uri_base); if (host == NULL) return (ENOMEM); hostp = host; /* Remove the protocol scheme */ c = strchr(host, '/'); if (c != NULL && *(c + 1) == '/') hostp = (c + 2); /* Remove any path information */ c = strchr(hostp, '/'); if (c != NULL) *c = '\0'; token.Status = EFI_NOT_READY; token.Message = &message; message.Data.Request = &request; message.HeaderCount = 3; message.Headers = headers; message.BodyLength = 0; message.Body = NULL; request.Method = HttpMethodGet; request.Url = calloc(strlen(oh->uri_base) + strlen(path) + 1, 2); headers[0].FieldName = (CHAR8 *)"Host"; headers[0].FieldValue = (CHAR8 *)hostp; headers[1].FieldName = (CHAR8 *)"Connection"; headers[1].FieldValue = (CHAR8 *)"close"; headers[2].FieldName = (CHAR8 *)"Accept"; headers[2].FieldValue = (CHAR8 *)"*/*"; cpy8to16(oh->uri_base, request.Url, strlen(oh->uri_base)); cpy8to16(path, request.Url + strlen(oh->uri_base), strlen(path)); status = oh->http->Request(oh->http, &token); free(request.Url); free(host); if (EFI_ERROR(status)) { BS->CloseEvent(token.Event); return (efi_status_to_errno(status)); } polltime = 0; while (!done && polltime < EFIHTTP_POLL_TIMEOUT) { status = oh->http->Poll(oh->http); if (EFI_ERROR(status)) break; if (!done) { delay(100 * 1000); polltime += 100; } } BS->CloseEvent(token.Event); if (EFI_ERROR(token.Status)) return (efi_status_to_errno(token.Status)); /* Wait for the read response */ done = false; status = BS->CreateEvent(EVT_NOTIFY_SIGNAL, TPL_CALLBACK, notify, &done, &token.Event); if (EFI_ERROR(status)) return (efi_status_to_errno(status)); token.Status = EFI_NOT_READY; token.Message = &message; message.Data.Response = &response; message.HeaderCount = 0; message.Headers = NULL; message.BodyLength = 0; message.Body = NULL; response.StatusCode = HTTP_STATUS_UNSUPPORTED_STATUS; status = oh->http->Response(oh->http, &token); if (EFI_ERROR(status)) { BS->CloseEvent(token.Event); return (efi_status_to_errno(status)); } polltime = 0; while (!done && polltime < EFIHTTP_POLL_TIMEOUT) { status = oh->http->Poll(oh->http); if (EFI_ERROR(status)) break; if (!done) { delay(100 * 1000); polltime += 100; } } BS->CloseEvent(token.Event); if (EFI_ERROR(token.Status)) { BS->FreePool(message.Headers); return (efi_status_to_errno(token.Status)); } if (response.StatusCode != HTTP_STATUS_200_OK) { BS->FreePool(message.Headers); return (EIO); } fh->size = 0; fh->is_dir = false; for (i = 0; i < message.HeaderCount; i++) { if (strcasecmp((const char *)message.Headers[i].FieldName, "Content-Length") == 0) fh->size = strtoul((const char *) message.Headers[i].FieldValue, NULL, 10); else if (strcasecmp((const char *)message.Headers[i].FieldName, "Content-type") == 0) { if (strncmp((const char *)message.Headers[i].FieldValue, "text/html", 9) == 0) fh->is_dir = true; } } return (0); } static int efihttp_fs_open(const char *path, struct open_file *f) { char *path_slash; int err; if (!efihttp_init_done) return (ENXIO); /* * If any path fails to open, try with a trailing slash in * case it's a directory. */ err = _efihttp_fs_open(path, f); if (err != 0) { /* * Work around a bug in the EFI HTTP implementation which * causes a crash if the http instance isn't torn down * between requests. * See https://bugzilla.tianocore.org/show_bug.cgi?id=1917 */ efihttp_dev_close(f); efihttp_dev_open(f); path_slash = malloc(strlen(path) + 2); if (path_slash == NULL) return (ENOMEM); strcpy(path_slash, path); strcat(path_slash, "/"); err = _efihttp_fs_open(path_slash, f); free(path_slash); } return (err); } static int efihttp_fs_close(struct open_file *f __unused) { return (0); } static int _efihttp_fs_read(struct open_file *f, void *buf, size_t size, size_t *resid) { EFI_HTTP_TOKEN token; EFI_HTTP_MESSAGE message; EFI_STATUS status; struct devdesc *dev; struct open_efihttp *oh; struct file_efihttp *fh; bool done; int polltime; fh = (struct file_efihttp *)f->f_fsdata; if (fh->size > 0 && fh->offset >= fh->size) { if (resid != NULL) *resid = size; return 0; } dev = (struct devdesc *)f->f_devdata; oh = (struct open_efihttp *)dev->d_opendata; done = false; status = BS->CreateEvent(EVT_NOTIFY_SIGNAL, TPL_CALLBACK, notify, &done, &token.Event); if (EFI_ERROR(status)) { return (efi_status_to_errno(status)); } token.Status = EFI_NOT_READY; token.Message = &message; message.Data.Request = NULL; message.HeaderCount = 0; message.Headers = NULL; message.BodyLength = size; message.Body = buf; status = oh->http->Response(oh->http, &token); if (status == EFI_CONNECTION_FIN) { if (resid) *resid = size; return (0); } else if (EFI_ERROR(status)) { BS->CloseEvent(token.Event); return (efi_status_to_errno(status)); } polltime = 0; while (!done && polltime < EFIHTTP_POLL_TIMEOUT) { status = oh->http->Poll(oh->http); if (EFI_ERROR(status)) break; if (!done) { delay(100 * 1000); polltime += 100; } } BS->CloseEvent(token.Event); if (token.Status == EFI_CONNECTION_FIN) { if (resid) *resid = size; return (0); } else if (EFI_ERROR(token.Status)) return (efi_status_to_errno(token.Status)); if (resid) *resid = size - message.BodyLength; fh->offset += message.BodyLength; return (0); } static int efihttp_fs_read(struct open_file *f, void *buf, size_t size, size_t *resid) { size_t res; int err = 0; while (size > 0) { err = _efihttp_fs_read(f, buf, size, &res); if (err != 0 || res == size) goto end; buf += (size - res); size = res; } end: if (resid) *resid = size; return (err); } static int efihttp_fs_write(struct open_file *f __unused, const void *buf __unused, size_t size __unused, size_t *resid __unused) { return (EIO); } static off_t efihttp_fs_seek(struct open_file *f, off_t offset, int where) { struct file_efihttp *fh; char *path; void *buf; size_t res, res2; int err; fh = (struct file_efihttp *)f->f_fsdata; if (where == SEEK_SET && fh->offset == offset) return (0); if (where == SEEK_SET && fh->offset < offset) { buf = malloc(1500); if (buf == NULL) return (ENOMEM); res = offset - fh->offset; while (res > 0) { err = _efihttp_fs_read(f, buf, min(1500, res), &res2); if (err != 0) { free(buf); return (err); } res -= min(1500, res) - res2; } free(buf); return (0); } else if (where == SEEK_SET) { path = fh->path; fh->path = NULL; efihttp_fs_close(f); /* * Work around a bug in the EFI HTTP implementation which * causes a crash if the http instance isn't torn down * between requests. * See https://bugzilla.tianocore.org/show_bug.cgi?id=1917 */ efihttp_dev_close(f); efihttp_dev_open(f); err = efihttp_fs_open(path, f); free(path); if (err != 0) return (err); return efihttp_fs_seek(f, offset, where); } return (EIO); } static int efihttp_fs_stat(struct open_file *f, struct stat *sb) { struct file_efihttp *fh; fh = (struct file_efihttp *)f->f_fsdata; memset(sb, 0, sizeof(*sb)); sb->st_nlink = 1; sb->st_mode = 0777 | (fh->is_dir ? S_IFDIR : S_IFREG); sb->st_size = fh->size; return (0); } static int efihttp_fs_readdir(struct open_file *f, struct dirent *d) { static char *dirbuf = NULL, *db2, *cursor; static int dirbuf_len = 0; char *end; struct file_efihttp *fh; fh = (struct file_efihttp *)f->f_fsdata; if (dirbuf_len < fh->size) { db2 = realloc(dirbuf, fh->size); if (db2 == NULL) { free(dirbuf); return (ENOMEM); } else dirbuf = db2; dirbuf_len = fh->size; } if (fh->offset != fh->size) { efihttp_fs_seek(f, 0, SEEK_SET); efihttp_fs_read(f, dirbuf, dirbuf_len, NULL); cursor = dirbuf; } cursor = strstr(cursor, "d_type = DT_DIR; } else d->d_type = DT_REG; memcpy(d->d_name, cursor, end - cursor); d->d_name[end - cursor] = '\0'; return (0); } diff --git a/stand/efi/libefi/efinet.c b/stand/efi/libefi/efinet.c index ede8b7297f17..62fc203c83b5 100644 --- a/stand/efi/libefi/efinet.c +++ b/stand/efi/libefi/efinet.c @@ -1,465 +1,464 @@ /*- * Copyright (c) 2001 Doug Rabson * Copyright (c) 2002, 2006 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 #include #include #include #include #include #include #include #include #include #include "dev_net.h" static EFI_GUID sn_guid = EFI_SIMPLE_NETWORK_PROTOCOL; static void efinet_end(struct netif *); static ssize_t efinet_get(struct iodesc *, void **, time_t); static void efinet_init(struct iodesc *, void *); static int efinet_match(struct netif *, void *); static int efinet_probe(struct netif *, void *); static ssize_t efinet_put(struct iodesc *, void *, size_t); struct netif_driver efinetif = { .netif_bname = "efinet", .netif_match = efinet_match, .netif_probe = efinet_probe, .netif_init = efinet_init, .netif_get = efinet_get, .netif_put = efinet_put, .netif_end = efinet_end, .netif_ifs = NULL, .netif_nifs = 0 }; #ifdef EFINET_DEBUG static void dump_mode(EFI_SIMPLE_NETWORK_MODE *mode) { int i; printf("State = %x\n", mode->State); printf("HwAddressSize = %u\n", mode->HwAddressSize); printf("MediaHeaderSize = %u\n", mode->MediaHeaderSize); printf("MaxPacketSize = %u\n", mode->MaxPacketSize); printf("NvRamSize = %u\n", mode->NvRamSize); printf("NvRamAccessSize = %u\n", mode->NvRamAccessSize); printf("ReceiveFilterMask = %x\n", mode->ReceiveFilterMask); printf("ReceiveFilterSetting = %u\n", mode->ReceiveFilterSetting); printf("MaxMCastFilterCount = %u\n", mode->MaxMCastFilterCount); printf("MCastFilterCount = %u\n", mode->MCastFilterCount); printf("MCastFilter = {"); for (i = 0; i < mode->MCastFilterCount; i++) printf(" %s", ether_sprintf(mode->MCastFilter[i].Addr)); printf(" }\n"); printf("CurrentAddress = %s\n", ether_sprintf(mode->CurrentAddress.Addr)); printf("BroadcastAddress = %s\n", ether_sprintf(mode->BroadcastAddress.Addr)); printf("PermanentAddress = %s\n", ether_sprintf(mode->PermanentAddress.Addr)); printf("IfType = %u\n", mode->IfType); printf("MacAddressChangeable = %d\n", mode->MacAddressChangeable); printf("MultipleTxSupported = %d\n", mode->MultipleTxSupported); printf("MediaPresentSupported = %d\n", mode->MediaPresentSupported); printf("MediaPresent = %d\n", mode->MediaPresent); } #endif static int efinet_match(struct netif *nif, void *machdep_hint) { struct devdesc *dev = machdep_hint; if (dev->d_unit == nif->nif_unit) return (1); return(0); } static int efinet_probe(struct netif *nif, void *machdep_hint) { EFI_SIMPLE_NETWORK *net; EFI_HANDLE h; EFI_STATUS status; h = nif->nif_driver->netif_ifs[nif->nif_unit].dif_private; /* * Open the network device in exclusive mode. Without this * we will be racing with the UEFI network stack. It will * pull packets off the network leading to lost packets. */ status = BS->OpenProtocol(h, &sn_guid, (void **)&net, IH, NULL, EFI_OPEN_PROTOCOL_EXCLUSIVE); if (status != EFI_SUCCESS) { printf("Unable to open network interface %d for " "exclusive access: %lu\n", nif->nif_unit, EFI_ERROR_CODE(status)); return (efi_status_to_errno(status)); } return (0); } static ssize_t efinet_put(struct iodesc *desc, void *pkt, size_t len) { struct netif *nif = desc->io_netif; EFI_SIMPLE_NETWORK *net; EFI_STATUS status; void *buf; net = nif->nif_devdata; if (net == NULL) return (-1); status = net->Transmit(net, 0, len, pkt, NULL, NULL, NULL); if (status != EFI_SUCCESS) return (-1); /* Wait for the buffer to be transmitted */ do { buf = NULL; /* XXX Is this needed? */ status = net->GetStatus(net, NULL, &buf); /* * XXX EFI1.1 and the E1000 card returns a different * address than we gave. Sigh. */ } while (status == EFI_SUCCESS && buf == NULL); /* XXX How do we deal with status != EFI_SUCCESS now? */ return ((status == EFI_SUCCESS) ? len : -1); } static ssize_t efinet_get(struct iodesc *desc, void **pkt, time_t timeout) { struct netif *nif = desc->io_netif; EFI_SIMPLE_NETWORK *net; EFI_STATUS status; UINTN bufsz; time_t t; char *buf, *ptr; ssize_t ret = -1; net = nif->nif_devdata; if (net == NULL) return (ret); bufsz = net->Mode->MaxPacketSize + ETHER_HDR_LEN + ETHER_CRC_LEN; buf = malloc(bufsz + ETHER_ALIGN); if (buf == NULL) return (ret); ptr = buf + ETHER_ALIGN; t = getsecs(); while ((getsecs() - t) < timeout) { status = net->Receive(net, NULL, &bufsz, ptr, NULL, NULL, NULL); if (status == EFI_SUCCESS) { *pkt = buf; ret = (ssize_t)bufsz; break; } if (status != EFI_NOT_READY) break; } if (ret == -1) free(buf); return (ret); } /* * Loader uses BOOTP/DHCP and also uses RARP as a fallback to populate * network parameters and problems with DHCP servers can cause the loader * to fail to populate them. Allow the device to ask about the basic * network parameters and if present use them. */ static void efi_env_net_params(struct iodesc *desc) { char *envstr; in_addr_t ipaddr, mask, gwaddr, serveraddr; n_long rootaddr; if ((envstr = getenv("rootpath")) != NULL) strlcpy(rootpath, envstr, sizeof(rootpath)); /* * Get network parameters. */ envstr = getenv("ipaddr"); ipaddr = (envstr != NULL) ? inet_addr(envstr) : 0; envstr = getenv("netmask"); mask = (envstr != NULL) ? inet_addr(envstr) : 0; envstr = getenv("gatewayip"); gwaddr = (envstr != NULL) ? inet_addr(envstr) : 0; envstr = getenv("serverip"); serveraddr = (envstr != NULL) ? inet_addr(envstr) : 0; /* No network params. */ if (ipaddr == 0 && mask == 0 && gwaddr == 0 && serveraddr == 0) return; /* Partial network params. */ if (ipaddr == 0 || mask == 0 || gwaddr == 0 || serveraddr == 0) { printf("Incomplete network settings from U-Boot\n"); return; } /* * Set network parameters. */ myip.s_addr = ipaddr; netmask = mask; gateip.s_addr = gwaddr; servip.s_addr = serveraddr; /* * There must be a rootpath. It may be ip:/path or it may be just the * path in which case the ip needs to be serverip. */ rootaddr = net_parse_rootpath(); if (rootaddr == INADDR_NONE) rootaddr = serveraddr; rootip.s_addr = rootaddr; #ifdef EFINET_DEBUG printf("%s: ip=%s\n", __func__, inet_ntoa(myip)); printf("%s: mask=%s\n", __func__, intoa(netmask)); printf("%s: gateway=%s\n", __func__, inet_ntoa(gateip)); printf("%s: server=%s\n", __func__, inet_ntoa(servip)); #endif desc->myip = myip; } static void efinet_init(struct iodesc *desc, void *machdep_hint) { struct netif *nif = desc->io_netif; EFI_SIMPLE_NETWORK *net; EFI_HANDLE h; EFI_STATUS status; UINT32 mask; /* Attempt to get netboot params from env */ efi_env_net_params(desc); if (nif->nif_driver->netif_ifs[nif->nif_unit].dif_unit < 0) { printf("Invalid network interface %d\n", nif->nif_unit); return; } h = nif->nif_driver->netif_ifs[nif->nif_unit].dif_private; status = OpenProtocolByHandle(h, &sn_guid, (void **)&nif->nif_devdata); if (status != EFI_SUCCESS) { printf("net%d: cannot fetch interface data (status=%lu)\n", nif->nif_unit, EFI_ERROR_CODE(status)); return; } net = nif->nif_devdata; if (net->Mode->State == EfiSimpleNetworkStopped) { status = net->Start(net); if (status != EFI_SUCCESS) { printf("net%d: cannot start interface (status=%lu)\n", nif->nif_unit, EFI_ERROR_CODE(status)); return; } } if (net->Mode->State != EfiSimpleNetworkInitialized) { status = net->Initialize(net, 0, 0); if (status != EFI_SUCCESS) { printf("net%d: cannot init. interface (status=%lu)\n", nif->nif_unit, EFI_ERROR_CODE(status)); return; } } mask = EFI_SIMPLE_NETWORK_RECEIVE_UNICAST | EFI_SIMPLE_NETWORK_RECEIVE_BROADCAST; status = net->ReceiveFilters(net, mask, 0, FALSE, 0, NULL); if (status != EFI_SUCCESS) printf("net%d: cannot set rx. filters (status=%lu)\n", nif->nif_unit, EFI_ERROR_CODE(status)); #ifdef EFINET_DEBUG dump_mode(net->Mode); #endif bcopy(net->Mode->CurrentAddress.Addr, desc->myea, 6); desc->xid = 1; } static void efinet_end(struct netif *nif) { EFI_SIMPLE_NETWORK *net = nif->nif_devdata; if (net == NULL) return; net->Shutdown(net); } static int efinet_dev_init(void); static int efinet_dev_print(int); struct devsw efinet_dev = { .dv_name = "net", .dv_type = DEVT_NET, .dv_init = efinet_dev_init, .dv_strategy = NULL, /* Will be set in efinet_dev_init */ .dv_open = NULL, /* Will be set in efinet_dev_init */ .dv_close = NULL, /* Will be set in efinet_dev_init */ .dv_ioctl = noioctl, .dv_print = efinet_dev_print, .dv_cleanup = nullsys, }; static int efinet_dev_init() { struct netif_dif *dif; struct netif_stats *stats; EFI_DEVICE_PATH *devpath, *node; EFI_HANDLE *handles, *handles2; EFI_STATUS status; UINTN sz; int err, i, nifs; extern struct devsw netdev; sz = 0; handles = NULL; status = BS->LocateHandle(ByProtocol, &sn_guid, NULL, &sz, NULL); if (status == EFI_BUFFER_TOO_SMALL) { handles = (EFI_HANDLE *)malloc(sz); if (handles == NULL) return (ENOMEM); status = BS->LocateHandle(ByProtocol, &sn_guid, NULL, &sz, handles); if (EFI_ERROR(status)) free(handles); } if (EFI_ERROR(status)) return (efi_status_to_errno(status)); handles2 = (EFI_HANDLE *)malloc(sz); if (handles2 == NULL) { free(handles); return (ENOMEM); } nifs = 0; for (i = 0; i < sz / sizeof(EFI_HANDLE); i++) { devpath = efi_lookup_devpath(handles[i]); if (devpath == NULL) continue; if ((node = efi_devpath_last_node(devpath)) == NULL) continue; if (DevicePathType(node) != MESSAGING_DEVICE_PATH || DevicePathSubType(node) != MSG_MAC_ADDR_DP) continue; handles2[nifs] = handles[i]; nifs++; } free(handles); if (nifs == 0) { err = ENOENT; goto done; } err = efi_register_handles(&efinet_dev, handles2, NULL, nifs); if (err != 0) goto done; efinetif.netif_ifs = calloc(nifs, sizeof(struct netif_dif)); stats = calloc(nifs, sizeof(struct netif_stats)); if (efinetif.netif_ifs == NULL || stats == NULL) { free(efinetif.netif_ifs); free(stats); efinetif.netif_ifs = NULL; err = ENOMEM; goto done; } efinetif.netif_nifs = nifs; for (i = 0; i < nifs; i++) { dif = &efinetif.netif_ifs[i]; dif->dif_unit = i; dif->dif_nsel = 1; dif->dif_stats = &stats[i]; dif->dif_private = handles2[i]; } efinet_dev.dv_open = netdev.dv_open; efinet_dev.dv_close = netdev.dv_close; efinet_dev.dv_strategy = netdev.dv_strategy; done: free(handles2); return (err); } static int efinet_dev_print(int verbose) { CHAR16 *text; EFI_HANDLE h; int unit, ret = 0; printf("%s devices:", efinet_dev.dv_name); if ((ret = pager_output("\n")) != 0) return (ret); for (unit = 0, h = efi_find_handle(&efinet_dev, 0); h != NULL; h = efi_find_handle(&efinet_dev, ++unit)) { printf(" %s%d:", efinet_dev.dv_name, unit); if (verbose) { text = efi_devpath_name(efi_lookup_devpath(h)); if (text != NULL) { printf(" %S", text); efi_free_devpath_name(text); } } if ((ret = pager_output("\n")) != 0) break; } return (ret); } diff --git a/stand/efi/libefi/efizfs.c b/stand/efi/libefi/efizfs.c index 83551596bb32..1c80f1ae26b9 100644 --- a/stand/efi/libefi/efizfs.c +++ b/stand/efi/libefi/efizfs.c @@ -1,125 +1,124 @@ /*- * Copyright (c) 2008-2010 Rui Paulo * Copyright (c) 2006 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 ``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 #include #include #ifdef EFI_ZFS_BOOT #include #endif #include #include #include "efizfs.h" #ifdef EFI_ZFS_BOOT static zfsinfo_list_t zfsinfo; uint64_t pool_guid; zfsinfo_list_t * efizfs_get_zfsinfo_list(void) { return (&zfsinfo); } EFI_HANDLE efizfs_get_handle_by_guid(uint64_t guid) { zfsinfo_t *zi; STAILQ_FOREACH(zi, &zfsinfo, zi_link) { if (zi->zi_pool_guid == guid) { return (zi->zi_handle); } } return (NULL); } bool efizfs_get_guid_by_handle(EFI_HANDLE handle, uint64_t *guid) { zfsinfo_t *zi; if (guid == NULL) return (false); STAILQ_FOREACH(zi, &zfsinfo, zi_link) { if (zi->zi_handle == handle) { *guid = zi->zi_pool_guid; return (true); } } return (false); } static void insert_zfs(EFI_HANDLE handle, uint64_t guid) { zfsinfo_t *zi; zi = malloc(sizeof(zfsinfo_t)); if (zi != NULL) { zi->zi_handle = handle; zi->zi_pool_guid = guid; STAILQ_INSERT_TAIL(&zfsinfo, zi, zi_link); } } void efi_zfs_probe(void) { pdinfo_list_t *hdi; pdinfo_t *hd, *pd = NULL; char devname[SPECNAMELEN + 1]; uint64_t guid; hdi = efiblk_get_pdinfo_list(&efipart_hddev); STAILQ_INIT(&zfsinfo); /* * Find the handle for the boot device. The boot1 did find the * device with loader binary, now we need to search for the * same device and if it is part of the zfs pool, we record the * pool GUID for currdev setup. */ STAILQ_FOREACH(hd, hdi, pd_link) { STAILQ_FOREACH(pd, &hd->pd_part, pd_link) { snprintf(devname, sizeof(devname), "%s%dp%d:", efipart_hddev.dv_name, hd->pd_unit, pd->pd_unit); guid = 0; if (zfs_probe_dev(devname, &guid, false) == 0) { insert_zfs(pd->pd_handle, guid); if (pd->pd_handle == boot_img->DeviceHandle) pool_guid = guid; } } } } #endif diff --git a/stand/efi/loader/arch/arm/exec.c b/stand/efi/loader/arch/arm/exec.c index 99ee498b1b65..85a8c26ade30 100644 --- a/stand/efi/loader/arch/arm/exec.c +++ b/stand/efi/loader/arch/arm/exec.c @@ -1,103 +1,102 @@ /*- * Copyright (c) 2001 Benno Rice * Copyright (c) 2007 Semihalf, Rafal Jaworowski * 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 #include #include #include #include #include #include #include #include "bootstrap.h" #include "loader_efi.h" extern int bi_load(char *, vm_offset_t *, vm_offset_t *, bool); static int __elfN(arm_load)(char *filename, uint64_t dest, struct preloaded_file **result) { int r; r = __elfN(loadfile)(filename, dest, result); if (r != 0) return (r); return (0); } static int __elfN(arm_exec)(struct preloaded_file *fp) { struct file_metadata *fmp; vm_offset_t modulep, kernend; Elf_Ehdr *e; int error; void (*entry)(void *); if ((fmp = file_findmetadata(fp, MODINFOMD_ELFHDR)) == NULL) return (EFTYPE); e = (Elf_Ehdr *)&fmp->md_data; efi_time_fini(); entry = efi_translate(e->e_entry); printf("Kernel entry at %p...\n", entry); printf("Kernel args: %s\n", fp->f_args); if ((error = bi_load(fp->f_args, &modulep, &kernend, true)) != 0) { efi_time_init(); return (error); } /* At this point we've called ExitBootServices, so we can't call * printf or any other function that uses Boot Services */ dev_cleanup(); (*entry)((void *)modulep); panic("exec returned"); } static struct file_format arm_elf = { __elfN(arm_load), __elfN(arm_exec) }; struct file_format *file_formats[] = { &arm_elf, NULL }; diff --git a/stand/efi/loader/arch/riscv/exec.c b/stand/efi/loader/arch/riscv/exec.c index 686a42028608..3c40517ea968 100644 --- a/stand/efi/loader/arch/riscv/exec.c +++ b/stand/efi/loader/arch/riscv/exec.c @@ -1,90 +1,89 @@ /*- * Copyright (c) 2001 Benno Rice * Copyright (c) 2007 Semihalf, Rafal Jaworowski * 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 #include #include #include #include #include #include #include #include "bootstrap.h" #include "loader_efi.h" extern int bi_load(char *, vm_offset_t *, vm_offset_t *, bool); static int __elfN(exec)(struct preloaded_file *fp) { struct file_metadata *fmp; vm_offset_t modulep, kernend; Elf_Ehdr *e; int error; void (*entry)(void *); if ((fmp = file_findmetadata(fp, MODINFOMD_ELFHDR)) == NULL) return (EFTYPE); e = (Elf_Ehdr *)&fmp->md_data; efi_time_fini(); entry = efi_translate(e->e_entry); printf("Kernel entry at %p...\n", entry); printf("Kernel args: %s\n", fp->f_args); if ((error = bi_load(fp->f_args, &modulep, &kernend, true)) != 0) { efi_time_init(); return (error); } /* * At this point we've called ExitBootServices, so we can't call * printf or any other function that uses Boot Services */ dev_cleanup(); (*entry)((void *)modulep); panic("exec returned"); } static struct file_format riscv_elf = { __elfN(loadfile), __elfN(exec) }; struct file_format *file_formats[] = { &riscv_elf, NULL }; diff --git a/stand/efi/loader/copy.c b/stand/efi/loader/copy.c index 12f7273edacd..3f2d1c6c20b0 100644 --- a/stand/efi/loader/copy.c +++ b/stand/efi/loader/copy.c @@ -1,531 +1,530 @@ /*- * Copyright (c) 2013 The FreeBSD Foundation * * This software was developed by Benno Rice under sponsorship from * the FreeBSD Foundation. * 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 #include #include #include #include #include "loader_efi.h" #define M(x) ((x) * 1024 * 1024) #define G(x) (1UL * (x) * 1024 * 1024 * 1024) #if defined(__amd64__) #include #include #include /* * The code is excerpted from sys/x86/x86/identcpu.c: identify_cpu(), * identify_hypervisor(), and dev/hyperv/vmbus/hyperv.c: hyperv_identify(). */ #define CPUID_LEAF_HV_MAXLEAF 0x40000000 #define CPUID_LEAF_HV_INTERFACE 0x40000001 #define CPUID_LEAF_HV_FEATURES 0x40000003 #define CPUID_LEAF_HV_LIMITS 0x40000005 #define CPUID_HV_IFACE_HYPERV 0x31237648 /* HV#1 */ #define CPUID_HV_MSR_TIME_REFCNT 0x0002 /* MSR_HV_TIME_REF_COUNT */ #define CPUID_HV_MSR_HYPERCALL 0x0020 static int running_on_hyperv(void) { char hv_vendor[16]; uint32_t regs[4]; do_cpuid(1, regs); if ((regs[2] & CPUID2_HV) == 0) return (0); do_cpuid(CPUID_LEAF_HV_MAXLEAF, regs); if (regs[0] < CPUID_LEAF_HV_LIMITS) return (0); ((uint32_t *)&hv_vendor)[0] = regs[1]; ((uint32_t *)&hv_vendor)[1] = regs[2]; ((uint32_t *)&hv_vendor)[2] = regs[3]; hv_vendor[12] = '\0'; if (strcmp(hv_vendor, "Microsoft Hv") != 0) return (0); do_cpuid(CPUID_LEAF_HV_INTERFACE, regs); if (regs[0] != CPUID_HV_IFACE_HYPERV) return (0); do_cpuid(CPUID_LEAF_HV_FEATURES, regs); if ((regs[0] & CPUID_HV_MSR_HYPERCALL) == 0) return (0); if ((regs[0] & CPUID_HV_MSR_TIME_REFCNT) == 0) return (0); return (1); } static void efi_verify_staging_size(unsigned long *nr_pages) { UINTN sz; EFI_MEMORY_DESCRIPTOR *map = NULL, *p; EFI_PHYSICAL_ADDRESS start, end; UINTN key, dsz; UINT32 dver; EFI_STATUS status; int i, ndesc; unsigned long available_pages = 0; sz = 0; for (;;) { status = BS->GetMemoryMap(&sz, map, &key, &dsz, &dver); if (!EFI_ERROR(status)) break; if (status != EFI_BUFFER_TOO_SMALL) { printf("Can't read memory map: %lu\n", EFI_ERROR_CODE(status)); goto out; } free(map); /* Allocate 10 descriptors more than the size reported, * to allow for any fragmentation caused by calling * malloc */ map = malloc(sz + (10 * dsz)); if (map == NULL) { printf("Unable to allocate memory\n"); goto out; } } ndesc = sz / dsz; for (i = 0, p = map; i < ndesc; i++, p = NextMemoryDescriptor(p, dsz)) { start = p->PhysicalStart; end = start + p->NumberOfPages * EFI_PAGE_SIZE; if (KERNLOAD < start || KERNLOAD >= end) continue; available_pages = p->NumberOfPages - ((KERNLOAD - start) >> EFI_PAGE_SHIFT); break; } if (available_pages == 0) { printf("Can't find valid memory map for staging area!\n"); goto out; } i++; p = NextMemoryDescriptor(p, dsz); for ( ; i < ndesc; i++, p = NextMemoryDescriptor(p, dsz)) { if (p->Type != EfiConventionalMemory && p->Type != EfiLoaderData) break; if (p->PhysicalStart != end) break; end = p->PhysicalStart + p->NumberOfPages * EFI_PAGE_SIZE; available_pages += p->NumberOfPages; } if (*nr_pages > available_pages) { printf("Staging area's size is reduced: %ld -> %ld!\n", *nr_pages, available_pages); *nr_pages = available_pages; } out: free(map); } #endif /* __amd64__ */ #if defined(__arm__) #define DEFAULT_EFI_STAGING_SIZE 32 #else #define DEFAULT_EFI_STAGING_SIZE 64 #endif #ifndef EFI_STAGING_SIZE #define EFI_STAGING_SIZE DEFAULT_EFI_STAGING_SIZE #endif #if defined(__aarch64__) || defined(__amd64__) || defined(__arm__) || \ defined(__riscv) #define EFI_STAGING_2M_ALIGN 1 #else #define EFI_STAGING_2M_ALIGN 0 #endif #if defined(__amd64__) #define EFI_STAGING_SLOP M(8) #else #define EFI_STAGING_SLOP 0 #endif static u_long staging_slop = EFI_STAGING_SLOP; EFI_PHYSICAL_ADDRESS staging, staging_end, staging_base; bool stage_offset_set = false; ssize_t stage_offset; static void efi_copy_free(void) { BS->FreePages(staging_base, (staging_end - staging_base) / EFI_PAGE_SIZE); stage_offset_set = false; stage_offset = 0; } #ifdef __amd64__ int copy_staging = COPY_STAGING_AUTO; static int command_copy_staging(int argc, char *argv[]) { static const char *const mode[3] = { [COPY_STAGING_ENABLE] = "enable", [COPY_STAGING_DISABLE] = "disable", [COPY_STAGING_AUTO] = "auto", }; int prev, res; res = CMD_OK; if (argc > 2) { res = CMD_ERROR; } else if (argc == 2) { prev = copy_staging; if (strcmp(argv[1], "enable") == 0) copy_staging = COPY_STAGING_ENABLE; else if (strcmp(argv[1], "disable") == 0) copy_staging = COPY_STAGING_DISABLE; else if (strcmp(argv[1], "auto") == 0) copy_staging = COPY_STAGING_AUTO; else { printf("usage: copy_staging enable|disable|auto\n"); res = CMD_ERROR; } if (res == CMD_OK && prev != copy_staging) { printf("changed copy_staging, unloading kernel\n"); unload(); efi_copy_free(); efi_copy_init(); } } else { printf("copy staging: %s\n", mode[copy_staging]); } return (res); } COMMAND_SET(copy_staging, "copy_staging", "copy staging", command_copy_staging); #endif static int command_staging_slop(int argc, char *argv[]) { char *endp; u_long new, prev; int res; res = CMD_OK; if (argc > 2) { res = CMD_ERROR; } else if (argc == 2) { new = strtoul(argv[1], &endp, 0); if (*endp != '\0') { printf("invalid slop value\n"); res = CMD_ERROR; } if (res == CMD_OK && staging_slop != new) { printf("changed slop, unloading kernel\n"); unload(); efi_copy_free(); efi_copy_init(); } } else { printf("staging slop %#lx\n", staging_slop); } return (res); } COMMAND_SET(staging_slop, "staging_slop", "set staging slop", command_staging_slop); #if defined(__amd64__) /* * The staging area must reside in the first 1GB or 4GB physical * memory: see elf64_exec() in * boot/efi/loader/arch/amd64/elf64_freebsd.c. */ static EFI_PHYSICAL_ADDRESS get_staging_max(void) { EFI_PHYSICAL_ADDRESS res; res = copy_staging == COPY_STAGING_ENABLE ? G(1) : G(4); return (res); } #define EFI_ALLOC_METHOD AllocateMaxAddress #else #define EFI_ALLOC_METHOD AllocateAnyPages #endif int efi_copy_init(void) { EFI_STATUS status; unsigned long nr_pages; vm_offset_t ess; ess = EFI_STAGING_SIZE; if (ess < DEFAULT_EFI_STAGING_SIZE) ess = DEFAULT_EFI_STAGING_SIZE; nr_pages = EFI_SIZE_TO_PAGES(M(1) * ess); #if defined(__amd64__) /* * We'll decrease nr_pages, if it's too big. Currently we only * apply this to FreeBSD VM running on Hyper-V. Why? Please see * https://bugs.freebsd.org/bugzilla/show_bug.cgi?id=211746#c28 */ if (running_on_hyperv()) efi_verify_staging_size(&nr_pages); staging = get_staging_max(); #endif status = BS->AllocatePages(EFI_ALLOC_METHOD, EfiLoaderCode, nr_pages, &staging); if (EFI_ERROR(status)) { printf("failed to allocate staging area: %lu\n", EFI_ERROR_CODE(status)); return (status); } staging_base = staging; staging_end = staging + nr_pages * EFI_PAGE_SIZE; #if EFI_STAGING_2M_ALIGN /* * Round the kernel load address to a 2MiB value. This is needed * because the kernel builds a page table based on where it has * been loaded in physical address space. As the kernel will use * either a 1MiB or 2MiB page for this we need to make sure it * is correctly aligned for both cases. */ staging = roundup2(staging, M(2)); #endif return (0); } static bool efi_check_space(vm_offset_t end) { EFI_PHYSICAL_ADDRESS addr, new_base, new_staging; EFI_STATUS status; unsigned long nr_pages; end = roundup2(end, EFI_PAGE_SIZE); /* There is already enough space */ if (end + staging_slop <= staging_end) return (true); if (!boot_services_active) { if (end <= staging_end) return (true); panic("efi_check_space: cannot expand staging area " "after boot services were exited\n"); } /* * Add slop at the end: * 1. amd64 kernel expects to do some very early allocations * by carving out memory after kernend. Slop guarantees * that it does not ovewrite anything useful. * 2. It seems that initial calculation of the staging size * could be somewhat smaller than actually copying in after * boot services are exited. Slop avoids calling * BS->AllocatePages() when it cannot work. */ end += staging_slop; nr_pages = EFI_SIZE_TO_PAGES(end - staging_end); #if defined(__amd64__) /* * amd64 needs all memory to be allocated under the 1G or 4G boundary. */ if (end > get_staging_max()) goto before_staging; #endif /* Try to allocate more space after the previous allocation */ addr = staging_end; status = BS->AllocatePages(AllocateAddress, EfiLoaderCode, nr_pages, &addr); if (!EFI_ERROR(status)) { staging_end = staging_end + nr_pages * EFI_PAGE_SIZE; return (true); } before_staging: /* Try allocating space before the previous allocation */ if (staging < nr_pages * EFI_PAGE_SIZE) goto expand; addr = staging - nr_pages * EFI_PAGE_SIZE; #if EFI_STAGING_2M_ALIGN /* See efi_copy_init for why this is needed */ addr = rounddown2(addr, M(2)); #endif nr_pages = EFI_SIZE_TO_PAGES(staging_base - addr); status = BS->AllocatePages(AllocateAddress, EfiLoaderCode, nr_pages, &addr); if (!EFI_ERROR(status)) { /* * Move the old allocation and update the state so * translation still works. */ staging_base = addr; memmove((void *)(uintptr_t)staging_base, (void *)(uintptr_t)staging, staging_end - staging); stage_offset -= staging - staging_base; staging = staging_base; return (true); } expand: nr_pages = EFI_SIZE_TO_PAGES(end - (vm_offset_t)staging); #if EFI_STAGING_2M_ALIGN nr_pages += M(2) / EFI_PAGE_SIZE; #endif #if defined(__amd64__) new_base = get_staging_max(); #endif status = BS->AllocatePages(EFI_ALLOC_METHOD, EfiLoaderCode, nr_pages, &new_base); if (!EFI_ERROR(status)) { #if EFI_STAGING_2M_ALIGN new_staging = roundup2(new_base, M(2)); #else new_staging = new_base; #endif /* * Move the old allocation and update the state so * translation still works. */ memcpy((void *)(uintptr_t)new_staging, (void *)(uintptr_t)staging, staging_end - staging); BS->FreePages(staging_base, (staging_end - staging_base) / EFI_PAGE_SIZE); stage_offset -= staging - new_staging; staging = new_staging; staging_end = new_base + nr_pages * EFI_PAGE_SIZE; staging_base = new_base; return (true); } printf("efi_check_space: Unable to expand staging area\n"); return (false); } void * efi_translate(vm_offset_t ptr) { return ((void *)(ptr + stage_offset)); } ssize_t efi_copyin(const void *src, vm_offset_t dest, const size_t len) { if (!stage_offset_set) { stage_offset = (vm_offset_t)staging - dest; stage_offset_set = true; } /* XXX: Callers do not check for failure. */ if (!efi_check_space(dest + stage_offset + len)) { errno = ENOMEM; return (-1); } bcopy(src, (void *)(dest + stage_offset), len); return (len); } ssize_t efi_copyout(const vm_offset_t src, void *dest, const size_t len) { /* XXX: Callers do not check for failure. */ if (src + stage_offset + len > staging_end) { errno = ENOMEM; return (-1); } bcopy((void *)(src + stage_offset), dest, len); return (len); } ssize_t efi_readin(readin_handle_t fd, vm_offset_t dest, const size_t len) { if (!stage_offset_set) { stage_offset = (vm_offset_t)staging - dest; stage_offset_set = true; } if (!efi_check_space(dest + stage_offset + len)) { errno = ENOMEM; return (-1); } return (VECTX_READ(fd, (void *)(dest + stage_offset), len)); } void efi_copy_finish(void) { uint64_t *src, *dst, *last; src = (uint64_t *)(uintptr_t)staging; dst = (uint64_t *)(uintptr_t)(staging - stage_offset); last = (uint64_t *)(uintptr_t)staging_end; while (src < last) *dst++ = *src++; } void efi_copy_finish_nop(void) { } diff --git a/stand/i386/boot0/boot0.S b/stand/i386/boot0/boot0.S index d92f8dcb05f1..123e66fca970 100644 --- a/stand/i386/boot0/boot0.S +++ b/stand/i386/boot0/boot0.S @@ -1,680 +1,673 @@ /* * Copyright (c) 2008 Luigi Rizzo (mostly documentation) * Copyright (c) 2002 Bruce M. Simpson * 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. */ /* build options: */ -#ifdef SIO /* use serial console on COM1. */ -#endif #ifdef PXE /* enable PXE/INT18 booting with F6 */ #define SAVE_MORE_MEMORY #endif -#ifdef CHECK_DRIVE /* make sure we boot from a HD. */ -#endif - -#ifdef ONLY_F_KEYS /* Only F1..F6, no digits on console */ -#endif #ifdef VOLUME_SERIAL /* support Volume serial number */ #define B0_BASE 0x1ae /* move the internal data area */ #define SAVE_MEMORY #else #define B0_BASE 0x1b2 #endif #ifdef TEST /* enable some test code */ #define SAVE_MEMORY #define SAVE_MORE_MEMORY #endif /* * Note - this code uses many tricks to save space and fit in one sector. * This includes using side effects of certain instructions, reusing * register values from previous operations, etc. * Be extremely careful when changing the code, even for simple things. */ /* * BOOT BLOCK STRUCTURE * * This code implements a Master Boot Record (MBR) for an Intel/PC disk. * It is 512 bytes long and it is normally loaded by the BIOS (or another * bootloader) at 0:0x7c00. This code depends on %cs:%ip being 0:0x7c00 * * The initial chunk of instructions is used as a signature by external * tools (e.g. boot0cfg) which can manipulate the block itself. * * The area at offset 0x1b2 contains a magic string ('Drive '), also * used as a signature to detect the block, and some variables that can * be updated by boot0cfg (and optionally written back to the disk). * These variables control the operation of the bootloader itself, * e.g. which partitions to enable, the timeout, the use of LBA * (called 'packet') or CHS mode, whether to force a drive number, * and whether to write back the user's selection back to disk. * * As in every Master Boot Record, the partition table is at 0x1be, * made of four 16-byte entries each containing: * * OFF SIZE DESCRIPTION * 0 1 status (0x80: bootable, 0: non bootable) * 1 3 start sector CHS * 8:head, 6:sector, 2:cyl bit 9..8, 8:cyl bit 7..0 * 4 1 partition type * 5 3 end sector CHS * 8 4 LBA of first sector * 12 4 partition size in sectors * * and followed by the two bytes 0x55, 0xAA (MBR signature). */ /* * BOOT BLOCK OPERATION * * On entry, the registers contain the following values: * * %cs:%ip 0:0x7c00 * %dl drive number (0x80, 0x81, ... ) * %si pointer to the partition table from which we were loaded. * Some boot code (e.g. syslinux) use this info to relocate * themselves, so we want to pass a valid one to the next stage. * NOTE: the use of %si is not a standard. * * This boot block first relocates itself at a different address (0:0x600), * to free the space at 0:0x7c00 for the next stage boot block. * * It then initializes some memory at 0:0x800 and above (pointed by %bp) * to store the original drive number (%dl) passed to us, and to construct a * fake partition entry. The latter is used by the disk I/O routine and, * in some cases, passed in %si to the next stage boot code. * * The variables at 0x1b2 are accessed as negative offsets from %bp. * * After the relocation, the code scans the partition table printing * out enabled partition or disks, and waits for user input. * * When a partition is selected, or a timeout expires, the currently * selected partition is used to load the next stage boot code, * %dl and %si are set appropriately as when we were called, and * control is transferred to the newly loaded code at 0:0x7c00. */ /* * CONSTANTS * * NHRDRV is the address in segment 0 where the BIOS writes the * total number of hard disks in the system. * LOAD is the original load address and cannot be changed. * ORIGIN is the relocation address. If you change it, you also need * to change the value passed to the linker in the Makefile * PRT_OFF is the location of the partition table (from the MBR standard). * B0_OFF is the location of the data area, known to boot0cfg so * it cannot be changed. Computed as a negative offset from 0x200 * MAGIC is the signature of a boot block. */ .set NHRDRV,0x475 # Number of hard drives .set ORIGIN,0x600 # Execution address .set LOAD,0x7c00 # Load address .set PRT_OFF,0x1be # Partition table .set B0_OFF,(B0_BASE-0x200) # Offset of boot0 data .set MAGIC,0xaa55 # Magic: bootable .set KEY_ENTER,0x1c # Enter key scan code .set KEY_F1,0x3b # F1 key scan code .set KEY_1,0x02 # #1 key scan code .set ASCII_BEL,'#' # ASCII code for .set ASCII_CR,0x0D # ASCII code for /* * Offsets of variables in the block at B0_OFF, and in the volatile * data area, computed as displacement from %bp. * We need to define them as constant as the assembler cannot * compute them in its single pass. */ .set _NXTDRV, B0_OFF+6 # Next drive .set _OPT, B0_OFF+7 # Default option .set _SETDRV, B0_OFF+8 # Drive to force .set _FLAGS, B0_OFF+9 # Flags .set SETDRV, 0x20 # the 'setdrv' flag .set NOUPDATE, 0x40 # the 'noupdate' flag .set USEPACKET, 0x80 # the 'packet' flag /* ticks is at a fixed position */ .set _TICKS, (PRT_OFF - 0x200 - 2) # Timeout ticks .set _MNUOPT, 0x10 # Saved menu entries .set TLEN, (desc_ofs - bootable_ids) # size of bootable ids .globl start # Entry point .code16 # This runs in real mode /* * MAIN ENTRY POINT * Initialise segments and registers to known values. * segments start at 0. * The stack is immediately below the address we were loaded to. * NOTE: the initial section of the code (up to movw $LOAD,%sp) * is used by boot0cfg, together with the 'Drive ' string and * the 0x55, 0xaa at the end, as an identifier for version 1.0 * of the boot code. Do not change it. * In version 1.0 the parameter table (_NEXTDRV etc) is at 0x1b9 */ start: cld # String ops inc xorw %ax,%ax # Zero movw %ax,%es # Address movw %ax,%ds # data movw %ax,%ss # Set up movw $LOAD,%sp # stack /* * Copy this code to the address it was linked for, 0x600 by default. */ movw %sp,%si # Source movw $start,%di # Destination movw $0x100,%cx # Word count rep # Relocate movsw # code /* * After the code, (i.e. at %di+0, 0x800) create a partition entry, * initialized to LBA 0 / CHS 0:0:1. * Set %bp to point to the partition and also, with negative offsets, * to the variables embedded in the bootblock (nextdrv and so on). */ movw %di,%bp # Address variables movb $0x8,%cl # Words to clear rep # Zero stosw # them incb -0xe(%di) # Set the S field to 1 jmp main-LOAD+ORIGIN # Jump to relocated code main: #if defined(SIO) && COMSPEED != 0 /* * Init the serial port. bioscom preserves the driver number in DX. */ movw $COMSPEED,%ax # defined by Makefile callw bioscom #endif /* * If the 'setdrv' flag is set in the boot sector, use the drive * number from the boot sector at 'setdrv_num'. * Optionally, do the same if the BIOS gives us an invalid number * (note though that the override prevents booting from a floppy * or a ZIP/flash drive in floppy emulation). * The test costs 4 bytes of code so it is disabled by default. */ testb $SETDRV,_FLAGS(%bp) # Set drive number? #ifndef CHECK_DRIVE /* disable drive checks */ jz save_curdrive # no, use the default #else jnz disable_update # Yes testb %dl,%dl # Drive number valid? js save_curdrive # Possibly (0x80 set) #endif /* * Disable updates if the drive number is forced. */ disable_update: orb $NOUPDATE,_FLAGS(%bp) # Disable updates movb _SETDRV(%bp),%dl # Use stored drive number /* * Whatever drive we decided to use, store it at (%bp). The byte * is normally used for the state of the partition (0x80 or 0x00), * but we abuse it as it is very convenient to access at offset 0. * The value is read back after 'check_selection' */ save_curdrive: movb %dl, (%bp) # Save drive number pushw %dx # Also in the stack #ifdef TEST /* test code, print internal bios drive */ rolb $1, %dl movw $drive, %si call putkey #endif callw putn # Print a newline /* * Start out with a pointer to the 4th byte of the first table entry * so that after 4 iterations it's beyond the end of the sector * and beyond a 256 byte boundary. We use the latter trick to check for * end of the loop without using an extra register (see start.5). */ movw $(partbl+0x4),%bx # Partition table (+4) xorw %dx,%dx # Item number /* * Loop around on the partition table, printing values until we * pass a 256 byte boundary. */ read_entry: movb %ch,-0x4(%bx) # Zero active flag (ch == 0) btw %dx,_FLAGS(%bp) # Entry enabled? jnc next_entry # No movb (%bx),%al # Load type test %al, %al # skip empty partition jz next_entry /* * Scan the table of bootable ids, which starts at %di and has * length TLEN. On a match, %di points to the element following the * match; the corresponding offset to the description is $(TLEN-1) * bytes ahead. We use a count of TLEN+1 so if we don't find a match * within the first TLEN entries, we hit the 'unknown' entry. */ movw $bootable_ids,%di # Lookup tables movb $(TLEN+1),%cl # Number of entries repne # Locate scasb # type /* * Get the matching element in the next array. * The byte at $(TLEN-1)(%di) contains the offset of the description * string from %di, so we add the number and print the string. */ addw $(TLEN-1), %di # Adjust movb (%di),%cl # Partition addw %cx,%di # description callw putx # Display it next_entry: incw %dx # Next item addb $0x10,%bl # Next entry jnc read_entry # Till done /* * We are past a 256 byte boundary: the partition table is finished. * Add one to the drive number and check it is valid. * Note that if we started from a floppy, %dl was 0 so we still * get an entry for the next drive, which is the first Hard Disk. */ popw %ax # Drive number subb $0x80-0x1,%al # Does next cmpb NHRDRV,%al # drive exist? (from BIOS?) jb print_drive # Yes /* * If this is the only drive, don't display it as an option. */ decw %ax # Already drive 0? jz print_prompt # Yes /* * If it was illegal or we cycled through them, go back to drive 0. */ xorb %al,%al # Drive 0 /* * Whatever drive we selected, make it an ascii digit and save it * back to the "nxtdrv" location in case we want to save it to disk. * This digit is also part of the printed drive string, so add 0x80 * to indicate end of string. */ print_drive: addb $'0'|0x80,%al # Save next movb %al,_NXTDRV(%bp) # drive number movw $drive,%di # Display callw putx # item /* * Menu is complete, display a prompt followed by current selection. * 'decw %si' makes the register point to the space after 'Boot: ' * so we do not see an extra CRLF on the screen. */ print_prompt: movw $prompt,%si # Display callw putstr # prompt movb _OPT(%bp),%dl # Display decw %si # default callw putkey # key jmp start_input # Skip beep /* * Here we have the code waiting for user input or a timeout. */ beep: movb $ASCII_BEL,%al # Input error, print or beep callw putchr start_input: /* * Actual Start of input loop. Take note of time */ xorb %ah,%ah # BIOS: Get int $0x1a # system time movw %dx,%di # Ticks when addw _TICKS(%bp),%di # timeout read_key: /* * Busy loop, looking for keystrokes but keeping one eye on the time. */ #ifndef SIO movb $0x1,%ah # BIOS: Check int $0x16 # for keypress #else /* SIO */ movb $0x03,%ah # BIOS: Read COM call bioscom testb $0x01,%ah # Check line status # (bit 1 indicates input) #endif /* SIO */ jnz got_key # Have input xorb %ah,%ah # BIOS: int 0x1a, 00 int $0x1a # get system time cmpw %di,%dx # Timeout? jb read_key # No /* * Timed out or default selection */ use_default: movb _OPT(%bp),%al # Load default orb $NOUPDATE,_FLAGS(%bp) # Disable updates jmp check_selection # Join common code /* * Get the keystroke. * ENTER or CR confirm the current selection (same as a timeout). * Otherwise convert F1..F6 (or '1'..'6') to 0..5 and check if the * selection is valid. * The SIO code uses ascii chars, the console code uses scancodes. */ got_key: #ifndef SIO xorb %ah,%ah # BIOS: int 0x16, 00 int $0x16 # get keypress movb %ah,%al # move scan code to %al cmpb $KEY_ENTER,%al #else movb $0x02,%ah # BIOS: Receive call bioscom cmpb $ASCII_CR,%al #endif je use_default # enter -> default /* * Check if the key is acceptable, and loop back if not. * The console (non-SIO) code looks at scancodes and accepts * both F1..F6 and 1..6 (the latter costs 6 bytes of code), * relying on the fact that F1..F6 have higher scancodes than 1..6 * The SIO code only takes 1..6 */ #ifdef SIO /* SIO mode, use ascii values */ subb $'1',%al # Subtract '1' ascii code #else /* console mode -- use scancodes */ subb $KEY_F1,%al /* Subtract F1 scan code */ #if !defined(ONLY_F_KEYS) cmpb $0x5,%al # F1..F6 jna 3f # Yes subb $(KEY_1 - KEY_F1),%al # Less #1 scan code 3: #endif /* ONLY_F_KEYS */ #endif /* SIO */ check_selection: cmpb $0x5,%al # F1..F6 or 1..6 ? #ifdef PXE /* enable PXE/INT18 using F6 */ jne 1f; int $0x18 # found F6, try INT18 1: #endif /* PXE */ jae beep # Not in F1..F5, beep /* * We have a selection. If it's a bad selection go back to complain. * The bits in MNUOPT were set when the options were printed. * Anything not printed is not an option. */ cbtw # Extend (%ah=0 used later) btw %ax,_MNUOPT(%bp) # Option enabled? jnc beep # No /* * Save the info in the original tables * for rewriting to the disk. */ movb %al,_OPT(%bp) # Save option /* * Make %si and %bx point to the fake partition at LBA 0 (CHS 0:0:1). * Because the correct address is already in %bp, just use it. * Set %dl with the drive number saved in byte 0. * If we have pressed F5 or 5, then this is a good, fake value * to present to the next stage boot code. */ movw %bp,%si # Partition for write movb (%si),%dl # Drive number, saved above movw %si,%bx # Partition for read cmpb $0x4,%al # F5/#5 pressed? pushf # Save results for later je 1f # Yes, F5 /* * F1..F4 was pressed, so make %bx point to the currently * selected partition, and leave the drive number unchanged. */ shlb $0x4,%al # Point to addw $partbl,%ax # selected xchgw %bx,%ax # partition movb $0x80,(%bx) # Flag active /* * If not asked to do a write-back (flags 0x40) don't do one. * Around the call, save the partition pointer to %bx and * restore to %si which is where the next stage expects it. */ 1: pushw %bx # Save testb $NOUPDATE,_FLAGS(%bp) # No updates? jnz 2f # skip update movw $start,%bx # Data to write movb $0x3,%ah # Write sector callw intx13 # to disk 2: popw %si # Restore /* * If going to next drive, replace drive with selected one. * Remember to un-ascii it. Hey 0x80 is already set, cool! */ popf # Restore %al test results jne 3f # If not F5/#5 movb _NXTDRV(%bp),%dl # Next drive subb $'0',%dl # number /* * Load selected bootsector to the LOAD location in RAM. If read * fails or there is no 0x55aa marker, treat it as a bad selection. */ 3: movw $LOAD,%bx # Address for read movb $0x2,%ah # Read sector callw intx13 # from disk jc beep # If error cmpw $MAGIC,0x1fe(%bx) # Bootable? jne beep # No pushw %si # Save ptr to selected part. callw putn # Leave some space popw %si # Restore, next stage uses it jmp *%bx # Invoke bootstrap /* * Display routines * putkey prints the option selected in %dl (F1..F5 or 1..5) followed by * the string at %si * putx: print the option in %dl followed by the string at %di * also record the drive as valid. * putn: print a crlf * putstr: print the string at %si * putchr: print the char in al */ /* * Display the option and record the drive as valid in the options. * That last point is done using the btsw instruction which does * a test and set. We don't care for the test part. */ putx: btsw %dx,_MNUOPT(%bp) # Enable menu option movw $item,%si # Display callw putkey # key movw %di,%si # Display the rest callw putstr # Display string putn: movw $crlf,%si # To next line jmp putstr putkey: #ifndef SIO movb $'F',%al # Display callw putchr # 'F' #endif movb $'1',%al # Prepare addb %dl,%al # digit putstr.1: callw putchr # Display char putstr: lodsb # Get byte testb $0x80,%al # End of string? jz putstr.1 # No andb $~0x80,%al # Clear MSB then print last putchr: #ifndef SIO pushw %bx # Save movw $0x7,%bx # Page:attribute movb $0xe,%ah # BIOS: Display int $0x10 # character popw %bx # Restore #else /* SIO */ movb $0x01,%ah # BIOS: Send character bioscom: pushw %dx # Save xorw %dx,%dx # Use COM1 int $0x14 # BIOS: Serial I/O popw %dx # Restore #endif /* SIO */ retw # To caller /* One-sector disk I/O routine */ /* * %dl: drive, %si partition entry, %es:%bx transfer buffer. * Load the CHS values and possibly the LBA address from the block * at %si, and use the appropriate method to load the sector. * Don't use packet mode for a floppy. */ intx13: # Prepare CHS parameters movb 0x1(%si),%dh # Load head movw 0x2(%si),%cx # Load cylinder:sector movb $0x1,%al # Sector count pushw %si # Save movw %sp,%di # Save #ifndef CHECK_DRIVE /* floppy support */ testb %dl, %dl # is this a floppy ? jz 1f # Yes, use CHS mode #endif testb $USEPACKET,_FLAGS(%bp) # Use packet interface? jz 1f # No pushl $0x0 # Set the pushl 0x8(%si) # LBA address pushw %es # Set the transfer pushw %bx # buffer address push $0x1 # Block count push $0x10 # Packet size movw %sp,%si # Packet pointer decw %ax # Verify off orb $0x40,%ah # Use disk packet 1: int $0x13 # BIOS: Disk I/O movw %di,%sp # Restore popw %si # Restore retw # To caller /* * Various menu strings. 'item' goes after 'prompt' to save space. * Also use shorter versions to make room for the PXE/INT18 code. */ prompt: #ifdef PXE .ascii "\nF6 PXE\r" #endif .ascii "\nBoot:" item: .ascii " "; .byte ' '|0x80 crlf: .ascii "\r"; .byte '\n'|0x80 /* Partition type tables */ bootable_ids: /* * These values indicate bootable types we know about. * Corresponding descriptions are at desc_ofs: * Entries don't need to be sorted. */ .byte 0x83, 0xa5, 0xa6, 0xa9, 0x06, 0x07, 0x0b #ifndef SAVE_MORE_MEMORY .byte 0x05 # extended partition #endif #ifndef SAVE_MEMORY /* other DOS partitions */ .byte 0x01 # FAT12 .byte 0x04 # FAT16 < 32M #endif desc_ofs: /* * Offsets that match the known types above, used to point to the * actual partition name. The last entry must point to os_misc, * which is used for non-matching names. */ .byte os_linux-. # 131, Linux .byte os_freebsd-. # 165, FreeBSD .byte os_bsd-. # 166, OpenBSD .byte os_bsd-. # 169, NetBSD .byte os_dos-. # 6, FAT16 >= 32M .byte os_win-. # 7, NTFS .byte os_win-. # 11, FAT32 #ifndef SAVE_MORE_MEMORY .byte os_ext-. # 5, DOS Ext #endif #ifndef SAVE_MEMORY .byte os_dos-. # 1, FAT12 DOS .byte os_dos-. # 4, FAT16 <32M #endif .byte os_misc-. # Unknown /* * And here are the strings themselves. The last byte of * the string has bit 7 set. */ os_misc: .byte '?'|0x80 os_dos: #ifndef SAVE_MORE_MEMORY /* 'DOS' remapped to 'WIN' if no room */ .ascii "DO"; .byte 'S'|0x80 #endif os_win: .ascii "Wi"; .byte 'n'|0x80 os_linux: .ascii "Linu"; .byte 'x'|0x80 os_freebsd: .ascii "Free" os_bsd: .ascii "BS"; .byte 'D'|0x80 #ifndef SAVE_MORE_MEMORY os_ext: .ascii "EX"; .byte 'T'|0x80 #endif .org (0x200 + B0_OFF),0x90 /* * The boot0 version 1.0 parameter table. * Do not move it nor change the "Drive " string, boot0cfg * uses its offset and content to identify the boot sector. * The other fields are sometimes changed before writing back to the drive * Be especially careful that nxtdrv: must come after drive:, as it * is part of the same string. */ drive: .ascii "Drive " nxtdrv: .byte 0x0 # Next drive number opt: .byte 0x0 # Option setdrv_num: .byte 0x80 # Drive to force flags: .byte FLAGS # Flags #ifdef VOLUME_SERIAL .byte 0xa8,0xa8,0xa8,0xa8 # Volume Serial Number #endif ticks: .word TICKS # Delay .org PRT_OFF /* * Here is the 64 byte partition table that fdisk would fiddle with. */ partbl: .fill 0x40,0x1,0x0 # Partition table .word MAGIC # Magic number .org 0x200 # again, safety check endblock: diff --git a/stand/i386/boot2/boot2.c b/stand/i386/boot2/boot2.c index da699b58b3b4..b886c70f3808 100644 --- a/stand/i386/boot2/boot2.c +++ b/stand/i386/boot2/boot2.c @@ -1,660 +1,659 @@ /*- * Copyright (c) 1998 Robert Nordier * All rights reserved. * * Redistribution and use in source and binary forms are freely * permitted provided that the above copyright notice and this * paragraph and the following disclaimer are duplicated in all * such forms. * * This software is provided "AS IS" and without any express or * implied warranties, including, without limitation, the implied * warranties of merchantability and fitness for a particular * purpose. */ -#include #include #include #include #include #include #include #include #include #include #include #include "boot2.h" #include "lib.h" #include "paths.h" #include "rbx.h" /* Define to 0 to omit serial support */ #ifndef SERIAL #define SERIAL 1 #endif #define IO_KEYBOARD 1 #define IO_SERIAL 2 #if SERIAL #define DO_KBD (ioctrl & IO_KEYBOARD) #define DO_SIO (ioctrl & IO_SERIAL) #else #define DO_KBD (1) #define DO_SIO (0) #endif #define SECOND 18 /* Circa that many ticks in a second. */ #define ARGS 0x900 #define NOPT 14 #define NDEV 3 #define MEM_BASE 0x12 #define MEM_EXT 0x15 #define DRV_HARD 0x80 #define DRV_MASK 0x7f #define TYPE_AD 0 #define TYPE_DA 1 #define TYPE_MAXHARD TYPE_DA #define TYPE_FD 2 extern uint32_t _end; static const char optstr[NOPT] = "DhaCcdgmnpqrsv"; /* Also 'P', 'S' */ static const unsigned char flags[NOPT] = { RBX_DUAL, RBX_SERIAL, RBX_ASKNAME, RBX_CDROM, RBX_CONFIG, RBX_KDB, RBX_GDB, RBX_MUTE, RBX_NOINTR, RBX_PAUSE, RBX_QUIET, RBX_DFLTROOT, RBX_SINGLE, RBX_VERBOSE }; static const char *const dev_nm[NDEV] = {"ad", "da", "fd"}; static const unsigned char dev_maj[NDEV] = {30, 4, 2}; static struct dsk { unsigned drive; unsigned type; unsigned unit; uint8_t slice; uint8_t part; unsigned start; int init; } dsk; static char cmd[512], cmddup[512], knamebuf[1024]; static const char *kname; uint32_t opts; static struct bootinfo bootinfo; #if SERIAL static int comspeed = SIOSPD; static uint8_t ioctrl = IO_KEYBOARD; #endif int main(void); void exit(int); static void load(void); static int parse(void); static int dskread(void *, unsigned, unsigned); static void printf(const char *,...); static void putchar(int); static int drvread(void *, unsigned, unsigned); static int keyhit(unsigned); static int xputc(int); static int xgetc(int); static inline int getc(int); static void memcpy(void *, const void *, int); static void memcpy(void *dst, const void *src, int len) { const char *s; char *d; s = src; d = dst; while (len--) *d++ = *s++; } static inline int strcmp(const char *s1, const char *s2) { for (; *s1 == *s2 && *s1; s1++, s2++); return ((unsigned char)*s1 - (unsigned char)*s2); } #define UFS_SMALL_CGBASE #include "ufsread.c" static int xfsread(ufs_ino_t inode, void *buf, size_t nbyte) { if ((size_t)fsread(inode, buf, nbyte) != nbyte) { printf("Invalid %s\n", "format"); return (-1); } return (0); } static inline void getstr(void) { char *s; int c; s = cmd; for (;;) { switch (c = xgetc(0)) { case 0: break; case '\177': case '\b': if (s > cmd) { s--; printf("\b \b"); } break; case '\n': case '\r': *s = 0; return; default: if (s - cmd < sizeof(cmd) - 1) *s++ = c; putchar(c); } } } static inline void putc(int c) { v86.addr = 0x10; v86.eax = 0xe00 | (c & 0xff); v86.ebx = 0x7; v86int(); } int main(void) { uint8_t autoboot; ufs_ino_t ino; size_t nbyte; dmadat = (void *)(roundup2(__base + (int32_t)&_end, 0x10000) - __base); v86.ctl = V86_FLAGS; v86.efl = PSL_RESERVED_DEFAULT | PSL_I; dsk.drive = *(uint8_t *)PTOV(ARGS); dsk.type = dsk.drive & DRV_HARD ? TYPE_AD : TYPE_FD; dsk.unit = dsk.drive & DRV_MASK; dsk.slice = *(uint8_t *)PTOV(ARGS + 1) + 1; bootinfo.bi_version = BOOTINFO_VERSION; bootinfo.bi_size = sizeof(bootinfo); /* Process configuration file */ autoboot = 1; if ((ino = lookup(PATH_CONFIG)) || (ino = lookup(PATH_DOTCONFIG))) { nbyte = fsread(ino, cmd, sizeof(cmd) - 1); cmd[nbyte] = '\0'; } if (*cmd) { memcpy(cmddup, cmd, sizeof(cmd)); if (parse()) autoboot = 0; if (!OPT_CHECK(RBX_QUIET)) printf("%s: %s", PATH_CONFIG, cmddup); /* Do not process this command twice */ *cmd = 0; } /* * Try to exec stage 3 boot loader. If interrupted by a keypress, * or in case of failure, try to load a kernel directly instead. */ if (!kname) { kname = PATH_LOADER; if (autoboot && !keyhit(3*SECOND)) { load(); kname = PATH_KERNEL; } } /* Present the user with the boot2 prompt. */ for (;;) { if (!autoboot || !OPT_CHECK(RBX_QUIET)) printf("\nFreeBSD/x86 boot\n" "Default: %u:%s(%u,%c)%s\n" "boot: ", dsk.drive & DRV_MASK, dev_nm[dsk.type], dsk.unit, 'a' + dsk.part, kname); if (DO_SIO) sio_flush(); if (!autoboot || keyhit(3*SECOND)) getstr(); else if (!autoboot || !OPT_CHECK(RBX_QUIET)) putchar('\n'); autoboot = 0; if (parse()) putchar('\a'); else load(); } } /* XXX - Needed for btxld to link the boot2 binary; do not remove. */ void exit(int x) { } static void load(void) { union { struct exec ex; Elf32_Ehdr eh; } hdr; static Elf32_Phdr ep[2]; static Elf32_Shdr es[2]; caddr_t p; ufs_ino_t ino; uint32_t addr; int k; uint8_t i, j; if (!(ino = lookup(kname))) { if (!ls) printf("No %s\n", kname); return; } if (xfsread(ino, &hdr, sizeof(hdr))) return; if (N_GETMAGIC(hdr.ex) == ZMAGIC) { addr = hdr.ex.a_entry & 0xffffff; p = PTOV(addr); fs_off = PAGE_SIZE; if (xfsread(ino, p, hdr.ex.a_text)) return; p += roundup2(hdr.ex.a_text, PAGE_SIZE); if (xfsread(ino, p, hdr.ex.a_data)) return; } else if (IS_ELF(hdr.eh)) { fs_off = hdr.eh.e_phoff; for (j = k = 0; k < hdr.eh.e_phnum && j < 2; k++) { if (xfsread(ino, ep + j, sizeof(ep[0]))) return; if (ep[j].p_type == PT_LOAD) j++; } for (i = 0; i < 2; i++) { p = PTOV(ep[i].p_paddr & 0xffffff); fs_off = ep[i].p_offset; if (xfsread(ino, p, ep[i].p_filesz)) return; } p += roundup2(ep[1].p_memsz, PAGE_SIZE); bootinfo.bi_symtab = VTOP(p); if (hdr.eh.e_shnum == hdr.eh.e_shstrndx + 3) { fs_off = hdr.eh.e_shoff + sizeof(es[0]) * (hdr.eh.e_shstrndx + 1); if (xfsread(ino, &es, sizeof(es))) return; for (i = 0; i < 2; i++) { *(Elf32_Word *)p = es[i].sh_size; p += sizeof(es[i].sh_size); fs_off = es[i].sh_offset; if (xfsread(ino, p, es[i].sh_size)) return; p += es[i].sh_size; } } addr = hdr.eh.e_entry & 0xffffff; bootinfo.bi_esymtab = VTOP(p); } else { printf("Invalid %s\n", "format"); return; } bootinfo.bi_kernelname = VTOP(kname); bootinfo.bi_bios_dev = dsk.drive; __exec((caddr_t)addr, RB_BOOTINFO | (opts & RBX_MASK), MAKEBOOTDEV(dev_maj[dsk.type], dsk.slice, dsk.unit, dsk.part), 0, 0, 0, VTOP(&bootinfo)); } static int parse(void) { char *arg, *ep, *p, *q; const char *cp; unsigned int drv; int c, i, j; size_t k; arg = cmd; while ((c = *arg++)) { if (c == ' ' || c == '\t' || c == '\n') continue; for (p = arg; *p && *p != '\n' && *p != ' ' && *p != '\t'; p++); ep = p; if (*p) *p++ = 0; if (c == '-') { while ((c = *arg++)) { if (c == 'P') { if (*(uint8_t *)PTOV(0x496) & 0x10) { cp = "yes"; } else { opts |= OPT_SET(RBX_DUAL) | OPT_SET(RBX_SERIAL); cp = "no"; } printf("Keyboard: %s\n", cp); continue; #if SERIAL } else if (c == 'S') { j = 0; while ((u_int)(i = *arg++ - '0') <= 9) j = j * 10 + i; if (j > 0 && i == -'0') { comspeed = j; break; } /* * Fall through to error below * ('S' not in optstr[]). */ #endif } for (i = 0; c != optstr[i]; i++) if (i == NOPT - 1) return (-1); opts ^= OPT_SET(flags[i]); } #if SERIAL ioctrl = OPT_CHECK(RBX_DUAL) ? (IO_SERIAL|IO_KEYBOARD) : OPT_CHECK(RBX_SERIAL) ? IO_SERIAL : IO_KEYBOARD; if (DO_SIO) { if (sio_init(115200 / comspeed) != 0) ioctrl &= ~IO_SERIAL; } #endif } else { for (q = arg--; *q && *q != '('; q++); if (*q) { drv = -1; if (arg[1] == ':') { drv = *arg - '0'; if (drv > 9) return (-1); arg += 2; } if (q - arg != 2) return (-1); for (i = 0; arg[0] != dev_nm[i][0] || arg[1] != dev_nm[i][1]; i++) if (i == NDEV - 1) return (-1); dsk.type = i; arg += 3; dsk.unit = *arg - '0'; if (arg[1] != ',' || dsk.unit > 9) return (-1); arg += 2; dsk.slice = WHOLE_DISK_SLICE; if (arg[1] == ',') { dsk.slice = *arg - '0' + 1; if (dsk.slice > NDOSPART + 1) return (-1); arg += 2; } if (arg[1] != ')') return (-1); dsk.part = *arg - 'a'; if (dsk.part > 7) return (-1); arg += 2; if (drv == -1) drv = dsk.unit; dsk.drive = (dsk.type <= TYPE_MAXHARD ? DRV_HARD : 0) + drv; dsk_meta = 0; } k = ep - arg; if (k > 0) { if (k >= sizeof(knamebuf)) return (-1); memcpy(knamebuf, arg, k + 1); kname = knamebuf; } } arg = p; } return (0); } static int dskread(void *buf, unsigned lba, unsigned nblk) { struct dos_partition *dp; struct disklabel *d; char *sec; unsigned i; uint8_t sl; const char *reason; if (!dsk_meta) { sec = dmadat->secbuf; dsk.start = 0; if (drvread(sec, DOSBBSECTOR, 1)) return (-1); dp = (void *)(sec + DOSPARTOFF); sl = dsk.slice; if (sl < BASE_SLICE) { for (i = 0; i < NDOSPART; i++) if (dp[i].dp_typ == DOSPTYP_386BSD && (dp[i].dp_flag & 0x80 || sl < BASE_SLICE)) { sl = BASE_SLICE + i; if (dp[i].dp_flag & 0x80 || dsk.slice == COMPATIBILITY_SLICE) break; } if (dsk.slice == WHOLE_DISK_SLICE) dsk.slice = sl; } if (sl != WHOLE_DISK_SLICE) { if (sl != COMPATIBILITY_SLICE) dp += sl - BASE_SLICE; if (dp->dp_typ != DOSPTYP_386BSD) { reason = "slice"; goto error; } dsk.start = dp->dp_start; } if (drvread(sec, dsk.start + LABELSECTOR, 1)) return (-1); d = (void *)(sec + LABELOFFSET); if (d->d_magic != DISKMAGIC || d->d_magic2 != DISKMAGIC) { if (dsk.part != RAW_PART) { reason = "label"; goto error; } } else { if (!dsk.init) { if (d->d_type == DTYPE_SCSI) dsk.type = TYPE_DA; dsk.init++; } if (dsk.part >= d->d_npartitions || !d->d_partitions[dsk.part].p_size) { reason = "partition"; goto error; } dsk.start += d->d_partitions[dsk.part].p_offset; dsk.start -= d->d_partitions[RAW_PART].p_offset; } } return (drvread(buf, dsk.start + lba, nblk)); error: printf("Invalid %s\n", reason); return (-1); } static void printf(const char *fmt,...) { va_list ap; static char buf[10]; char *s; unsigned u; int c; va_start(ap, fmt); while ((c = *fmt++)) { if (c == '%') { c = *fmt++; switch (c) { case 'c': putchar(va_arg(ap, int)); continue; case 's': for (s = va_arg(ap, char *); *s; s++) putchar(*s); continue; case 'u': u = va_arg(ap, unsigned); s = buf; do *s++ = '0' + u % 10U; while (u /= 10U); while (--s >= buf) putchar(*s); continue; } } putchar(c); } va_end(ap); return; } static void putchar(int c) { if (c == '\n') xputc('\r'); xputc(c); } static int drvread(void *buf, unsigned lba, unsigned nblk) { static unsigned c = 0x2d5c7c2f; if (!OPT_CHECK(RBX_QUIET)) { xputc(c = c << 8 | c >> 24); xputc('\b'); } v86.ctl = V86_ADDR | V86_CALLF | V86_FLAGS; v86.addr = XREADORG; /* call to xread in boot1 */ v86.es = VTOPSEG(buf); v86.eax = lba; v86.ebx = VTOPOFF(buf); v86.ecx = lba >> 16; v86.edx = nblk << 8 | dsk.drive; v86int(); v86.ctl = V86_FLAGS; if (V86_CY(v86.efl)) { printf("error %u lba %u\n", v86.eax >> 8 & 0xff, lba); return (-1); } return (0); } static int keyhit(unsigned ticks) { uint32_t t0, t1; if (OPT_CHECK(RBX_NOINTR)) return (0); t0 = 0; for (;;) { if (xgetc(1)) return (1); t1 = *(uint32_t *)PTOV(0x46c); if (!t0) t0 = t1; if ((uint32_t)(t1 - t0) >= ticks) return (0); } } static int xputc(int c) { if (DO_KBD) putc(c); if (DO_SIO) sio_putc(c); return (c); } static int getc(int fn) { v86.addr = 0x16; v86.eax = fn << 8; v86int(); return (fn == 0 ? v86.eax & 0xff : !V86_ZR(v86.efl)); } static int xgetc(int fn) { if (OPT_CHECK(RBX_NOINTR)) return (0); for (;;) { if (DO_KBD && getc(1)) return (fn ? 1 : getc(0)); if (DO_SIO && sio_ischar()) return (fn ? 1 : sio_getc()); if (fn) return (0); } } diff --git a/stand/i386/common/cons.c b/stand/i386/common/cons.c index c3a44499b5b4..1007d6965d95 100644 --- a/stand/i386/common/cons.c +++ b/stand/i386/common/cons.c @@ -1,202 +1,201 @@ /*- * Copyright (c) 1998 Robert Nordier * All rights reserved. * * Redistribution and use in source and binary forms are freely * permitted provided that the above copyright notice and this * paragraph and the following disclaimer are duplicated in all * such forms. * * This software is provided "AS IS" and without any express or * implied warranties, including, without limitation, the implied * warranties of merchantability and fitness for a particular * purpose. */ -#include #include #include #include #include "stand.h" #include "lib.h" #include "rbx.h" #include "cons.h" #define SECOND 18 /* Circa that many ticks in a second. */ uint8_t ioctrl = IO_KEYBOARD; void putc(int c) { v86.ctl = V86_FLAGS; v86.addr = 0x10; v86.eax = 0xe00 | (c & 0xff); v86.ebx = 0x7; v86int(); } void xputc(int c) { if (ioctrl & IO_KEYBOARD) putc(c); if (ioctrl & IO_SERIAL) sio_putc(c); } static void getcursor(int *row, int *col) { v86.ctl = V86_FLAGS; v86.addr = 0x10; v86.eax = 0x300; v86.ebx = 0x7; v86int(); if (row != NULL) *row = v86.edx >> 8; if (col != NULL) *col = v86.edx & 0xff; } void putchar(int c) { int i, col; switch (c) { case '\n': xputc('\r'); break; case '\t': col = 0; getcursor(NULL, &col); col = 8 - (col % 8); for (i = 0; i < col; i++) xputc(' '); return; } xputc(c); } int getc(int fn) { v86.ctl = V86_FLAGS; v86.addr = 0x16; v86.eax = fn << 8; v86int(); if (fn == 0) return (v86.eax); if (V86_ZR(v86.efl)) return (0); return (v86.eax); } int xgetc(int fn) { if (OPT_CHECK(RBX_NOINTR)) return (0); for (;;) { if (ioctrl & IO_KEYBOARD && getc(1)) return (fn ? 1 : getc(0)); if (ioctrl & IO_SERIAL && sio_ischar()) return (fn ? 1 : sio_getc()); if (fn) return (0); } /* NOTREACHED */ } int getchar(void) { return (xgetc(0) & 0xff); } int keyhit(unsigned int secs) { uint32_t t0, t1, c; if (OPT_CHECK(RBX_NOINTR)) return (0); secs *= SECOND; t0 = 0; for (;;) { /* * The extra comparison is an attempt to work around * what appears to be a bug in QEMU and Bochs. Both emulators * sometimes report a key-press with scancode one and ascii zero * when no such key is pressed in reality. As far as I can tell, * this only happens shortly after a reboot. */ c = xgetc(1); if (c != 0 && c != 0x0100) return (1); if (secs > 0) { t1 = *(uint32_t *)PTOV(0x46c); if (!t0) t0 = t1; if (t1 < t0 || t1 >= t0 + secs) return (0); } } /* NOTREACHED */ } void getstr(char *cmdstr, size_t cmdstrsize) { char *s; int c; s = cmdstr; for (;;) { c = xgetc(0); /* Translate some extended codes. */ switch (c) { case 0x5300: /* delete */ c = '\177'; break; default: c &= 0xff; break; } switch (c) { case '\177': case '\b': if (s > cmdstr) { s--; printf("\b \b"); } break; case '\n': case '\r': *s = 0; return; default: if (c >= 0x20 && c <= 0x7e) { if (s - cmdstr < cmdstrsize - 1) *s++ = c; putchar(c); } break; } } } diff --git a/stand/i386/common/drv.c b/stand/i386/common/drv.c index bc8927dc6c5e..394e1b1e8493 100644 --- a/stand/i386/common/drv.c +++ b/stand/i386/common/drv.c @@ -1,100 +1,99 @@ /*- * Copyright (c) 1998 Robert Nordier * Copyright (c) 2010 Pawel Jakub Dawidek * All rights reserved. * * Redistribution and use in source and binary forms are freely * permitted provided that the above copyright notice and this * paragraph and the following disclaimer are duplicated in all * such forms. * * This software is provided "AS IS" and without any express or * implied warranties, including, without limitation, the implied * warranties of merchantability and fitness for a particular * purpose. */ -#include #include #include #include "stand.h" #include "rbx.h" #include "drv.h" #include "edd.h" static struct edd_params params; uint64_t drvsize(struct dsk *dskp) { params.len = sizeof(struct edd_params); v86.ctl = V86_FLAGS; v86.addr = 0x13; v86.eax = 0x4800; v86.edx = dskp->drive; v86.ds = VTOPSEG(¶ms); v86.esi = VTOPOFF(¶ms); v86int(); if (V86_CY(v86.efl)) { printf("error %u\n", v86.eax >> 8 & 0xff); return (0); } return (params.sectors); } static struct edd_packet packet; int drvread(struct dsk *dskp, void *buf, daddr_t lba, unsigned nblk) { static unsigned c = 0x2d5c7c2f; if (!OPT_CHECK(RBX_QUIET)) printf("%c\b", c = c << 8 | c >> 24); packet.len = sizeof(struct edd_packet); packet.count = nblk; packet.off = VTOPOFF(buf); packet.seg = VTOPSEG(buf); packet.lba = lba; v86.ctl = V86_FLAGS; v86.addr = 0x13; v86.eax = 0x4200; v86.edx = dskp->drive; v86.ds = VTOPSEG(&packet); v86.esi = VTOPOFF(&packet); v86int(); if (V86_CY(v86.efl)) { printf("%s: error %u lba %llu\n", BOOTPROG, v86.eax >> 8 & 0xff, lba); return (-1); } return (0); } #if defined(GPT) || defined(ZFS) int drvwrite(struct dsk *dskp, void *buf, daddr_t lba, unsigned nblk) { packet.len = sizeof(struct edd_packet); packet.count = nblk; packet.off = VTOPOFF(buf); packet.seg = VTOPSEG(buf); packet.lba = lba; v86.ctl = V86_FLAGS; v86.addr = 0x13; v86.eax = 0x4300; v86.edx = dskp->drive; v86.ds = VTOPSEG(&packet); v86.esi = VTOPOFF(&packet); v86int(); if (V86_CY(v86.efl)) { printf("error %u lba %llu\n", v86.eax >> 8 & 0xff, lba); return (-1); } return (0); } #endif /* GPT || ZFS */ diff --git a/stand/i386/gptboot/gptboot.c b/stand/i386/gptboot/gptboot.c index 7cae837f9a56..949c00cdf544 100644 --- a/stand/i386/gptboot/gptboot.c +++ b/stand/i386/gptboot/gptboot.c @@ -1,656 +1,655 @@ /*- * Copyright (c) 1998 Robert Nordier * All rights reserved. * * Redistribution and use in source and binary forms are freely * permitted provided that the above copyright notice and this * paragraph and the following disclaimer are duplicated in all * such forms. * * This software is provided "AS IS" and without any express or * implied warranties, including, without limitation, the implied * warranties of merchantability and fitness for a particular * purpose. */ -#include #include #include #include #include #include #include #include #include #include #include #include #include "stand.h" #include "bootargs.h" #include "lib.h" #include "rbx.h" #include "drv.h" #include "cons.h" #include "gpt.h" #include "paths.h" #define ARGS 0x900 #define NOPT 14 #define NDEV 3 #define MEM_BASE 0x12 #define MEM_EXT 0x15 #define DRV_HARD 0x80 #define DRV_MASK 0x7f #define TYPE_AD 0 #define TYPE_DA 1 #define TYPE_MAXHARD TYPE_DA #define TYPE_FD 2 extern uint32_t _end; static const uuid_t freebsd_ufs_uuid = GPT_ENT_TYPE_FREEBSD_UFS; static const char optstr[NOPT] = "DhaCcdgmnpqrsv"; /* Also 'P', 'S' */ static const unsigned char flags[NOPT] = { RBX_DUAL, RBX_SERIAL, RBX_ASKNAME, RBX_CDROM, RBX_CONFIG, RBX_KDB, RBX_GDB, RBX_MUTE, RBX_NOINTR, RBX_PAUSE, RBX_QUIET, RBX_DFLTROOT, RBX_SINGLE, RBX_VERBOSE }; uint32_t opts; static const char *const dev_nm[NDEV] = {"ad", "da", "fd"}; static const unsigned char dev_maj[NDEV] = {30, 4, 2}; static char kname[1024]; static int comspeed = SIOSPD; static struct bootinfo bootinfo; #ifdef LOADER_GELI_SUPPORT static struct geli_boot_args geliargs; #endif static vm_offset_t high_heap_base; static uint32_t bios_basemem, bios_extmem, high_heap_size; static struct bios_smap smap; /* * The minimum amount of memory to reserve in bios_extmem for the heap. */ #define HEAP_MIN (3 * 1024 * 1024) static char *heap_next; static char *heap_end; static void load(void); static int parse_cmds(char *, int *); static int dskread(void *, daddr_t, unsigned); #ifdef LOADER_GELI_SUPPORT static int vdev_read(void *vdev __unused, void *priv, off_t off, void *buf, size_t bytes); #endif #include "ufsread.c" #include "gpt.c" #ifdef LOADER_GELI_SUPPORT #include "geliboot.h" static char gelipw[GELI_PW_MAXLEN]; #endif struct gptdsk { struct dsk dsk; #ifdef LOADER_GELI_SUPPORT struct geli_dev *gdev; #endif }; static struct gptdsk gdsk; static inline int xfsread(ufs_ino_t inode, void *buf, size_t nbyte) { if ((size_t)fsread(inode, buf, nbyte) != nbyte) { printf("Invalid %s\n", "format"); return (-1); } return (0); } static void bios_getmem(void) { uint64_t size; /* Parse system memory map */ v86.ebx = 0; do { v86.ctl = V86_FLAGS; v86.addr = MEM_EXT; /* int 0x15 function 0xe820*/ v86.eax = 0xe820; v86.ecx = sizeof(struct bios_smap); v86.edx = SMAP_SIG; v86.es = VTOPSEG(&smap); v86.edi = VTOPOFF(&smap); v86int(); if ((v86.efl & 1) || (v86.eax != SMAP_SIG)) break; /* look for a low-memory segment that's large enough */ if ((smap.type == SMAP_TYPE_MEMORY) && (smap.base == 0) && (smap.length >= (512 * 1024))) bios_basemem = smap.length; /* look for the first segment in 'extended' memory */ if ((smap.type == SMAP_TYPE_MEMORY) && (smap.base == 0x100000)) { bios_extmem = smap.length; } /* * Look for the largest segment in 'extended' memory beyond * 1MB but below 4GB. */ if ((smap.type == SMAP_TYPE_MEMORY) && (smap.base > 0x100000) && (smap.base < 0x100000000ull)) { size = smap.length; /* * If this segment crosses the 4GB boundary, * truncate it. */ if (smap.base + size > 0x100000000ull) size = 0x100000000ull - smap.base; if (size > high_heap_size) { high_heap_size = size; high_heap_base = smap.base; } } } while (v86.ebx != 0); /* Fall back to the old compatibility function for base memory */ if (bios_basemem == 0) { v86.ctl = 0; v86.addr = 0x12; /* int 0x12 */ v86int(); bios_basemem = (v86.eax & 0xffff) * 1024; } /* * Fall back through several compatibility functions for extended * memory */ if (bios_extmem == 0) { v86.ctl = V86_FLAGS; v86.addr = 0x15; /* int 0x15 function 0xe801*/ v86.eax = 0xe801; v86int(); if (!(v86.efl & 1)) { bios_extmem = ((v86.ecx & 0xffff) + ((v86.edx & 0xffff) * 64)) * 1024; } } if (bios_extmem == 0) { v86.ctl = 0; v86.addr = 0x15; /* int 0x15 function 0x88*/ v86.eax = 0x8800; v86int(); bios_extmem = (v86.eax & 0xffff) * 1024; } /* * If we have extended memory and did not find a suitable heap * region in the SMAP, use the last 3MB of 'extended' memory as a * high heap candidate. */ if (bios_extmem >= HEAP_MIN && high_heap_size < HEAP_MIN) { high_heap_size = HEAP_MIN; high_heap_base = bios_extmem + 0x100000 - HEAP_MIN; } } static int gptinit(void) { if (gptread(&gdsk.dsk, dmadat->secbuf) == -1) { printf("%s: unable to load GPT\n", BOOTPROG); return (-1); } if (gptfind(&freebsd_ufs_uuid, &gdsk.dsk, gdsk.dsk.part) == -1) { printf("%s: no UFS partition was found\n", BOOTPROG); return (-1); } #ifdef LOADER_GELI_SUPPORT gdsk.gdev = geli_taste(vdev_read, &gdsk.dsk, (gpttable[curent].ent_lba_end - gpttable[curent].ent_lba_start), "disk%up%u:", gdsk.dsk.unit, curent + 1); if (gdsk.gdev != NULL) { if (geli_havekey(gdsk.gdev) != 0 && geli_passphrase(gdsk.gdev, gelipw) != 0) { printf("%s: unable to decrypt GELI key\n", BOOTPROG); return (-1); } } #endif dsk_meta = 0; return (0); } int main(void); int main(void) { char cmd[512], cmdtmp[512]; ssize_t sz; int autoboot, dskupdated; ufs_ino_t ino; dmadat = (void *)(roundup2(__base + (int32_t)&_end, 0x10000) - __base); bios_getmem(); if (high_heap_size > 0) { heap_end = PTOV(high_heap_base + high_heap_size); heap_next = PTOV(high_heap_base); } else { heap_next = (char *)dmadat + sizeof(*dmadat); heap_end = (char *)PTOV(bios_basemem); } setheap(heap_next, heap_end); v86.ctl = V86_FLAGS; v86.efl = PSL_RESERVED_DEFAULT | PSL_I; gdsk.dsk.drive = *(uint8_t *)PTOV(ARGS); gdsk.dsk.type = gdsk.dsk.drive & DRV_HARD ? TYPE_AD : TYPE_FD; gdsk.dsk.unit = gdsk.dsk.drive & DRV_MASK; gdsk.dsk.part = -1; gdsk.dsk.start = 0; bootinfo.bi_version = BOOTINFO_VERSION; bootinfo.bi_size = sizeof(bootinfo); bootinfo.bi_basemem = bios_basemem / 1024; bootinfo.bi_extmem = bios_extmem / 1024; bootinfo.bi_memsizes_valid++; bootinfo.bi_bios_dev = gdsk.dsk.drive; /* Process configuration file */ if (gptinit() != 0) return (-1); autoboot = 1; *cmd = '\0'; for (;;) { *kname = '\0'; if ((ino = lookup(PATH_CONFIG)) || (ino = lookup(PATH_DOTCONFIG))) { sz = fsread(ino, cmd, sizeof(cmd) - 1); cmd[(sz < 0) ? 0 : sz] = '\0'; } if (*cmd != '\0') { memcpy(cmdtmp, cmd, sizeof(cmdtmp)); if (parse_cmds(cmdtmp, &dskupdated)) break; if (dskupdated && gptinit() != 0) break; if (!OPT_CHECK(RBX_QUIET)) printf("%s: %s", PATH_CONFIG, cmd); *cmd = '\0'; } if (autoboot && keyhit(3)) { if (*kname == '\0') memcpy(kname, PATH_LOADER, sizeof(PATH_LOADER)); break; } autoboot = 0; /* * Try to exec stage 3 boot loader. If interrupted by a * keypress, or in case of failure, try to load a kernel * directly instead. */ if (*kname != '\0') load(); memcpy(kname, PATH_LOADER, sizeof(PATH_LOADER)); load(); memcpy(kname, PATH_KERNEL, sizeof(PATH_KERNEL)); load(); gptbootfailed(&gdsk.dsk); if (gptfind(&freebsd_ufs_uuid, &gdsk.dsk, -1) == -1) break; dsk_meta = 0; } /* Present the user with the boot2 prompt. */ for (;;) { if (!OPT_CHECK(RBX_QUIET)) { printf("\nFreeBSD/x86 boot\n" "Default: %u:%s(%up%u)%s\n" "boot: ", gdsk.dsk.drive & DRV_MASK, dev_nm[gdsk.dsk.type], gdsk.dsk.unit, gdsk.dsk.part, kname); } if (ioctrl & IO_SERIAL) sio_flush(); *cmd = '\0'; if (keyhit(0)) getstr(cmd, sizeof(cmd)); else if (!OPT_CHECK(RBX_QUIET)) putchar('\n'); if (parse_cmds(cmd, &dskupdated)) { putchar('\a'); continue; } if (dskupdated && gptinit() != 0) continue; load(); } /* NOTREACHED */ } /* XXX - Needed for btxld to link the boot2 binary; do not remove. */ void exit(int x) { while (1); __unreachable(); } static void load(void) { union { struct exec ex; Elf32_Ehdr eh; } hdr; static Elf32_Phdr ep[2]; static Elf32_Shdr es[2]; caddr_t p; ufs_ino_t ino; uint32_t addr, x; int fmt, i, j; if (!(ino = lookup(kname))) { if (!ls) { printf("%s: No %s on %u:%s(%up%u)\n", BOOTPROG, kname, gdsk.dsk.drive & DRV_MASK, dev_nm[gdsk.dsk.type], gdsk.dsk.unit, gdsk.dsk.part); } return; } if (xfsread(ino, &hdr, sizeof(hdr))) return; if (N_GETMAGIC(hdr.ex) == ZMAGIC) fmt = 0; else if (IS_ELF(hdr.eh)) fmt = 1; else { printf("Invalid %s\n", "format"); return; } if (fmt == 0) { addr = hdr.ex.a_entry & 0xffffff; p = PTOV(addr); fs_off = PAGE_SIZE; if (xfsread(ino, p, hdr.ex.a_text)) return; p += roundup2(hdr.ex.a_text, PAGE_SIZE); if (xfsread(ino, p, hdr.ex.a_data)) return; p += hdr.ex.a_data + roundup2(hdr.ex.a_bss, PAGE_SIZE); bootinfo.bi_symtab = VTOP(p); memcpy(p, &hdr.ex.a_syms, sizeof(hdr.ex.a_syms)); p += sizeof(hdr.ex.a_syms); if (hdr.ex.a_syms) { if (xfsread(ino, p, hdr.ex.a_syms)) return; p += hdr.ex.a_syms; if (xfsread(ino, p, sizeof(int))) return; x = *(uint32_t *)p; p += sizeof(int); x -= sizeof(int); if (xfsread(ino, p, x)) return; p += x; } } else { fs_off = hdr.eh.e_phoff; for (j = i = 0; i < hdr.eh.e_phnum && j < 2; i++) { if (xfsread(ino, ep + j, sizeof(ep[0]))) return; if (ep[j].p_type == PT_LOAD) j++; } for (i = 0; i < 2; i++) { p = PTOV(ep[i].p_paddr & 0xffffff); fs_off = ep[i].p_offset; if (xfsread(ino, p, ep[i].p_filesz)) return; } p += roundup2(ep[1].p_memsz, PAGE_SIZE); bootinfo.bi_symtab = VTOP(p); if (hdr.eh.e_shnum == hdr.eh.e_shstrndx + 3) { fs_off = hdr.eh.e_shoff + sizeof(es[0]) * (hdr.eh.e_shstrndx + 1); if (xfsread(ino, &es, sizeof(es))) return; for (i = 0; i < 2; i++) { memcpy(p, &es[i].sh_size, sizeof(es[i].sh_size)); p += sizeof(es[i].sh_size); fs_off = es[i].sh_offset; if (xfsread(ino, p, es[i].sh_size)) return; p += es[i].sh_size; } } addr = hdr.eh.e_entry & 0xffffff; } bootinfo.bi_esymtab = VTOP(p); bootinfo.bi_kernelname = VTOP(kname); bootinfo.bi_bios_dev = gdsk.dsk.drive; #ifdef LOADER_GELI_SUPPORT geliargs.size = sizeof(geliargs); explicit_bzero(gelipw, sizeof(gelipw)); export_geli_boot_data(&geliargs.gelidata); #endif /* * Note that the geliargs struct is passed by value, not by pointer. * Code in btxldr.S copies the values from the entry stack to a fixed * location within loader(8) at startup due to the presence of the * KARGS_FLAGS_EXTARG flag. */ __exec((caddr_t)addr, RB_BOOTINFO | (opts & RBX_MASK), MAKEBOOTDEV(dev_maj[gdsk.dsk.type], gdsk.dsk.part + 1, gdsk.dsk.unit, 0xff), #ifdef LOADER_GELI_SUPPORT KARGS_FLAGS_GELI | KARGS_FLAGS_EXTARG, 0, 0, VTOP(&bootinfo), geliargs #else 0, 0, 0, VTOP(&bootinfo) #endif ); } static int parse_cmds(char *cmdstr, int *dskupdated) { char *arg; char *ep, *p, *q; const char *cp; unsigned int drv; int c, i, j; arg = cmdstr; *dskupdated = 0; while ((c = *arg++)) { if (c == ' ' || c == '\t' || c == '\n') continue; for (p = arg; *p && *p != '\n' && *p != ' ' && *p != '\t'; p++); ep = p; if (*p) *p++ = 0; if (c == '-') { while ((c = *arg++)) { if (c == 'P') { if (*(uint8_t *)PTOV(0x496) & 0x10) { cp = "yes"; } else { opts |= OPT_SET(RBX_DUAL) | OPT_SET(RBX_SERIAL); cp = "no"; } printf("Keyboard: %s\n", cp); continue; } else if (c == 'S') { j = 0; while ((unsigned int)(i = *arg++ - '0') <= 9) j = j * 10 + i; if (j > 0 && i == -'0') { comspeed = j; break; } /* * Fall through to error below * ('S' not in optstr[]). */ } for (i = 0; c != optstr[i]; i++) if (i == NOPT - 1) return (-1); opts ^= OPT_SET(flags[i]); } ioctrl = OPT_CHECK(RBX_DUAL) ? (IO_SERIAL|IO_KEYBOARD) : OPT_CHECK(RBX_SERIAL) ? IO_SERIAL : IO_KEYBOARD; if (ioctrl & IO_SERIAL) { if (sio_init(115200 / comspeed) != 0) ioctrl &= ~IO_SERIAL; } } else { for (q = arg--; *q && *q != '('; q++); if (*q) { drv = -1; if (arg[1] == ':') { drv = *arg - '0'; if (drv > 9) return (-1); arg += 2; } if (q - arg != 2) return (-1); for (i = 0; arg[0] != dev_nm[i][0] || arg[1] != dev_nm[i][1]; i++) if (i == NDEV - 1) return (-1); gdsk.dsk.type = i; arg += 3; gdsk.dsk.unit = *arg - '0'; if (arg[1] != 'p' || gdsk.dsk.unit > 9) return (-1); arg += 2; j = 0; while (*arg >= '0' && *arg <= '9') j = j * 10 + *arg++ - '0'; gdsk.dsk.part = j; if (gdsk.dsk.part < 1 || gdsk.dsk.part > 128) return (-1); if (arg[0] != ')') return (-1); arg++; if (drv == -1) drv = gdsk.dsk.unit; gdsk.dsk.drive = (gdsk.dsk.type <= TYPE_MAXHARD ? DRV_HARD : 0) + drv; *dskupdated = 1; } if ((i = ep - arg)) { if ((size_t)i >= sizeof(kname)) return (-1); memcpy(kname, arg, i + 1); } } arg = p; } return (0); } static int dskread(void *buf, daddr_t lba, unsigned nblk) { int err; err = drvread(&gdsk.dsk, buf, lba + gdsk.dsk.start, nblk); #ifdef LOADER_GELI_SUPPORT if (err == 0 && gdsk.gdev != NULL) { /* Decrypt */ if (geli_io(gdsk.gdev, GELI_DECRYPT, lba * DEV_BSIZE, buf, nblk * DEV_BSIZE)) return (err); } #endif return (err); } #ifdef LOADER_GELI_SUPPORT /* * Read function compatible with the ZFS callback, required to keep the GELI * implementation the same for both UFS and ZFS. */ static int vdev_read(void *vdev __unused, void *priv, off_t off, void *buf, size_t bytes) { char *p; daddr_t lba; unsigned int nb; struct gptdsk *dskp; dskp = (struct gptdsk *)priv; if ((off & (DEV_BSIZE - 1)) || (bytes & (DEV_BSIZE - 1))) return (-1); p = buf; lba = off / DEV_BSIZE; lba += dskp->dsk.start; while (bytes > 0) { nb = bytes / DEV_BSIZE; if (nb > VBLKSIZE / DEV_BSIZE) nb = VBLKSIZE / DEV_BSIZE; if (drvread(&dskp->dsk, dmadat->blkbuf, lba, nb)) return (-1); memcpy(p, dmadat->blkbuf, nb * DEV_BSIZE); p += nb * DEV_BSIZE; lba += nb; bytes -= nb * DEV_BSIZE; } return (0); } #endif /* LOADER_GELI_SUPPORT */ diff --git a/stand/i386/isoboot/isoboot.c b/stand/i386/isoboot/isoboot.c index 4c3f7af8537a..4ffe83cdc9fe 100644 --- a/stand/i386/isoboot/isoboot.c +++ b/stand/i386/isoboot/isoboot.c @@ -1,520 +1,519 @@ /*- * Copyright (c) 1998 Robert Nordier * All rights reserved. * * Redistribution and use in source and binary forms are freely * permitted provided that the above copyright notice and this * paragraph and the following disclaimer are duplicated in all * such forms. * * This software is provided "AS IS" and without any express or * implied warranties, including, without limitation, the implied * warranties of merchantability and fitness for a particular * purpose. */ -#include #include #include #include #include #include #include #include #include #include #include #include #include "stand.h" #include "bootargs.h" #include "lib.h" #include "rbx.h" #include "drv.h" #include "cons.h" #include "gpt.h" #include "paths.h" #define ARGS 0x900 #define NOPT 14 #define NDEV 3 #define MEM_BASE 0x12 #define MEM_EXT 0x15 #define DRV_HARD 0x80 #define DRV_MASK 0x7f #define TYPE_AD 0 #define TYPE_DA 1 #define TYPE_MAXHARD TYPE_DA #define TYPE_FD 2 extern uint32_t _end; static const char optstr[NOPT] = "DhaCcdgmnpqrsv"; /* Also 'P', 'S' */ static const unsigned char flags[NOPT] = { RBX_DUAL, RBX_SERIAL, RBX_ASKNAME, RBX_CDROM, RBX_CONFIG, RBX_KDB, RBX_GDB, RBX_MUTE, RBX_NOINTR, RBX_PAUSE, RBX_QUIET, RBX_DFLTROOT, RBX_SINGLE, RBX_VERBOSE }; uint32_t opts; static const char *const dev_nm[NDEV] = {"ad", "da", "fd"}; static const unsigned char dev_maj[NDEV] = {30, 4, 2}; static struct dsk dsk; static char kname[1024]; static int comspeed = SIOSPD; static struct bootinfo bootinfo; static vm_offset_t high_heap_base; static uint32_t bios_basemem, bios_extmem, high_heap_size; static struct bios_smap smap; /* * The minimum amount of memory to reserve in bios_extmem for the heap. */ #define HEAP_MIN (3 * 1024 * 1024) static char *heap_next; static char *heap_end; int main(void); static void load(void); static int parse_cmds(char *, int *); static uint8_t ls, dsk_meta; static uint32_t fs_off; #include "cd9660read.c" static inline int xfsread(uint64_t inode, void *buf, size_t nbyte) { if ((size_t)cd9660_fsread(inode, buf, nbyte) != nbyte) { printf("Invalid %s\n", "format"); return (-1); } return (0); } static void bios_getmem(void) { uint64_t size; /* Parse system memory map */ v86.ebx = 0; do { v86.ctl = V86_FLAGS; v86.addr = MEM_EXT; /* int 0x15 function 0xe820*/ v86.eax = 0xe820; v86.ecx = sizeof(struct bios_smap); v86.edx = SMAP_SIG; v86.es = VTOPSEG(&smap); v86.edi = VTOPOFF(&smap); v86int(); if ((v86.efl & 1) || (v86.eax != SMAP_SIG)) break; /* look for a low-memory segment that's large enough */ if ((smap.type == SMAP_TYPE_MEMORY) && (smap.base == 0) && (smap.length >= (512 * 1024))) bios_basemem = smap.length; /* look for the first segment in 'extended' memory */ if ((smap.type == SMAP_TYPE_MEMORY) && (smap.base == 0x100000)) { bios_extmem = smap.length; } /* * Look for the largest segment in 'extended' memory beyond * 1MB but below 4GB. */ if ((smap.type == SMAP_TYPE_MEMORY) && (smap.base > 0x100000) && (smap.base < 0x100000000ull)) { size = smap.length; /* * If this segment crosses the 4GB boundary, * truncate it. */ if (smap.base + size > 0x100000000ull) size = 0x100000000ull - smap.base; if (size > high_heap_size) { high_heap_size = size; high_heap_base = smap.base; } } } while (v86.ebx != 0); /* Fall back to the old compatibility function for base memory */ if (bios_basemem == 0) { v86.ctl = 0; v86.addr = 0x12; /* int 0x12 */ v86int(); bios_basemem = (v86.eax & 0xffff) * 1024; } /* * Fall back through several compatibility functions for extended * memory */ if (bios_extmem == 0) { v86.ctl = V86_FLAGS; v86.addr = 0x15; /* int 0x15 function 0xe801*/ v86.eax = 0xe801; v86int(); if (!(v86.efl & 1)) { bios_extmem = ((v86.ecx & 0xffff) + ((v86.edx & 0xffff) * 64)) * 1024; } } if (bios_extmem == 0) { v86.ctl = 0; v86.addr = 0x15; /* int 0x15 function 0x88*/ v86.eax = 0x8800; v86int(); bios_extmem = (v86.eax & 0xffff) * 1024; } /* * If we have extended memory and did not find a suitable heap * region in the SMAP, use the last 3MB of 'extended' memory as a * high heap candidate. */ if (bios_extmem >= HEAP_MIN && high_heap_size < HEAP_MIN) { high_heap_size = HEAP_MIN; high_heap_base = bios_extmem + 0x100000 - HEAP_MIN; } } int main(void) { char cmd[512], cmdtmp[512]; ssize_t sz; int autoboot, dskupdated; uint64_t ino; bios_getmem(); if (high_heap_size > 0) { heap_end = PTOV(high_heap_base + high_heap_size); heap_next = PTOV(high_heap_base); } else { heap_next = (char *) (roundup2(__base + (int32_t)&_end, 0x10000) - __base); heap_end = (char *)PTOV(bios_basemem); } setheap(heap_next, heap_end); v86.ctl = V86_FLAGS; v86.efl = PSL_RESERVED_DEFAULT | PSL_I; dsk.drive = *(uint8_t *)PTOV(ARGS); dsk.type = dsk.drive & DRV_HARD ? TYPE_AD : TYPE_FD; dsk.unit = dsk.drive & DRV_MASK; dsk.part = -1; dsk.start = 0; bootinfo.bi_version = BOOTINFO_VERSION; bootinfo.bi_size = sizeof(bootinfo); bootinfo.bi_basemem = bios_basemem / 1024; bootinfo.bi_extmem = bios_extmem / 1024; bootinfo.bi_memsizes_valid++; bootinfo.bi_bios_dev = dsk.drive; autoboot = 1; *cmd = '\0'; for (;;) { *kname = '\0'; if ((ino = cd9660_lookup(PATH_CONFIG)) || (ino = cd9660_lookup(PATH_DOTCONFIG))) { sz = cd9660_fsread(ino, cmd, sizeof(cmd) - 1); cmd[(sz < 0) ? 0 : sz] = '\0'; } if (*cmd != '\0') { memcpy(cmdtmp, cmd, sizeof(cmdtmp)); if (parse_cmds(cmdtmp, &dskupdated)) break; if (!OPT_CHECK(RBX_QUIET)) printf("%s: %s", PATH_CONFIG, cmd); *cmd = '\0'; } if (autoboot && keyhit(3)) { if (*kname == '\0') memcpy(kname, PATH_LOADER, sizeof(PATH_LOADER)); break; } autoboot = 0; /* * Try to exec stage 3 boot loader. If interrupted by a * keypress, or in case of failure, try to load a kernel * directly instead. */ if (*kname != '\0') load(); memcpy(kname, PATH_LOADER, sizeof(PATH_LOADER)); load(); memcpy(kname, PATH_KERNEL, sizeof(PATH_KERNEL)); load(); dsk_meta = 0; } /* Present the user with the boot2 prompt. */ for (;;) { if (!OPT_CHECK(RBX_QUIET)) { printf("\nFreeBSD/x86 boot\n" "Default: %u:%s(%up%u)%s\n" "boot: ", dsk.drive & DRV_MASK, dev_nm[dsk.type], dsk.unit, dsk.part, kname); } if (ioctrl & IO_SERIAL) sio_flush(); *cmd = '\0'; if (keyhit(0)) getstr(cmd, sizeof(cmd)); else if (!OPT_CHECK(RBX_QUIET)) putchar('\n'); if (parse_cmds(cmd, &dskupdated)) { putchar('\a'); continue; } load(); } /* NOTREACHED */ } /* Needed so btxld can link us properly; do not remove. */ void exit(int x) { while (1); __unreachable(); } static void load(void) { union { struct exec ex; Elf32_Ehdr eh; } hdr; static Elf32_Phdr ep[2]; static Elf32_Shdr es[2]; caddr_t p; uint64_t ino; uint32_t addr, x; int fmt, i, j; if (!(ino = cd9660_lookup(kname))) { if (!ls) { printf("%s: No %s on %u:%s(%up%u)\n", BOOTPROG, kname, dsk.drive & DRV_MASK, dev_nm[dsk.type], dsk.unit, dsk.part); } return; } if (xfsread(ino, &hdr, sizeof(hdr))) return; if (N_GETMAGIC(hdr.ex) == ZMAGIC) fmt = 0; else if (IS_ELF(hdr.eh)) fmt = 1; else { printf("Invalid %s\n", "format"); return; } if (fmt == 0) { addr = hdr.ex.a_entry & 0xffffff; p = PTOV(addr); fs_off = PAGE_SIZE; if (xfsread(ino, p, hdr.ex.a_text)) return; p += roundup2(hdr.ex.a_text, PAGE_SIZE); if (xfsread(ino, p, hdr.ex.a_data)) return; p += hdr.ex.a_data + roundup2(hdr.ex.a_bss, PAGE_SIZE); bootinfo.bi_symtab = VTOP(p); memcpy(p, &hdr.ex.a_syms, sizeof(hdr.ex.a_syms)); p += sizeof(hdr.ex.a_syms); if (hdr.ex.a_syms) { if (xfsread(ino, p, hdr.ex.a_syms)) return; p += hdr.ex.a_syms; if (xfsread(ino, p, sizeof(int))) return; x = *(uint32_t *)p; p += sizeof(int); x -= sizeof(int); if (xfsread(ino, p, x)) return; p += x; } } else { fs_off = hdr.eh.e_phoff; for (j = i = 0; i < hdr.eh.e_phnum && j < 2; i++) { if (xfsread(ino, ep + j, sizeof(ep[0]))) return; if (ep[j].p_type == PT_LOAD) j++; } for (i = 0; i < 2; i++) { p = PTOV(ep[i].p_paddr & 0xffffff); fs_off = ep[i].p_offset; if (xfsread(ino, p, ep[i].p_filesz)) return; } p += roundup2(ep[1].p_memsz, PAGE_SIZE); bootinfo.bi_symtab = VTOP(p); if (hdr.eh.e_shnum == hdr.eh.e_shstrndx + 3) { fs_off = hdr.eh.e_shoff + sizeof(es[0]) * (hdr.eh.e_shstrndx + 1); if (xfsread(ino, &es, sizeof(es))) return; for (i = 0; i < 2; i++) { memcpy(p, &es[i].sh_size, sizeof(es[i].sh_size)); p += sizeof(es[i].sh_size); fs_off = es[i].sh_offset; if (xfsread(ino, p, es[i].sh_size)) return; p += es[i].sh_size; } } addr = hdr.eh.e_entry & 0xffffff; } bootinfo.bi_esymtab = VTOP(p); bootinfo.bi_kernelname = VTOP(kname); bootinfo.bi_bios_dev = dsk.drive; __exec((caddr_t)addr, RB_BOOTINFO | (opts & RBX_MASK), MAKEBOOTDEV(dev_maj[dsk.type], 0, dsk.unit, 0), 0, 0, 0, VTOP(&bootinfo)); } static int parse_cmds(char *cmdstr, int *dskupdated) { char *arg; char *ep, *p, *q; const char *cp; unsigned int drv; int c, i, j; arg = cmdstr; *dskupdated = 0; while ((c = *arg++)) { if (c == ' ' || c == '\t' || c == '\n') continue; for (p = arg; *p && *p != '\n' && *p != ' ' && *p != '\t'; p++); ep = p; if (*p) *p++ = 0; if (c == '-') { while ((c = *arg++)) { if (c == 'P') { if (*(uint8_t *)PTOV(0x496) & 0x10) { cp = "yes"; } else { opts |= OPT_SET(RBX_DUAL) | OPT_SET(RBX_SERIAL); cp = "no"; } printf("Keyboard: %s\n", cp); continue; } else if (c == 'S') { j = 0; while ((unsigned int)(i = *arg++ - '0') <= 9) j = j * 10 + i; if (j > 0 && i == -'0') { comspeed = j; break; } /* * Fall through to error below * ('S' not in optstr[]). */ } for (i = 0; c != optstr[i]; i++) if (i == NOPT - 1) return (-1); opts ^= OPT_SET(flags[i]); } ioctrl = OPT_CHECK(RBX_DUAL) ? (IO_SERIAL|IO_KEYBOARD) : OPT_CHECK(RBX_SERIAL) ? IO_SERIAL : IO_KEYBOARD; if (ioctrl & IO_SERIAL) { if (sio_init(115200 / comspeed) != 0) ioctrl &= ~IO_SERIAL; } } else { for (q = arg--; *q && *q != '('; q++); if (*q) { drv = -1; if (arg[1] == ':') { drv = *arg - '0'; if (drv > 9) return (-1); arg += 2; } if (q - arg != 2) return (-1); for (i = 0; arg[0] != dev_nm[i][0] || arg[1] != dev_nm[i][1]; i++) if (i == NDEV - 1) return (-1); dsk.type = i; arg += 3; dsk.unit = *arg - '0'; if (arg[1] != 'p' || dsk.unit > 9) return (-1); arg += 2; dsk.part = *arg - '0'; if (dsk.part < 1 || dsk.part > 9) return (-1); arg++; if (arg[0] != ')') return (-1); arg++; if (drv == -1) drv = dsk.unit; dsk.drive = (dsk.type <= TYPE_MAXHARD ? DRV_HARD : 0) + drv; *dskupdated = 1; } if ((i = ep - arg)) { if ((size_t)i >= sizeof(kname)) return (-1); memcpy(kname, arg, i + 1); } } arg = p; } return (0); } diff --git a/stand/i386/libi386/elf32_freebsd.c b/stand/i386/libi386/elf32_freebsd.c index 185dc0c08d0f..ae5702e5e65f 100644 --- a/stand/i386/libi386/elf32_freebsd.c +++ b/stand/i386/libi386/elf32_freebsd.c @@ -1,82 +1,81 @@ /*- * 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 #include #include #include #include #include #include #include #include "bootstrap.h" #include "libi386.h" #include "btxv86.h" static int elf32_exec(struct preloaded_file *amp); static int elf32_obj_exec(struct preloaded_file *amp); struct file_format i386_elf = { elf32_loadfile, elf32_exec }; struct file_format i386_elf_obj = { elf32_obj_loadfile, elf32_obj_exec }; /* * There is an ELF kernel and one or more ELF modules loaded. * We wish to start executing the kernel image, so make such * preparations as are required, and do so. */ static int elf32_exec(struct preloaded_file *fp) { struct file_metadata *md; Elf_Ehdr *ehdr; vm_offset_t entry, bootinfop, modulep, kernend; int boothowto, err, bootdev; if ((md = file_findmetadata(fp, MODINFOMD_ELFHDR)) == NULL) return(EFTYPE); ehdr = (Elf_Ehdr *)&(md->md_data); err = bi_load32(fp->f_args, &boothowto, &bootdev, &bootinfop, &modulep, &kernend); if (err != 0) return(err); entry = ehdr->e_entry & 0xffffff; #ifdef DEBUG printf("Start @ 0x%lx ...\n", entry); #endif dev_cleanup(); __exec((void *)entry, boothowto, bootdev, 0, 0, 0, bootinfop, modulep, kernend); panic("exec returned"); } static int elf32_obj_exec(struct preloaded_file *fp) { return (EFTYPE); } diff --git a/stand/i386/libi386/multiboot.c b/stand/i386/libi386/multiboot.c index 1ac5aefebf38..e11da0444fcd 100644 --- a/stand/i386/libi386/multiboot.c +++ b/stand/i386/libi386/multiboot.c @@ -1,387 +1,386 @@ /*- * Copyright (c) 2014 Roger Pau Monné * 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. */ /* * This multiboot implementation only implements a subset of the full * multiboot specification in order to be able to boot Xen and a * FreeBSD Dom0. Trying to use it to boot other multiboot compliant * kernels will most surely fail. * * The full multiboot specification can be found here: * http://www.gnu.org/software/grub/manual/multiboot/multiboot.html */ -#include #include #include #include #include #include #define _MACHINE_ELF_WANT_32BIT #include #include #include #include #include "bootstrap.h" #include "multiboot.h" #include "libi386.h" #include #define MULTIBOOT_SUPPORTED_FLAGS \ (MULTIBOOT_PAGE_ALIGN|MULTIBOOT_MEMORY_INFO) #define NUM_MODULES 2 extern int elf32_loadfile_raw(char *filename, uint64_t dest, struct preloaded_file **result, int multiboot); extern int elf64_load_modmetadata(struct preloaded_file *fp, uint64_t dest); extern int elf64_obj_loadfile(char *filename, uint64_t dest, struct preloaded_file **result); static int multiboot_loadfile(char *, uint64_t, struct preloaded_file **); static int multiboot_exec(struct preloaded_file *); static int multiboot_obj_loadfile(char *, uint64_t, struct preloaded_file **); static int multiboot_obj_exec(struct preloaded_file *fp); struct file_format multiboot = { multiboot_loadfile, multiboot_exec }; struct file_format multiboot_obj = { multiboot_obj_loadfile, multiboot_obj_exec }; extern void multiboot_tramp(); static const char mbl_name[] = "FreeBSD Loader"; static int multiboot_loadfile(char *filename, uint64_t dest, struct preloaded_file **result) { uint32_t *magic; int i, error; caddr_t header_search; ssize_t search_size; int fd; struct multiboot_header *header; char *cmdline; /* * Read MULTIBOOT_SEARCH size in order to search for the * multiboot magic header. */ if (filename == NULL) return (EFTYPE); if ((fd = open(filename, O_RDONLY)) == -1) return (errno); header_search = malloc(MULTIBOOT_SEARCH); if (header_search == NULL) { close(fd); return (ENOMEM); } search_size = read(fd, header_search, MULTIBOOT_SEARCH); magic = (uint32_t *)header_search; header = NULL; for (i = 0; i < (search_size / sizeof(uint32_t)); i++) { if (magic[i] == MULTIBOOT_HEADER_MAGIC) { header = (struct multiboot_header *)&magic[i]; break; } } if (header == NULL) { error = EFTYPE; goto out; } /* Valid multiboot header has been found, validate checksum */ if (header->magic + header->flags + header->checksum != 0) { printf( "Multiboot checksum failed, magic: 0x%x flags: 0x%x checksum: 0x%x\n", header->magic, header->flags, header->checksum); error = EFTYPE; goto out; } if ((header->flags & ~MULTIBOOT_SUPPORTED_FLAGS) != 0) { printf("Unsupported multiboot flags found: 0x%x\n", header->flags); error = EFTYPE; goto out; } error = elf32_loadfile_raw(filename, dest, result, 1); if (error != 0) { printf( "elf32_loadfile_raw failed: %d unable to load multiboot kernel\n", error); goto out; } /* * f_addr is already aligned to PAGE_SIZE, make sure * f_size it's also aligned so when the modules are loaded * they are aligned to PAGE_SIZE. */ (*result)->f_size = roundup((*result)->f_size, PAGE_SIZE); out: free(header_search); close(fd); return (error); } static int multiboot_exec(struct preloaded_file *fp) { vm_offset_t modulep, kernend, entry; struct file_metadata *md; Elf_Ehdr *ehdr; struct multiboot_info *mb_info = NULL; struct multiboot_mod_list *mb_mod = NULL; char *cmdline = NULL; size_t len; int error, mod_num; struct xen_header header; CTASSERT(sizeof(header) <= PAGE_SIZE); /* * Don't pass the memory size found by the bootloader, the memory * available to Dom0 will be lower than that. */ unsetenv("smbios.memory.enabled"); /* Allocate the multiboot struct and fill the basic details. */ mb_info = malloc(sizeof(struct multiboot_info)); if (mb_info == NULL) { error = ENOMEM; goto error; } bzero(mb_info, sizeof(struct multiboot_info)); mb_info->flags = MULTIBOOT_INFO_MEMORY|MULTIBOOT_INFO_BOOT_LOADER_NAME; mb_info->mem_lower = bios_basemem / 1024; mb_info->mem_upper = bios_extmem / 1024; mb_info->boot_loader_name = VTOP(mbl_name); /* Set the Xen command line. */ if (fp->f_args == NULL) { /* Add the Xen command line if it is set. */ cmdline = getenv("xen_cmdline"); if (cmdline != NULL) { fp->f_args = strdup(cmdline); if (fp->f_args == NULL) { error = ENOMEM; goto error; } } } if (fp->f_args != NULL) { len = strlen(fp->f_name) + 1 + strlen(fp->f_args) + 1; cmdline = malloc(len); if (cmdline == NULL) { error = ENOMEM; goto error; } snprintf(cmdline, len, "%s %s", fp->f_name, fp->f_args); mb_info->cmdline = VTOP(cmdline); mb_info->flags |= MULTIBOOT_INFO_CMDLINE; } /* Find the entry point of the Xen kernel and save it for later */ if ((md = file_findmetadata(fp, MODINFOMD_ELFHDR)) == NULL) { printf("Unable to find %s entry point\n", fp->f_name); error = EINVAL; goto error; } ehdr = (Elf_Ehdr *)&(md->md_data); entry = ehdr->e_entry & 0xffffff; /* * Prepare the multiboot module list, Xen assumes the first * module is the Dom0 kernel, and the second one is the initramfs. * This is not optimal for FreeBSD, that doesn't have a initramfs * but instead loads modules dynamically and creates the metadata * info on-the-fly. * * As expected, the first multiboot module is going to be the * FreeBSD kernel loaded as a raw file. The second module is going * to contain the metadata info and the loaded modules. * * There's a small header prefixed in the second module that contains * some information required to calculate the relocated address of * modulep based on the original offset of modulep from the start of * the module address. Note other fields might be added to this header * if required. * * Native layout: * fp->f_addr + fp->f_size * +---------+----------------+------------+ * | | | | * | Kernel | Modules | Metadata | * | | | | * +---------+----------------+------------+ * fp->f_addr modulep kernend * * Xen dom0 layout: * fp->f_addr fp->f_addr + fp->f_size * +---------+------------+----------------+------------+ * | | | | | * | Kernel | xen_header | Modules | Metadata | * | | | | | * +---------+------------+----------------+------------+ * modulep kernend * \________/\__________________________________________/ * module 0 module 1 */ fp = file_findfile(NULL, "elf kernel"); if (fp == NULL) { printf("No FreeBSD kernel provided, aborting\n"); error = EINVAL; goto error; } mb_mod = malloc(sizeof(struct multiboot_mod_list) * NUM_MODULES); if (mb_mod == NULL) { error = ENOMEM; goto error; } bzero(mb_mod, sizeof(struct multiboot_mod_list) * NUM_MODULES); error = bi_load64(fp->f_args, &modulep, &kernend, 0); if (error != 0) { printf("bi_load64 failed: %d\n", error); goto error; } mb_mod[0].mod_start = fp->f_addr; mb_mod[0].mod_end = fp->f_addr + fp->f_size - PAGE_SIZE; mb_mod[1].mod_start = mb_mod[0].mod_end; mb_mod[1].mod_end = kernend; /* Indicate the kernel metadata is prefixed with a xen_header. */ cmdline = strdup("header"); if (cmdline == NULL) { printf("Out of memory, aborting\n"); error = ENOMEM; goto error; } mb_mod[1].cmdline = VTOP(cmdline); mb_info->mods_count = NUM_MODULES; mb_info->mods_addr = VTOP(mb_mod); mb_info->flags |= MULTIBOOT_INFO_MODS; header.flags = XENHEADER_HAS_MODULEP_OFFSET; header.modulep_offset = modulep - mb_mod[1].mod_start; archsw.arch_copyin(&header, mb_mod[1].mod_start, sizeof(header)); dev_cleanup(); __exec((void *)VTOP(multiboot_tramp), (void *)entry, (void *)VTOP(mb_info)); panic("exec returned"); error: if (mb_mod) free(mb_mod); if (mb_info) free(mb_info); if (cmdline) free(cmdline); return (error); } static int multiboot_obj_loadfile(char *filename, uint64_t dest, struct preloaded_file **result) { struct preloaded_file *mfp, *kfp, *rfp; struct kernel_module *kmp; int error, mod_num; /* See if there's a multiboot kernel loaded */ mfp = file_findfile(NULL, "elf multiboot kernel"); if (mfp == NULL) return (EFTYPE); /* * We have a multiboot kernel loaded, see if there's a FreeBSD * kernel loaded also. */ kfp = file_findfile(NULL, "elf kernel"); if (kfp == NULL) { /* * No kernel loaded, this must be it. The kernel has to * be loaded as a raw file, it will be processed by * Xen and correctly loaded as an ELF file. */ rfp = file_loadraw(filename, "elf kernel", 0); if (rfp == NULL) { printf( "Unable to load %s as a multiboot payload kernel\n", filename); return (EINVAL); } /* Load kernel metadata... */ setenv("kernelname", filename, 1); error = elf64_load_modmetadata(rfp, rfp->f_addr + rfp->f_size); if (error) { printf("Unable to load kernel %s metadata error: %d\n", rfp->f_name, error); return (EINVAL); } /* * Reserve one page at the end of the kernel to place some * metadata in order to cope for Xen relocating the modules and * the metadata information. */ rfp->f_size = roundup(rfp->f_size, PAGE_SIZE); rfp->f_size += PAGE_SIZE; *result = rfp; } else { /* The rest should be loaded as regular modules */ error = elf64_obj_loadfile(filename, dest, result); if (error != 0) { printf("Unable to load %s as an object file, error: %d", filename, error); return (error); } } return (0); } static int multiboot_obj_exec(struct preloaded_file *fp) { return (EFTYPE); } diff --git a/stand/kboot/hostcons.c b/stand/kboot/hostcons.c index 24e26af2ab19..114188adcff2 100644 --- a/stand/kboot/hostcons.c +++ b/stand/kboot/hostcons.c @@ -1,100 +1,99 @@ /*- * Copyright (C) 2014 Nathan Whitehorn * 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 TOOLS GMBH 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 #include "bootstrap.h" #include "host_syscall.h" #include "termios.h" static void hostcons_probe(struct console *cp); static int hostcons_init(int arg); static void hostcons_putchar(int c); static int hostcons_getchar(void); static int hostcons_poll(void); struct console hostconsole = { "host", "Host Console", 0, hostcons_probe, hostcons_init, hostcons_putchar, hostcons_getchar, hostcons_poll, }; static struct host_termios old_settings; static void hostcons_probe(struct console *cp) { cp->c_flags |= C_PRESENTIN|C_PRESENTOUT; } static int hostcons_init(int arg) { struct host_termios new_settings; host_tcgetattr(0, &old_settings); new_settings = old_settings; host_cfmakeraw(&new_settings); host_tcsetattr(0, HOST_TCSANOW, &new_settings); return (0); } static void hostcons_putchar(int c) { uint8_t ch = c; host_write(1, &ch, 1); } static int hostcons_getchar(void) { uint8_t ch; int rv; rv = host_read(0, &ch, 1); if (rv == 1) return (ch); return (-1); } static int hostcons_poll(void) { struct host_timeval tv = {0,0}; long fds = 1 << 0; int ret; ret = host_select(32, &fds, NULL, NULL, &tv); return (ret > 0); } diff --git a/stand/kboot/hostdisk.c b/stand/kboot/hostdisk.c index a659c6e63530..552ae68daced 100644 --- a/stand/kboot/hostdisk.c +++ b/stand/kboot/hostdisk.c @@ -1,627 +1,626 @@ /*- * Copyright (C) 2014 Nathan Whitehorn * All rights reserved. * Copyright 2022 Netflix, Inc * * 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 TOOLS GMBH 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 #include #include #include #include "host_syscall.h" #include "kboot.h" #include "bootstrap.h" #ifdef LOADER_ZFS_SUPPORT #include "libzfs.h" #include #endif static int hostdisk_init(void); static int hostdisk_strategy(void *devdata, int flag, daddr_t dblk, size_t size, char *buf, size_t *rsize); static int hostdisk_open(struct open_file *f, ...); static int hostdisk_close(struct open_file *f); static int hostdisk_ioctl(struct open_file *f, u_long cmd, void *data); static int hostdisk_print(int verbose); static char *hostdisk_fmtdev(struct devdesc *vdev); static bool hostdisk_match(struct devsw *devsw, const char *devspec); static int hostdisk_parsedev(struct devdesc **idev, const char *devspec, const char **path); struct devsw hostdisk = { .dv_name = "/dev", .dv_type = DEVT_HOSTDISK, .dv_init = hostdisk_init, .dv_strategy = hostdisk_strategy, .dv_open = hostdisk_open, .dv_close = hostdisk_close, .dv_ioctl = hostdisk_ioctl, .dv_print = hostdisk_print, .dv_cleanup = nullsys, .dv_fmtdev = hostdisk_fmtdev, .dv_match = hostdisk_match, .dv_parsedev = hostdisk_parsedev, }; /* * We need to walk through the /sys/block directories looking for * block devices that we can use. */ #define SYSBLK "/sys/block" #define HOSTDISK_MIN_SIZE (16ul << 20) /* 16MB */ typedef STAILQ_HEAD(, hdinfo) hdinfo_list_t; typedef struct hdinfo { STAILQ_ENTRY(hdinfo) hd_link; /* link in device list */ hdinfo_list_t hd_children; struct hdinfo *hd_parent; const char *hd_dev; uint64_t hd_size; /* In bytes */ uint64_t hd_sectors; uint64_t hd_sectorsize; int hd_flags; #define HDF_HAS_ZPOOL 1 /* We found a zpool here and uuid valid */ uint64_t hd_zfs_uuid; } hdinfo_t; #define dev2hd(d) ((hdinfo_t *)d->d_opendata) #define hd_name(hd) ((hd->hd_dev + 5)) static hdinfo_list_t hdinfo = STAILQ_HEAD_INITIALIZER(hdinfo); typedef bool fef_cb_t(struct host_dirent64 *, void *); #define FEF_RECURSIVE 1 static bool foreach_file(const char *dir, fef_cb_t cb, void *argp, u_int flags) { char dents[2048]; int fd, dentsize; struct host_dirent64 *dent; fd = host_open(dir, O_RDONLY, 0); if (fd < 0) { printf("Can't open %s\n", dir);/* XXX */ return (false); } while (1) { dentsize = host_getdents64(fd, dents, sizeof(dents)); if (dentsize <= 0) break; for (dent = (struct host_dirent64 *)dents; (char *)dent < dents + dentsize; dent = (struct host_dirent64 *)((void *)dent + dent->d_reclen)) { if (!cb(dent, argp)) break; } } host_close(fd); return (true); } static void hostdisk_add_part(hdinfo_t *hd, const char *drv, uint64_t secs) { hdinfo_t *md; char *dev; printf("hd %s adding %s %ju\n", hd->hd_dev, drv, (uintmax_t)secs); if ((md = calloc(1, sizeof(*md))) == NULL) return; if (asprintf(&dev, "/dev/%s", drv) == -1) { printf("hostdisk: no memory\n"); free(md); return; } md->hd_dev = dev; md->hd_sectors = secs; md->hd_sectorsize = hd->hd_sectorsize; md->hd_size = md->hd_sectors * md->hd_sectorsize; md->hd_parent = hd; STAILQ_INSERT_TAIL(&hd->hd_children, md, hd_link); } static bool hostdisk_one_part(struct host_dirent64 *dent, void *argp) { hdinfo_t *hd = argp; char szfn[1024]; uint64_t sz; /* Need to skip /dev/ at start of hd_name */ if (strncmp(dent->d_name, hd_name(hd), strlen(hd_name(hd))) != 0) return (true); /* Find out how big this is -- no size not a disk */ snprintf(szfn, sizeof(szfn), "%s/%s/%s/size", SYSBLK, hd_name(hd), dent->d_name); if (!file2u64(szfn, &sz)) return true; hostdisk_add_part(hd, dent->d_name, sz); return true; } static void hostdisk_add_parts(hdinfo_t *hd) { char fn[1024]; snprintf(fn, sizeof(fn), "%s/%s", SYSBLK, hd_name(hd)); foreach_file(fn, hostdisk_one_part, hd, 0); } static void hostdisk_add_drive(const char *drv, uint64_t secs) { hdinfo_t *hd = NULL; char *dev = NULL; char fn[1024]; if ((hd = calloc(1, sizeof(*hd))) == NULL) return; if (asprintf(&dev, "/dev/%s", drv) == -1) { printf("hostdisk: no memory\n"); free(hd); return; } hd->hd_dev = dev; hd->hd_sectors = secs; snprintf(fn, sizeof(fn), "%s/%s/queue/hw_sector_size", SYSBLK, drv); if (!file2u64(fn, &hd->hd_sectorsize)) goto err; hd->hd_size = hd->hd_sectors * hd->hd_sectorsize; if (hd->hd_size < HOSTDISK_MIN_SIZE) goto err; hd->hd_flags = 0; STAILQ_INIT(&hd->hd_children); printf("/dev/%s: %ju %ju %ju\n", drv, hd->hd_size, hd->hd_sectors, hd->hd_sectorsize); STAILQ_INSERT_TAIL(&hdinfo, hd, hd_link); hostdisk_add_parts(hd); return; err: free(dev); free(hd); return; } /* Find a disk / partition by its filename */ static hdinfo_t * hostdisk_find(const char *fn) { hdinfo_t *hd, *md; STAILQ_FOREACH(hd, &hdinfo, hd_link) { if (strcmp(hd->hd_dev, fn) == 0) return (hd); STAILQ_FOREACH(md, &hd->hd_children, hd_link) { if (strcmp(md->hd_dev, fn) == 0) return (md); } } return (NULL); } static bool hostdisk_one_disk(struct host_dirent64 *dent, void *argp __unused) { char szfn[1024]; uint64_t sz; /* * Skip . and .. */ if (strcmp(dent->d_name, ".") == 0 || strcmp(dent->d_name, "..") == 0) return (true); /* Find out how big this is -- no size not a disk */ snprintf(szfn, sizeof(szfn), "%s/%s/size", SYSBLK, dent->d_name); if (!file2u64(szfn, &sz)) return (true); hostdisk_add_drive(dent->d_name, sz); return (true); } static void hostdisk_fake_one_disk(char *override) { hdinfo_t *hd = NULL; struct host_kstat sb; if (host_stat(override, &sb) != 0) return; if (!HOST_S_ISREG(sb.st_mode)) return; if (sb.st_size == 0) return; if ((hd = calloc(1, sizeof(*hd))) == NULL) return; if ((hd->hd_dev = strdup(override)) == NULL) goto err; hd->hd_size = sb.st_size; hd->hd_sectorsize = 512; /* XXX configurable? */ hd->hd_sectors = hd->hd_size / hd->hd_sectorsize; if (hd->hd_size < HOSTDISK_MIN_SIZE) goto err; hd->hd_flags = 0; STAILQ_INIT(&hd->hd_children); printf("%s: %ju %ju %ju\n", hd->hd_dev, hd->hd_size, hd->hd_sectors, hd->hd_sectorsize); STAILQ_INSERT_TAIL(&hdinfo, hd, hd_link); return; err: free(__DECONST(void *, hd->hd_dev)); free(hd); } static void hostdisk_find_block_devices(void) { char *override; override=getenv("hostdisk_override"); if (override != NULL) hostdisk_fake_one_disk(override); else foreach_file(SYSBLK, hostdisk_one_disk, NULL, 0); } static int hostdisk_init(void) { hostdisk_find_block_devices(); return (0); } static int hostdisk_strategy(void *devdata, int flag, daddr_t dblk, size_t size, char *buf, size_t *rsize) { struct devdesc *desc = devdata; daddr_t pos; int n; int64_t off; uint64_t res; uint32_t posl, posh; pos = dblk * 512; posl = pos & 0xffffffffu; posh = (pos >> 32) & 0xffffffffu; if ((off = host_llseek(desc->d_unit, posh, posl, &res, 0)) < 0) { printf("Seek error on fd %d to %ju (dblk %ju) returns %jd\n", desc->d_unit, (uintmax_t)pos, (uintmax_t)dblk, (intmax_t)off); return (EIO); } n = host_read(desc->d_unit, buf, size); if (n < 0) return (EIO); *rsize = n; return (0); } static int hostdisk_open(struct open_file *f, ...) { struct devdesc *desc; const char *fn; va_list vl; va_start(vl, f); desc = va_arg(vl, struct devdesc *); va_end(vl); fn = dev2hd(desc)->hd_dev; desc->d_unit = host_open(fn, O_RDONLY, 0); if (desc->d_unit <= 0) { printf("hostdisk_open: couldn't open %s: %d\n", fn, errno); return (ENOENT); } return (0); } static int hostdisk_close(struct open_file *f) { struct devdesc *desc = f->f_devdata; host_close(desc->d_unit); return (0); } static int hostdisk_ioctl(struct open_file *f, u_long cmd, void *data) { struct devdesc *desc = f->f_devdata; hdinfo_t *hd = dev2hd(desc); switch (cmd) { case DIOCGSECTORSIZE: *(u_int *)data = hd->hd_sectorsize; break; case DIOCGMEDIASIZE: *(uint64_t *)data = hd->hd_size; break; default: return (ENOTTY); } return (0); } static int hostdisk_print(int verbose) { char line[80]; hdinfo_t *hd, *md; int ret = 0; printf("%s devices:", hostdisk.dv_name); if (pager_output("\n") != 0) return (1); STAILQ_FOREACH(hd, &hdinfo, hd_link) { snprintf(line, sizeof(line), " %s: %ju X %ju: %ju bytes\n", hd->hd_dev, (uintmax_t)hd->hd_sectors, (uintmax_t)hd->hd_sectorsize, (uintmax_t)hd->hd_size); if ((ret = pager_output(line)) != 0) break; STAILQ_FOREACH(md, &hd->hd_children, hd_link) { snprintf(line, sizeof(line), " %s: %ju X %ju: %ju bytes\n", md->hd_dev, (uintmax_t)md->hd_sectors, (uintmax_t)md->hd_sectorsize, (uintmax_t)md->hd_size); if ((ret = pager_output(line)) != 0) goto done; } } done: return (ret); } static char * hostdisk_fmtdev(struct devdesc *vdev) { static char name[DEV_DEVLEN]; snprintf(name, sizeof(name), "%s:", dev2hd(vdev)->hd_dev); return (name); } static bool hostdisk_match(struct devsw *devsw, const char *devspec) { hdinfo_t *hd; const char *colon; char *cp; colon = strchr(devspec, ':'); if (colon == NULL) return false; cp = strdup(devspec); cp[colon - devspec] = '\0'; hd = hostdisk_find(cp); free(cp); return (hd != NULL); } static int hostdisk_parsedev(struct devdesc **idev, const char *devspec, const char **path) { const char *cp; struct devdesc *dev; hdinfo_t *hd; int len; char *fn; /* Must have a : in it */ cp = strchr(devspec, ':'); if (cp == NULL) return (EINVAL); /* XXX Stat the /dev or defer error handling to open(2) call? */ if (path != NULL) *path = cp + 1; len = cp - devspec; fn = strdup(devspec); fn[len] = '\0'; hd = hostdisk_find(fn); if (hd == NULL) { printf("Can't find hdinfo for %s\n", fn); free(fn); return (EINVAL); } free(fn); dev = malloc(sizeof(*dev)); if (dev == NULL) return (ENOMEM); dev->d_unit = 0; dev->d_dev = &hostdisk; dev->d_opendata = hd; *idev = dev; return (0); } /* XXX refactor */ static bool sanity_check_currdev(void) { struct stat st; return (stat(PATH_DEFAULTS_LOADER_CONF, &st) == 0 || #ifdef PATH_BOOTABLE_TOKEN stat(PATH_BOOTABLE_TOKEN, &st) == 0 || /* non-standard layout */ #endif stat(PATH_KERNEL, &st) == 0); } static const char * hostdisk_try_one(hdinfo_t *hd) { char *fn; if (asprintf(&fn, "%s:", hd->hd_dev) == -1) return (NULL); set_currdev(fn); printf("Trying %s\n", fn); if (sanity_check_currdev()) return (fn); printf("Failed %s\n", fn); free(fn); return (NULL); } const char * hostdisk_gen_probe(void) { hdinfo_t *hd, *md; const char *rv = NULL; STAILQ_FOREACH(hd, &hdinfo, hd_link) { /* try whole disk */ if (hd->hd_flags & HDF_HAS_ZPOOL) continue; rv = hostdisk_try_one(hd); if (rv != NULL) return (rv); /* try all partitions */ STAILQ_FOREACH(md, &hd->hd_children, hd_link) { if (md->hd_flags & HDF_HAS_ZPOOL) continue; rv = hostdisk_try_one(md); if (rv != NULL) return (rv); } } return (NULL); } #ifdef LOADER_ZFS_SUPPORT static bool hostdisk_zfs_check_one(hdinfo_t *hd) { char *fn; bool found = false; uint64_t pool_uuid; if (asprintf(&fn, "%s:", hd->hd_dev) == -1) return (false); pool_uuid = 0; zfs_probe_dev(fn, &pool_uuid, false); if (pool_uuid != 0) { found = true; hd->hd_flags |= HDF_HAS_ZPOOL; hd->hd_zfs_uuid = pool_uuid; } free(fn); return (found); } void hostdisk_zfs_probe(void) { hdinfo_t *hd, *md; STAILQ_FOREACH(hd, &hdinfo, hd_link) { if (hostdisk_zfs_check_one(hd)) continue; STAILQ_FOREACH(md, &hd->hd_children, hd_link) { hostdisk_zfs_check_one(md); } } } /* This likely shoud move to libsa/zfs/zfs.c and be used by at least EFI booting */ static bool probe_zfs_currdev(uint64_t pool_guid, uint64_t root_guid, bool setcurrdev) { char *devname; struct zfs_devdesc currdev; bool bootable; currdev.dd.d_dev = &zfs_dev; currdev.dd.d_unit = 0; currdev.pool_guid = pool_guid; currdev.root_guid = root_guid; devname = devformat(&currdev.dd); if (setcurrdev) set_currdev(devname); bootable = sanity_check_currdev(); if (bootable) { char buf[VDEV_PAD_SIZE]; if (zfs_get_bootonce(&currdev, OS_BOOTONCE, buf, sizeof(buf)) == 0) { printf("zfs bootonce: %s\n", buf); if (setcurrdev) set_currdev(buf); setenv("zfs-bootonce", buf, 1); } (void)zfs_attach_nvstore(&currdev); init_zfs_boot_options(devname); } return (bootable); } static bool hostdisk_zfs_try_default(hdinfo_t *hd) { return (probe_zfs_currdev(hd->hd_zfs_uuid, 0, true)); } bool hostdisk_zfs_find_default(void) { hdinfo_t *hd, *md; STAILQ_FOREACH(hd, &hdinfo, hd_link) { if (hd->hd_flags & HDF_HAS_ZPOOL) { if (hostdisk_zfs_try_default(hd)) return (true); continue; } STAILQ_FOREACH(md, &hd->hd_children, hd_link) { if (md->hd_flags & HDF_HAS_ZPOOL) { if (hostdisk_zfs_try_default(md)) return (true); } } } return (false); } #endif diff --git a/stand/kboot/kbootfdt.c b/stand/kboot/kbootfdt.c index 34c475f0bb3a..70844820d345 100644 --- a/stand/kboot/kbootfdt.c +++ b/stand/kboot/kbootfdt.c @@ -1,135 +1,134 @@ /*- * Copyright (C) 2014 Nathan Whitehorn * 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 TOOLS GMBH 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 #include #include #include "bootstrap.h" #include "host_syscall.h" #include "kboot.h" static void add_node_to_fdt(void *buffer, const char *path, int fdt_offset) { int child_offset, fd, pfd, error, dentsize; char subpath[512]; void *propbuf; ssize_t proplen; char dents[2048]; struct host_dirent64 *dent; int d_type; fd = host_open(path, O_RDONLY, 0); while (1) { dentsize = host_getdents64(fd, dents, sizeof(dents)); if (dentsize <= 0) break; for (dent = (struct host_dirent64 *)dents; (char *)dent < dents + dentsize; dent = (struct host_dirent64 *)((void *)dent + dent->d_reclen)) { sprintf(subpath, "%s/%s", path, dent->d_name); if (strcmp(dent->d_name, ".") == 0 || strcmp(dent->d_name, "..") == 0) continue; d_type = dent->d_type; if (d_type == HOST_DT_DIR) { child_offset = fdt_add_subnode(buffer, fdt_offset, dent->d_name); if (child_offset < 0) { printf("Error %d adding node %s/%s, skipping\n", child_offset, path, dent->d_name); continue; } add_node_to_fdt(buffer, subpath, child_offset); } else { propbuf = malloc(1024); proplen = 0; pfd = host_open(subpath, O_RDONLY, 0); if (pfd > 0) { proplen = host_read(pfd, propbuf, 1024); host_close(pfd); } error = fdt_setprop(buffer, fdt_offset, dent->d_name, propbuf, proplen); free(propbuf); if (error) printf("Error %d adding property %s to " "node %d\n", error, dent->d_name, fdt_offset); } } } host_close(fd); } int fdt_platform_load_dtb(void) { void *buffer; size_t buflen = 409600; int fd; /* * Should load /sys/firmware/fdt if it exists, otherwise we walk the * tree from /proc/device-tree. The former is much easier than the * latter and also the newer interface. But as long as we support the * PS3 boot, we'll need the latter due to that kernel's age. It likely * would be better to script the decision between the two, but that * turns out to be tricky... */ buffer = malloc(buflen); fd = host_open("/sys/firmware/fdt", O_RDONLY, 0); if (fd != -1) { buflen = host_read(fd, buffer, buflen); close(fd); } else { fdt_create_empty_tree(buffer, buflen); add_node_to_fdt(buffer, "/proc/device-tree", fdt_path_offset(buffer, "/")); } fdt_arch_fixups(buffer); fdt_pack(buffer); fdt_load_dtb_addr(buffer); free(buffer); return (0); } void fdt_platform_load_overlays(void) { fdt_load_dtb_overlays(NULL); } void fdt_platform_fixups(void) { fdt_apply_overlays(); } diff --git a/stand/liblua/lutils.c b/stand/liblua/lutils.c index d6c9051f9039..1792d0c8c620 100644 --- a/stand/liblua/lutils.c +++ b/stand/liblua/lutils.c @@ -1,606 +1,605 @@ /*- * Copyright (c) 2014 Pedro Souza * 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 #include "lua.h" #include "lauxlib.h" #include "lstd.h" #include "lutils.h" #include "bootstrap.h" #include #include /* * Like loader.perform, except args are passed already parsed * on the stack. */ static int lua_command(lua_State *L) { int i; int res = 1; int argc = lua_gettop(L); char **argv; argv = malloc(sizeof(char *) * (argc + 1)); if (argv == NULL) return 0; for (i = 0; i < argc; i++) argv[i] = (char *)(intptr_t)luaL_checkstring(L, i + 1); argv[argc] = NULL; res = interp_builtin_cmd(argc, argv); free(argv); lua_pushinteger(L, res); return 1; } static int lua_has_command(lua_State *L) { const char *cmd; if (lua_gettop(L) != 1) { lua_pushnil(L); return 1; } cmd = luaL_checkstring(L, 1); lua_pushinteger(L, interp_has_builtin_cmd(cmd)); return 1; } static int lua_perform(lua_State *L) { int argc; char **argv; int res = 1; if (parse(&argc, &argv, luaL_checkstring(L, 1)) == 0) { res = interp_builtin_cmd(argc, argv); free(argv); } lua_pushinteger(L, res); return 1; } static int lua_command_error(lua_State *L) { lua_pushstring(L, command_errbuf); return 1; } /* * Accepts a space-delimited loader command and runs it through the standard * loader parsing, as if it were executed at the loader prompt by the user. */ static int lua_interpret(lua_State *L) { const char *interp_string; if (lua_gettop(L) != 1) { lua_pushnil(L); return 1; } interp_string = luaL_checkstring(L, 1); lua_pushinteger(L, interp_run(interp_string)); return 1; } static int lua_parse(lua_State *L) { int argc, nargc; char **argv; if (parse(&argc, &argv, luaL_checkstring(L, 1)) == 0) { for (nargc = 0; nargc < argc; ++nargc) { lua_pushstring(L, argv[nargc]); } free(argv); return nargc; } lua_pushnil(L); return 1; } static int lua_getchar(lua_State *L) { lua_pushinteger(L, getchar()); return 1; } static int lua_ischar(lua_State *L) { lua_pushboolean(L, ischar()); return 1; } static int lua_gets(lua_State *L) { char buf[129]; ngets(buf, 128); lua_pushstring(L, buf); return 1; } static int lua_time(lua_State *L) { lua_pushinteger(L, time(NULL)); return 1; } static int lua_delay(lua_State *L) { delay((int)luaL_checknumber(L, 1)); return 0; } static int lua_getenv(lua_State *L) { lua_pushstring(L, getenv(luaL_checkstring(L, 1))); return 1; } static int lua_setenv(lua_State *L) { const char *key, *val; key = luaL_checkstring(L, 1); val = luaL_checkstring(L, 2); lua_pushinteger(L, setenv(key, val, 1)); return 1; } static int lua_unsetenv(lua_State *L) { const char *ev; ev = luaL_checkstring(L, 1); lua_pushinteger(L, unsetenv(ev)); return 1; } static int lua_printc(lua_State *L) { ssize_t cur, l; const char *s = luaL_checklstring(L, 1, &l); for (cur = 0; cur < l; ++cur) putchar((unsigned char)*(s++)); return 1; } static int lua_openfile(lua_State *L) { const char *mode, *str; int nargs; nargs = lua_gettop(L); if (nargs < 1 || nargs > 2) { lua_pushnil(L); return 1; } str = lua_tostring(L, 1); mode = "r"; if (nargs > 1) { mode = lua_tostring(L, 2); if (mode == NULL) { lua_pushnil(L); return 1; } } FILE * f = fopen(str, mode); if (f != NULL) { FILE ** ptr = (FILE**)lua_newuserdata(L, sizeof(FILE**)); *ptr = f; } else lua_pushnil(L); return 1; } static int lua_closefile(lua_State *L) { FILE ** f; if (lua_gettop(L) != 1) { lua_pushboolean(L, 0); return 1; } f = (FILE**)lua_touserdata(L, 1); if (f != NULL && *f != NULL) { lua_pushboolean(L, fclose(*f) == 0 ? 1 : 0); *f = NULL; } else lua_pushboolean(L, 0); return 1; } static int lua_readfile(lua_State *L) { FILE **f; size_t size, r; char * buf; if (lua_gettop(L) < 1 || lua_gettop(L) > 2) { lua_pushnil(L); lua_pushinteger(L, 0); return 2; } f = (FILE**)lua_touserdata(L, 1); if (f == NULL || *f == NULL) { lua_pushnil(L); lua_pushinteger(L, 0); return 2; } if (lua_gettop(L) == 2) size = (size_t)lua_tonumber(L, 2); else size = (*f)->size; buf = (char*)malloc(size); r = fread(buf, 1, size, *f); lua_pushlstring(L, buf, r); free(buf); lua_pushinteger(L, r); return 2; } /* * Implements io.write(file, ...) * Any number of string and number arguments may be passed to it, * and it will return the number of bytes written, or nil, an error string, and * the errno. */ static int lua_writefile(lua_State *L) { FILE **f; const char *buf; int i, nargs; size_t bufsz, w, wrsz; buf = NULL; bufsz = 0; w = 0; wrsz = 0; nargs = lua_gettop(L); if (nargs < 2) { errno = EINVAL; return luaL_fileresult(L, 0, NULL); } f = (FILE**)lua_touserdata(L, 1); if (f == NULL || *f == NULL) { errno = EINVAL; return luaL_fileresult(L, 0, NULL); } /* Do a validation pass first */ for (i = 0; i < nargs - 1; i++) { /* * With Lua's API, lua_isstring really checks if the argument * is a string or a number. The latter will be implicitly * converted to a string by our later call to lua_tolstring. */ if (!lua_isstring(L, i + 2)) { errno = EINVAL; return luaL_fileresult(L, 0, NULL); } } for (i = 0; i < nargs - 1; i++) { /* We've already validated; there's no chance of failure */ buf = lua_tolstring(L, i + 2, &bufsz); wrsz = fwrite(buf, 1, bufsz, *f); if (wrsz < bufsz) return luaL_fileresult(L, 0, NULL); w += wrsz; } lua_pushinteger(L, w); return 1; } /* * put image using terminal coordinates. */ static int lua_term_putimage(lua_State *L) { const char *name; png_t png; uint32_t x1, y1, x2, y2, f; int nargs, ret = 0, error; nargs = lua_gettop(L); if (nargs != 6) { lua_pushboolean(L, 0); return 1; } name = luaL_checkstring(L, 1); x1 = luaL_checknumber(L, 2); y1 = luaL_checknumber(L, 3); x2 = luaL_checknumber(L, 4); y2 = luaL_checknumber(L, 5); f = luaL_checknumber(L, 6); x1 = gfx_state.tg_origin.tp_col + x1 * gfx_state.tg_font.vf_width; y1 = gfx_state.tg_origin.tp_row + y1 * gfx_state.tg_font.vf_height; if (x2 != 0) { x2 = gfx_state.tg_origin.tp_col + x2 * gfx_state.tg_font.vf_width; } if (y2 != 0) { y2 = gfx_state.tg_origin.tp_row + y2 * gfx_state.tg_font.vf_height; } if ((error = png_open(&png, name)) != PNG_NO_ERROR) { if (f & FL_PUTIMAGE_DEBUG) printf("%s\n", png_error_string(error)); } else { if (gfx_fb_putimage(&png, x1, y1, x2, y2, f) == 0) ret = 1; (void) png_close(&png); } lua_pushboolean(L, ret); return 1; } static int lua_fb_putimage(lua_State *L) { const char *name; png_t png; uint32_t x1, y1, x2, y2, f; int nargs, ret = 0, error; nargs = lua_gettop(L); if (nargs != 6) { lua_pushboolean(L, 0); return 1; } name = luaL_checkstring(L, 1); x1 = luaL_checknumber(L, 2); y1 = luaL_checknumber(L, 3); x2 = luaL_checknumber(L, 4); y2 = luaL_checknumber(L, 5); f = luaL_checknumber(L, 6); if ((error = png_open(&png, name)) != PNG_NO_ERROR) { if (f & FL_PUTIMAGE_DEBUG) printf("%s\n", png_error_string(error)); } else { if (gfx_fb_putimage(&png, x1, y1, x2, y2, f) == 0) ret = 1; (void) png_close(&png); } lua_pushboolean(L, ret); return 1; } static int lua_fb_setpixel(lua_State *L) { uint32_t x, y; int nargs; nargs = lua_gettop(L); if (nargs != 2) { lua_pushnil(L); return 1; } x = luaL_checknumber(L, 1); y = luaL_checknumber(L, 2); gfx_fb_setpixel(x, y); return 0; } static int lua_fb_line(lua_State *L) { uint32_t x0, y0, x1, y1, wd; int nargs; nargs = lua_gettop(L); if (nargs != 5) { lua_pushnil(L); return 1; } x0 = luaL_checknumber(L, 1); y0 = luaL_checknumber(L, 2); x1 = luaL_checknumber(L, 3); y1 = luaL_checknumber(L, 4); wd = luaL_checknumber(L, 5); gfx_fb_line(x0, y0, x1, y1, wd); return 0; } static int lua_fb_bezier(lua_State *L) { uint32_t x0, y0, x1, y1, x2, y2, width; int nargs; nargs = lua_gettop(L); if (nargs != 7) { lua_pushnil(L); return 1; } x0 = luaL_checknumber(L, 1); y0 = luaL_checknumber(L, 2); x1 = luaL_checknumber(L, 3); y1 = luaL_checknumber(L, 4); x2 = luaL_checknumber(L, 5); y2 = luaL_checknumber(L, 6); width = luaL_checknumber(L, 7); gfx_fb_bezier(x0, y0, x1, y1, x2, y2, width); return 0; } static int lua_fb_drawrect(lua_State *L) { uint32_t x0, y0, x1, y1, fill; int nargs; nargs = lua_gettop(L); if (nargs != 5) { lua_pushnil(L); return 1; } x0 = luaL_checknumber(L, 1); y0 = luaL_checknumber(L, 2); x1 = luaL_checknumber(L, 3); y1 = luaL_checknumber(L, 4); fill = luaL_checknumber(L, 5); gfx_fb_drawrect(x0, y0, x1, y1, fill); return 0; } static int lua_term_drawrect(lua_State *L) { uint32_t x0, y0, x1, y1; int nargs; nargs = lua_gettop(L); if (nargs != 4) { lua_pushnil(L); return 1; } x0 = luaL_checknumber(L, 1); y0 = luaL_checknumber(L, 2); x1 = luaL_checknumber(L, 3); y1 = luaL_checknumber(L, 4); gfx_term_drawrect(x0, y0, x1, y1); return 0; } #define REG_SIMPLE(n) { #n, lua_ ## n } static const struct luaL_Reg loaderlib[] = { REG_SIMPLE(delay), REG_SIMPLE(command_error), REG_SIMPLE(command), REG_SIMPLE(interpret), REG_SIMPLE(parse), REG_SIMPLE(getenv), REG_SIMPLE(has_command), REG_SIMPLE(perform), REG_SIMPLE(printc), /* Also registered as the global 'printc' */ REG_SIMPLE(setenv), REG_SIMPLE(time), REG_SIMPLE(unsetenv), REG_SIMPLE(fb_bezier), REG_SIMPLE(fb_drawrect), REG_SIMPLE(fb_line), REG_SIMPLE(fb_putimage), REG_SIMPLE(fb_setpixel), REG_SIMPLE(term_drawrect), REG_SIMPLE(term_putimage), { NULL, NULL }, }; static const struct luaL_Reg iolib[] = { { "close", lua_closefile }, REG_SIMPLE(getchar), REG_SIMPLE(gets), REG_SIMPLE(ischar), { "open", lua_openfile }, { "read", lua_readfile }, { "write", lua_writefile }, { NULL, NULL }, }; #undef REG_SIMPLE int luaopen_loader(lua_State *L) { luaL_newlib(L, loaderlib); /* Add loader.machine and loader.machine_arch properties */ lua_pushstring(L, MACHINE); lua_setfield(L, -2, "machine"); lua_pushstring(L, MACHINE_ARCH); lua_setfield(L, -2, "machine_arch"); lua_pushstring(L, LUA_PATH); lua_setfield(L, -2, "lua_path"); lua_pushinteger(L, bootprog_rev); lua_setfield(L, -2, "version"); /* Set global printc to loader.printc */ lua_register(L, "printc", lua_printc); return 1; } int luaopen_io(lua_State *L) { luaL_newlib(L, iolib); return 1; } diff --git a/stand/libofw/ofw_console.c b/stand/libofw/ofw_console.c index 76942358e5c5..42ff045e8831 100644 --- a/stand/libofw/ofw_console.c +++ b/stand/libofw/ofw_console.c @@ -1,122 +1,121 @@ /* $NetBSD: prom.c,v 1.3 1997/09/06 14:03:58 drochner Exp $ */ /*- * Mach Operating System * Copyright (c) 1992 Carnegie Mellon University * All Rights Reserved. * * Permission to use, copy, modify and distribute this software and its * documentation is hereby granted, provided that both the copyright * notice and this permission notice appear in all copies of the * software, derivative works or modified versions, and any portions * thereof, and that both notices appear in supporting documentation. * * CARNEGIE MELLON ALLOWS FREE USE OF THIS SOFTWARE IN ITS "AS IS" * CONDITION. CARNEGIE MELLON DISCLAIMS ANY LIABILITY OF ANY KIND FOR * ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS SOFTWARE. * * Carnegie Mellon requests users of this software to return to * * Software Distribution Coordinator or Software.Distribution@CS.CMU.EDU * School of Computer Science * Carnegie Mellon University * Pittsburgh PA 15213-3890 * * any improvements or extensions that they make and grant Carnegie Mellon * the rights to redistribute these changes. */ -#include #include #include "bootstrap.h" #include "openfirm.h" static void ofw_cons_probe(struct console *cp); static int ofw_cons_init(int); void ofw_cons_putchar(int); int ofw_cons_getchar(void); int ofw_cons_poll(void); static ihandle_t stdin; static ihandle_t stdout; struct console ofwconsole = { "ofw", "Open Firmware console", 0, ofw_cons_probe, ofw_cons_init, ofw_cons_putchar, ofw_cons_getchar, ofw_cons_poll, }; static void ofw_cons_probe(struct console *cp) { OF_getprop(chosen, "stdin", &stdin, sizeof(stdin)); OF_getprop(chosen, "stdout", &stdout, sizeof(stdout)); cp->c_flags |= C_PRESENTIN|C_PRESENTOUT; } static int ofw_cons_init(int arg) { return 0; } void ofw_cons_putchar(int c) { char cbuf; if (c == '\n') { cbuf = '\r'; OF_write(stdout, &cbuf, 1); } cbuf = c; OF_write(stdout, &cbuf, 1); } static int saved_char = -1; int ofw_cons_getchar() { unsigned char ch = '\0'; int l; if (saved_char != -1) { l = saved_char; saved_char = -1; return l; } /* At least since version 4.0.0, QEMU became bug-compatible * with PowerVM's vty, by inserting a \0 after every \r. * As this confuses loader's interpreter and as a \0 coming * from the console doesn't seem reasonable, it's filtered here. */ if (OF_read(stdin, &ch, 1) > 0 && ch != '\0') return (ch); return (-1); } int ofw_cons_poll() { unsigned char ch; if (saved_char != -1) return 1; if (OF_read(stdin, &ch, 1) > 0) { saved_char = ch; return 1; } return 0; } diff --git a/stand/libofw/ofw_memory.c b/stand/libofw/ofw_memory.c index cd95827cba9a..67a4527e07b4 100644 --- a/stand/libofw/ofw_memory.c +++ b/stand/libofw/ofw_memory.c @@ -1,112 +1,111 @@ /*- * Copyright (c) 2001 Benno Rice * 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 Benno Rice ``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 #include #include #include #include "libofw.h" #include "openfirm.h" struct ofw_mapping { vm_offset_t va; int len; vm_offset_t pa; int mode; }; struct ofw_mapping2 { vm_offset_t va; int len; vm_offset_t pa_hi; vm_offset_t pa_lo; int mode; }; void ofw_memmap(int acells) { struct ofw_mapping *mapptr; struct ofw_mapping2 *mapptr2; phandle_t mmup; int nmapping, i; u_char mappings[256 * sizeof(struct ofw_mapping2)]; char lbuf[80]; mmup = OF_instance_to_package(mmu); bzero(mappings, sizeof(mappings)); nmapping = OF_getprop(mmup, "translations", mappings, sizeof(mappings)); if (nmapping == -1) { printf("Could not get memory map (%d)\n", nmapping); return; } pager_open(); if (acells == 1) { nmapping /= sizeof(struct ofw_mapping); mapptr = (struct ofw_mapping *) mappings; printf("%17s\t%17s\t%8s\t%6s\n", "Virtual Range", "Physical Range", "#Pages", "Mode"); for (i = 0; i < nmapping; i++) { sprintf(lbuf, "%08jx-%08jx\t%08jx-%08jx\t%8d\t%6x\n", (uintmax_t)mapptr[i].va, (uintmax_t)mapptr[i].va + mapptr[i].len, (uintmax_t)mapptr[i].pa, (uintmax_t)mapptr[i].pa + mapptr[i].len, mapptr[i].len / 0x1000, mapptr[i].mode); if (pager_output(lbuf)) break; } } else { nmapping /= sizeof(struct ofw_mapping2); mapptr2 = (struct ofw_mapping2 *) mappings; printf("%17s\t%17s\t%8s\t%6s\n", "Virtual Range", "Physical Range", "#Pages", "Mode"); for (i = 0; i < nmapping; i++) { sprintf(lbuf, "%08jx-%08jx\t%08jx-%08jx\t%8d\t%6x\n", (uintmax_t)mapptr2[i].va, (uintmax_t)mapptr2[i].va + mapptr2[i].len, (uintmax_t)mapptr2[i].pa_lo, (uintmax_t)mapptr2[i].pa_lo + mapptr2[i].len, mapptr2[i].len / 0x1000, mapptr2[i].mode); if (pager_output(lbuf)) break; } } pager_close(); } diff --git a/stand/libofw/ofw_net.c b/stand/libofw/ofw_net.c index 15bac848a340..b4bcb7a00fe2 100644 --- a/stand/libofw/ofw_net.c +++ b/stand/libofw/ofw_net.c @@ -1,326 +1,325 @@ /*- * Copyright (c) 2000-2001 Benno Rice * 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 Benno Rice ``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 #include #include #include #include #include #include #include #include #include #include #include #include "libofw.h" #include "openfirm.h" static int ofwn_probe(struct netif *, void *); static int ofwn_match(struct netif *, void *); static void ofwn_init(struct iodesc *, void *); static ssize_t ofwn_get(struct iodesc *, void **, time_t); static ssize_t ofwn_put(struct iodesc *, void *, size_t); static void ofwn_end(struct netif *); extern struct netif_stats ofwn_stats[]; struct netif_dif ofwn_ifs[] = { { .dif_unit=0, .dif_nsel=1, .dif_stats=&ofwn_stats[0], .dif_private=0, }, }; struct netif_stats ofwn_stats[nitems(ofwn_ifs)]; struct netif_driver ofwnet = { .netif_bname="net", .netif_match=ofwn_match, .netif_probe=ofwn_probe, .netif_init=ofwn_init, .netif_get=ofwn_get, .netif_put=ofwn_put, .netif_end=ofwn_end, .netif_ifs=ofwn_ifs, .netif_nifs=nitems(ofwn_ifs) }; static ihandle_t netinstance; static void *dmabuf; static int ofwn_match(struct netif *nif, void *machdep_hint) { return 1; } static int ofwn_probe(struct netif *nif, void *machdep_hint) { return 0; } static ssize_t ofwn_put(struct iodesc *desc, void *pkt, size_t len) { size_t sendlen; ssize_t rv; #if defined(NETIF_DEBUG) struct ether_header *eh; printf("netif_put: desc=0x%x pkt=0x%x len=%d\n", desc, pkt, len); eh = pkt; printf("dst: %s ", ether_sprintf(eh->ether_dhost)); printf("src: %s ", ether_sprintf(eh->ether_shost)); printf("type: 0x%x\n", eh->ether_type & 0xffff); #endif sendlen = len; if (sendlen < 60) { sendlen = 60; #if defined(NETIF_DEBUG) printf("netif_put: length padded to %d\n", sendlen); #endif } if (dmabuf) { bcopy(pkt, dmabuf, sendlen); pkt = dmabuf; } rv = OF_write(netinstance, pkt, len); #if defined(NETIF_DEBUG) printf("netif_put: OF_write returned %d\n", rv); #endif return rv; } static ssize_t ofwn_get(struct iodesc *desc, void **pkt, time_t timeout) { time_t t; ssize_t length; size_t len; char *buf, *ptr; #if defined(NETIF_DEBUG) printf("netif_get: pkt=%p, timeout=%d\n", pkt, timeout); #endif /* * We should read the "max-frame-size" int property instead, * but at this time the iodesc does not have mtu, so we will take * a small shortcut here. */ len = ETHER_MAX_LEN; buf = malloc(len + ETHER_ALIGN); if (buf == NULL) return (-1); ptr = buf + ETHER_ALIGN; t = getsecs(); do { length = OF_read(netinstance, ptr, len); } while ((length == -2 || length == 0) && (getsecs() - t < timeout)); #if defined(NETIF_DEBUG) printf("netif_get: received length=%d (%x)\n", length, length); #endif if (length < 12) { free(buf); return (-1); } #if defined(NETIF_VERBOSE_DEBUG) { char *ch = ptr; int i; for(i = 0; i < 96; i += 4) { printf("%02x%02x%02x%02x ", ch[i], ch[i+1], ch[i+2], ch[i+3]); } printf("\n"); } #endif #if defined(NETIF_DEBUG) { struct ether_header *eh = ptr; printf("dst: %s ", ether_sprintf(eh->ether_dhost)); printf("src: %s ", ether_sprintf(eh->ether_shost)); printf("type: 0x%x\n", eh->ether_type & 0xffff); } #endif *pkt = buf; return (length); } static void ofwn_init(struct iodesc *desc, void *machdep_hint) { phandle_t netdev; char path[64]; char *ch; int pathlen; pathlen = OF_getprop(chosen, "bootpath", path, 64); if ((ch = strchr(path, ':')) != NULL) *ch = '\0'; netdev = OF_finddevice(path); if (OF_getprop(netdev, "local-mac-address", desc->myea, 6) == -1) goto punt; printf("boot: ethernet address: %s\n", ether_sprintf(desc->myea)); if ((netinstance = OF_open(path)) == -1) { printf("Could not open network device.\n"); goto punt; } #if defined(NETIF_DEBUG) printf("ofwn_init: Open Firmware instance handle: %08x\n", netinstance); #endif dmabuf = NULL; if (OF_call_method("dma-alloc", netinstance, 1, 1, (64 * 1024), &dmabuf) < 0) { printf("Failed to allocate DMA buffer (got %p).\n", dmabuf); goto punt; } #if defined(NETIF_DEBUG) printf("ofwn_init: allocated DMA buffer: %p\n", dmabuf); #endif return; punt: printf("\n"); printf("Could not boot from %s.\n", path); OF_enter(); } static void ofwn_end(struct netif *nif) { #ifdef BROKEN /* dma-free freezes at least some Apple ethernet controllers */ OF_call_method("dma-free", netinstance, 2, 0, dmabuf, MAXPHYS); #endif OF_close(netinstance); } #if 0 int ofwn_getunit(const char *path) { int i; char newpath[255]; OF_canon(path, newpath, 254); for (i = 0; i < nofwninfo; i++) { printf(">>> test =\t%s\n", ofwninfo[i].ofwn_path); if (strcmp(path, ofwninfo[i].ofwn_path) == 0) return i; if (strcmp(newpath, ofwninfo[i].ofwn_path) == 0) return i; } return -1; } #endif /* * To properly match network devices, we have to subclass the netdev device. * It has a different devdesc than a normal network device (which is fine: * it's a struct superset) and different matching criteria (since it has to * look at the path, find a handle and see if that handle is a network node * or not). */ static int ofwnd_init(void); static int ofwnd_parsedev(struct devdesc **, const char *, const char **); static bool ofwnd_match(struct devsw *, const char *); static char *ofwnd_fmtdev(struct devdesc *); struct devsw ofw_netdev = { .dv_name = "network", .dv_type = DEVT_NET, .dv_init = ofwnd_init, .dv_match = ofwnd_match, .dv_fmtdev = ofwnd_fmtdev, .dv_parsedev = ofwnd_parsedev, }; static int ofwnd_init(void) { netdev.dv_init(); ofw_netdev.dv_strategy = netdev.dv_strategy; ofw_netdev.dv_open = netdev.dv_open; ofw_netdev.dv_close = netdev.dv_close; ofw_netdev.dv_ioctl = netdev.dv_ioctl; ofw_netdev.dv_print = netdev.dv_print; ofw_netdev.dv_fmtdev = netdev.dv_fmtdev; /* parsedev is unique to ofwnd */ /* match is unique to ofwnd */ return (0); } static int ofwnd_parsedev(struct devdesc **dev, const char *devspec, const char **path) { return (ofw_common_parsedev(dev, devspec, path, ofw_netdev.dv_name)); } static bool ofwnd_match(struct devsw *devsw, const char *devspec) { const char *path; return (ofw_path_to_handle(devspec, devsw->dv_name, &path) != -1); } static char * ofwnd_fmtdev(struct devdesc *idev) { struct ofw_devdesc *dev = (struct ofw_devdesc *)idev; return (dev->d_path); } diff --git a/stand/libofw/openfirm.h b/stand/libofw/openfirm.h index f41fc20cfa7d..35d10c320b57 100644 --- a/stand/libofw/openfirm.h +++ b/stand/libofw/openfirm.h @@ -1,123 +1,122 @@ /* $NetBSD: openfirm.h,v 1.1 1998/05/15 10:16:00 tsubai Exp $ */ /*- * Copyright (C) 1995, 1996 Wolfgang Solfrank. * Copyright (C) 1995, 1996 TooLs GmbH. * 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 TooLs GmbH. * 4. The name of TooLs GmbH may not be used to endorse or promote products * derived from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY TOOLS GMBH ``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 TOOLS GMBH 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. */ /*- * Copyright (C) 2000 Benno Rice. * 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 Benno Rice ``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 TOOLS GMBH 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. */ #ifndef _OPENFIRM_H_ #define _OPENFIRM_H_ /* * Prototypes for Open Firmware Interface Routines */ -#include #include typedef uint32_t ihandle_t; typedef uint32_t phandle_t; typedef uint32_t cell_t; extern int (*openfirmware)(void *); extern phandle_t chosen; extern ihandle_t memory, mmu; extern int real_mode; /* * This isn't actually an Open Firmware function, but it seemed like the right * place for it to go. */ void OF_init(int (*openfirm)(void *)); /* Generic functions */ int OF_test(char *); void OF_quiesce(); /* Disable firmware */ /* Device tree functions */ phandle_t OF_peer(phandle_t); phandle_t OF_child(phandle_t); phandle_t OF_parent(phandle_t); phandle_t OF_instance_to_package(ihandle_t); int OF_getproplen(phandle_t, const char *); int OF_getprop(phandle_t, const char *, void *, int); int OF_getencprop(phandle_t, const char *, cell_t *, int); int OF_nextprop(phandle_t, const char *, char *); int OF_setprop(phandle_t, const char *, void *, int); int OF_canon(const char *, char *, int); phandle_t OF_finddevice(const char *); int OF_instance_to_path(ihandle_t, char *, int); int OF_package_to_path(phandle_t, char *, int); int OF_call_method(char *, ihandle_t, int, int, ...); /* Device I/O functions */ ihandle_t OF_open(char *); void OF_close(ihandle_t); int OF_read(ihandle_t, void *, int); int OF_write(ihandle_t, void *, int); int OF_seek(ihandle_t, u_quad_t); unsigned int OF_blocks(ihandle_t); int OF_block_size(ihandle_t); /* Memory functions */ void *OF_claim(void *, u_int, u_int); void OF_release(void *, u_int); /* Control transfer functions */ void OF_boot(char *); void OF_enter(void); void OF_exit(void) __attribute__((noreturn)); void OF_chain(void *, u_int, void (*)(), void *, u_int); /* Time function */ int OF_milliseconds(void); #endif /* _OPENFIRM_H_ */ diff --git a/stand/libsa/__main.c b/stand/libsa/__main.c index f65f3a4105b4..2c057104476c 100644 --- a/stand/libsa/__main.c +++ b/stand/libsa/__main.c @@ -1,41 +1,40 @@ /* $NetBSD: __main.c,v 1.4 1996/03/14 18:52:03 christos Exp $ */ /* * Copyright (c) 1993 Christopher G. Demetriou * 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 Christopher G. Demetriou. * 4. The name of the author may not be used to endorse or promote products * derived from this software without specific prior written permission * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -#include #include void __main(void); void __main(void) { } diff --git a/stand/libsa/arp.c b/stand/libsa/arp.c index e461d9322e7e..eff8b9f332a3 100644 --- a/stand/libsa/arp.c +++ b/stand/libsa/arp.c @@ -1,301 +1,300 @@ /* $NetBSD: arp.c,v 1.18 1997/07/07 15:52:49 drochner Exp $ */ /* * Copyright (c) 1992 Regents of the University of California. * All rights reserved. * * This software was developed by the Computer Systems Engineering group * at Lawrence Berkeley Laboratory under DARPA contract BG 91-66 and * contributed to Berkeley. * * 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. */ -#include #include #include #include #include #include #include #include #include "stand.h" #include "net.h" /* Cache stuff */ #define ARP_NUM 8 /* need at most 3 arp entries */ struct arp_list { struct in_addr addr; u_char ea[6]; } arp_list[ARP_NUM] = { /* XXX - net order `INADDR_BROADCAST' must be a constant */ { {0xffffffff}, BA } }; int arp_num = 1; /* Local forwards */ static ssize_t arpsend(struct iodesc *, void *, size_t); static ssize_t arprecv(struct iodesc *, void **, void **, time_t, void *); /* Broadcast an ARP packet, asking who has addr on interface d */ u_char * arpwhohas(struct iodesc *d, struct in_addr addr) { int i; struct ether_arp *ah; struct arp_list *al; void *pkt; struct { struct ether_header eh; struct { struct ether_arp arp; u_char pad[18]; /* 60 - sizeof(...) */ } data; } wbuf; /* Try for cached answer first */ for (i = 0, al = arp_list; i < arp_num; ++i, ++al) if (addr.s_addr == al->addr.s_addr) return (al->ea); /* Don't overflow cache */ if (arp_num > ARP_NUM - 1) { arp_num = 1; /* recycle */ printf("arpwhohas: overflowed arp_list!\n"); } #ifdef ARP_DEBUG if (debug) printf("arpwhohas: send request for %s\n", inet_ntoa(addr)); #endif bzero((char*)&wbuf.data, sizeof(wbuf.data)); ah = &wbuf.data.arp; ah->arp_hrd = htons(ARPHRD_ETHER); ah->arp_pro = htons(ETHERTYPE_IP); ah->arp_hln = sizeof(ah->arp_sha); /* hardware address length */ ah->arp_pln = sizeof(ah->arp_spa); /* protocol address length */ ah->arp_op = htons(ARPOP_REQUEST); MACPY(d->myea, ah->arp_sha); bcopy(&d->myip, ah->arp_spa, sizeof(ah->arp_spa)); /* Leave zeros in arp_tha */ bcopy(&addr, ah->arp_tpa, sizeof(ah->arp_tpa)); /* Store ip address in cache (incomplete entry). */ al->addr = addr; pkt = NULL; ah = NULL; i = sendrecv(d, arpsend, &wbuf.data, sizeof(wbuf.data), arprecv, &pkt, (void **)&ah, NULL); if (i == -1) { panic("arp: no response for %s", inet_ntoa(addr)); } /* Store ethernet address in cache */ #ifdef ARP_DEBUG if (debug) { struct ether_header *eh; eh = (struct ether_header *)((uintptr_t)pkt + ETHER_ALIGN); printf("arp: response from %s\n", ether_sprintf(eh->ether_shost)); printf("arp: cacheing %s --> %s\n", inet_ntoa(addr), ether_sprintf(ah->arp_sha)); } #endif MACPY(ah->arp_sha, al->ea); ++arp_num; free(pkt); return (al->ea); } static ssize_t arpsend(struct iodesc *d, void *pkt, size_t len) { #ifdef ARP_DEBUG if (debug) printf("arpsend: called\n"); #endif return (sendether(d, pkt, len, bcea, ETHERTYPE_ARP)); } /* * Returns 0 if this is the packet we're waiting for * else -1 (and errno == 0) */ static ssize_t arprecv(struct iodesc *d, void **pkt, void **payload, time_t tleft, void *extra) { ssize_t n; struct ether_arp *ah; uint16_t etype; /* host order */ void *ptr; #ifdef ARP_DEBUG if (debug) printf("arprecv: "); #endif ptr = NULL; n = readether(d, &ptr, (void **)&ah, tleft, &etype); errno = 0; /* XXX */ if (n == -1 || n < sizeof(struct ether_arp)) { #ifdef ARP_DEBUG if (debug) printf("bad len=%zd\n", n); #endif free(ptr); return (-1); } if (etype != ETHERTYPE_ARP) { #ifdef ARP_DEBUG if (debug) printf("not arp type=%d\n", etype); #endif free(ptr); return (-1); } /* Ethernet address now checked in readether() */ if (ah->arp_hrd != htons(ARPHRD_ETHER) || ah->arp_pro != htons(ETHERTYPE_IP) || ah->arp_hln != sizeof(ah->arp_sha) || ah->arp_pln != sizeof(ah->arp_spa) ) { #ifdef ARP_DEBUG if (debug) printf("bad hrd/pro/hln/pln\n"); #endif free(ptr); return (-1); } if (ah->arp_op == htons(ARPOP_REQUEST)) { #ifdef ARP_DEBUG if (debug) printf("is request\n"); #endif arp_reply(d, ah); free(ptr); return (-1); } if (ah->arp_op != htons(ARPOP_REPLY)) { #ifdef ARP_DEBUG if (debug) printf("not ARP reply\n"); #endif free(ptr); return (-1); } /* Is the reply from the source we want? */ if (bcmp(&arp_list[arp_num].addr, ah->arp_spa, sizeof(ah->arp_spa))) { #ifdef ARP_DEBUG if (debug) printf("unwanted address\n"); #endif free(ptr); return (-1); } /* We don't care who the reply was sent to. */ /* We have our answer. */ #ifdef ARP_DEBUG if (debug) printf("got it\n"); #endif *pkt = ptr; *payload = ah; return (n); } /* * Convert an ARP request into a reply and send it. * Notes: Re-uses buffer. Pad to length = 46. */ void arp_reply(struct iodesc *d, void *pkt) { struct ether_arp *arp = pkt; if (arp->arp_hrd != htons(ARPHRD_ETHER) || arp->arp_pro != htons(ETHERTYPE_IP) || arp->arp_hln != sizeof(arp->arp_sha) || arp->arp_pln != sizeof(arp->arp_spa) ) { #ifdef ARP_DEBUG if (debug) printf("arp_reply: bad hrd/pro/hln/pln\n"); #endif return; } if (arp->arp_op != htons(ARPOP_REQUEST)) { #ifdef ARP_DEBUG if (debug) printf("arp_reply: not request!\n"); #endif return; } /* If we are not the target, ignore the request. */ if (bcmp(arp->arp_tpa, &d->myip, sizeof(arp->arp_tpa))) return; #ifdef ARP_DEBUG if (debug) { printf("arp_reply: to %s\n", ether_sprintf(arp->arp_sha)); } #endif arp->arp_op = htons(ARPOP_REPLY); /* source becomes target */ bcopy(arp->arp_sha, arp->arp_tha, sizeof(arp->arp_tha)); bcopy(arp->arp_spa, arp->arp_tpa, sizeof(arp->arp_tpa)); /* here becomes source */ bcopy(d->myea, arp->arp_sha, sizeof(arp->arp_sha)); bcopy(&d->myip, arp->arp_spa, sizeof(arp->arp_spa)); /* * No need to get fancy here. If the send fails, the * requestor will just ask again. */ (void) sendether(d, pkt, sizeof(*arp) + 18, arp->arp_tha, ETHERTYPE_ARP); } diff --git a/stand/libsa/bcd.c b/stand/libsa/bcd.c index 600d2bbff703..2f55bf9eda3c 100644 --- a/stand/libsa/bcd.c +++ b/stand/libsa/bcd.c @@ -1,36 +1,35 @@ /* * Some data-tables that are often used. * Cannot be copyrighted. */ -#include #include u_char const bcd2bin_data[] = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 0, 0, 0, 0, 0, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 0, 0, 0, 0, 0, 0, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 0, 0, 0, 0, 0, 0, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 0, 0, 0, 0, 0, 0, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 0, 0, 0, 0, 0, 0, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 0, 0, 0, 0, 0, 0, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 0, 0, 0, 0, 0, 0, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 0, 0, 0, 0, 0, 0, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 0, 0, 0, 0, 0, 0, 90, 91, 92, 93, 94, 95, 96, 97, 98, 99 }; u_char const bin2bcd_data[] = { 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, 0x28, 0x29, 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x40, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48, 0x49, 0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57, 0x58, 0x59, 0x60, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69, 0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78, 0x79, 0x80, 0x81, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87, 0x88, 0x89, 0x90, 0x91, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97, 0x98, 0x99 }; /* This is actually used with radix [2..36] */ char const hex2ascii_data[] = "0123456789abcdefghijklmnopqrstuvwxyz"; diff --git a/stand/libsa/cd9660read.c b/stand/libsa/cd9660read.c index 4a4501ada529..6062eb061506 100644 --- a/stand/libsa/cd9660read.c +++ b/stand/libsa/cd9660read.c @@ -1,364 +1,363 @@ /* * Copyright (C) 1996 Wolfgang Solfrank. * Copyright (C) 1996 TooLs GmbH. * 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 TooLs GmbH. * 4. The name of TooLs GmbH may not be used to endorse or promote products * derived from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY TOOLS GMBH ``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 TOOLS GMBH 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. */ /* Originally derived from libsa/cd9660.c: */ /* $NetBSD: cd9660.c,v 1.5 1997/06/26 19:11:33 drochner Exp $ */ -#include #include #include #include static uint64_t cd9660_lookup(const char *); static ssize_t cd9660_fsread(uint64_t, void *, size_t); #define SUSP_CONTINUATION "CE" #define SUSP_PRESENT "SP" #define SUSP_STOP "ST" #define SUSP_EXTREF "ER" #define RRIP_NAME "NM" typedef struct { ISO_SUSP_HEADER h; u_char signature [ISODCL ( 5, 6)]; u_char len_skp [ISODCL ( 7, 7)]; /* 711 */ } ISO_SUSP_PRESENT; static int read_iso_block(void *buffer, daddr_t blkno) { return (drvread(&dsk, buffer, blkno * 4, 4)); } static ISO_SUSP_HEADER * susp_lookup_record(const char *identifier, struct iso_directory_record *dp, int lenskip) { static char susp_buffer[ISO_DEFAULT_BLOCK_SIZE]; ISO_SUSP_HEADER *sh; ISO_RRIP_CONT *shc; char *p, *end; int error; p = dp->name + isonum_711(dp->name_len) + lenskip; /* Names of even length have a padding byte after the name. */ if ((isonum_711(dp->name_len) & 1) == 0) p++; end = (char *)dp + isonum_711(dp->length); while (p + 3 < end) { sh = (ISO_SUSP_HEADER *)p; if (bcmp(sh->type, identifier, 2) == 0) return (sh); if (bcmp(sh->type, SUSP_STOP, 2) == 0) return (NULL); if (bcmp(sh->type, SUSP_CONTINUATION, 2) == 0) { shc = (ISO_RRIP_CONT *)sh; error = read_iso_block(susp_buffer, isonum_733(shc->location)); /* Bail if it fails. */ if (error != 0) return (NULL); p = susp_buffer + isonum_733(shc->offset); end = p + isonum_733(shc->length); } else { /* Ignore this record and skip to the next. */ p += isonum_711(sh->length); /* Avoid infinite loops with corrupted file systems */ if (isonum_711(sh->length) == 0) return (NULL); } } return (NULL); } static const char * rrip_lookup_name(struct iso_directory_record *dp, int lenskip, size_t *len) { ISO_RRIP_ALTNAME *p; if (len == NULL) return (NULL); p = (ISO_RRIP_ALTNAME *)susp_lookup_record(RRIP_NAME, dp, lenskip); if (p == NULL) return (NULL); switch (*p->flags) { case ISO_SUSP_CFLAG_CURRENT: *len = 1; return ("."); case ISO_SUSP_CFLAG_PARENT: *len = 2; return (".."); case 0: *len = isonum_711(p->h.length) - 5; return ((char *)p + 5); default: /* * We don't handle hostnames or continued names as they are * too hard, so just bail and use the default name. */ return (NULL); } } static int rrip_check(struct iso_directory_record *dp, int *lenskip) { ISO_SUSP_PRESENT *sp; ISO_RRIP_EXTREF *er; char *p; /* First, see if we can find a SP field. */ p = dp->name + isonum_711(dp->name_len); if (p > (char *)dp + isonum_711(dp->length)) { return (0); } sp = (ISO_SUSP_PRESENT *)p; if (bcmp(sp->h.type, SUSP_PRESENT, 2) != 0) { return (0); } if (isonum_711(sp->h.length) != sizeof(ISO_SUSP_PRESENT)) { return (0); } if (sp->signature[0] != 0xbe || sp->signature[1] != 0xef) { return (0); } *lenskip = isonum_711(sp->len_skp); /* * Now look for an ER field. If RRIP is present, then there must * be at least one of these. It would be more pedantic to walk * through the list of fields looking for a Rock Ridge ER field. */ er = (ISO_RRIP_EXTREF *)susp_lookup_record(SUSP_EXTREF, dp, 0); if (er == NULL) { return (0); } return (1); } static int dirmatch(const char *path, struct iso_directory_record *dp, int use_rrip, int lenskip) { size_t len; const char *cp = NULL; int i, icase; if (use_rrip) cp = rrip_lookup_name(dp, lenskip, &len); else cp = NULL; if (cp == NULL) { len = isonum_711(dp->name_len); cp = dp->name; icase = 1; } else icase = 0; for (i = len; --i >= 0; path++, cp++) { if (!*path || *path == '/') break; if (*path == *cp) continue; if (!icase && toupper(*path) == *cp) continue; return 0; } if (*path && *path != '/') { return 0; } /* * Allow stripping of trailing dots and the version number. * Note that this will find the first instead of the last version * of a file. */ if (i >= 0 && (*cp == ';' || *cp == '.')) { /* This is to prevent matching of numeric extensions */ if (*cp == '.' && cp[1] != ';') { return 0; } while (--i >= 0) if (*++cp != ';' && (*cp < '0' || *cp > '9')) { return 0; } } return 1; } static uint64_t cd9660_lookup(const char *path) { static char blkbuf[MAX(ISO_DEFAULT_BLOCK_SIZE, sizeof(struct iso_primary_descriptor))]; struct iso_primary_descriptor *vd; struct iso_directory_record rec; struct iso_directory_record *dp = NULL; size_t dsize, off; daddr_t bno, boff; int rc, first, use_rrip, lenskip; uint64_t cookie; for (bno = 16;; bno++) { rc = read_iso_block(blkbuf, bno); vd = (struct iso_primary_descriptor *)blkbuf; if (bcmp(vd->id, ISO_STANDARD_ID, sizeof vd->id) != 0) return (0); if (isonum_711(vd->type) == ISO_VD_END) return (0); if (isonum_711(vd->type) == ISO_VD_PRIMARY) break; } bcopy(vd->root_directory_record, &rec, sizeof(rec)); if (*path == '/') path++; /* eat leading '/' */ first = 1; use_rrip = 0; lenskip = 0; while (*path) { bno = isonum_733(rec.extent) + isonum_711(rec.ext_attr_length); dsize = isonum_733(rec.size); off = 0; boff = 0; while (off < dsize) { if ((off % ISO_DEFAULT_BLOCK_SIZE) == 0) { rc = read_iso_block(blkbuf, bno + boff); if (rc) { return (0); } boff++; dp = (struct iso_directory_record *) blkbuf; } if (isonum_711(dp->length) == 0) { /* skip to next block, if any */ off = boff * ISO_DEFAULT_BLOCK_SIZE; continue; } /* See if RRIP is in use. */ if (first) use_rrip = rrip_check(dp, &lenskip); if (dirmatch(path, dp, use_rrip, first ? 0 : lenskip)) { first = 0; break; } else first = 0; dp = (struct iso_directory_record *) ((char *) dp + isonum_711(dp->length)); /* If the new block has zero length, it is padding. */ if (isonum_711(dp->length) == 0) { /* Skip to next block, if any. */ off = boff * ISO_DEFAULT_BLOCK_SIZE; continue; } off += isonum_711(dp->length); } if (off >= dsize) { return (0); } rec = *dp; while (*path && *path != '/') /* look for next component */ path++; if (*path) path++; /* skip '/' */ } if ((isonum_711(rec.flags) & 2) != 0) { return (0); } cookie = isonum_733(rec.extent) + isonum_711(rec.ext_attr_length); cookie = (cookie << 32) | isonum_733(rec.size); return (cookie); } static ssize_t cd9660_fsread(uint64_t cookie, void *buf, size_t nbytes) { static char blkbuf[ISO_DEFAULT_BLOCK_SIZE]; static daddr_t curstart = 0, curblk = 0; daddr_t blk, blk_off; off_t byte_off; size_t size, remaining, n; char *s; size = cookie & 0xffffffff; blk = (cookie >> 32) & 0xffffffff; /* Make sure we're looking at the right file. */ if (((blk << 32) | size) != cookie) { return (-1); } if (blk != curstart) { curstart = blk; fs_off = 0; } size -= fs_off; if (size < nbytes) { nbytes = size; } remaining = nbytes; s = buf; while (remaining > 0) { blk_off = fs_off >> ISO_DEFAULT_BLOCK_SHIFT; byte_off = fs_off & (ISO_DEFAULT_BLOCK_SIZE - 1); if (curblk != curstart + blk_off) { curblk = curstart + blk_off; read_iso_block(blkbuf, curblk); } if (remaining < ISO_DEFAULT_BLOCK_SIZE - byte_off) { n = remaining; } else { n = ISO_DEFAULT_BLOCK_SIZE - byte_off; } memcpy(s, blkbuf + byte_off, n); remaining -= n; s += n; fs_off += n; } return (nbytes); } diff --git a/stand/libsa/dev.c b/stand/libsa/dev.c index ea81e0f92115..1edc843d508c 100644 --- a/stand/libsa/dev.c +++ b/stand/libsa/dev.c @@ -1,178 +1,177 @@ /* $NetBSD: dev.c,v 1.4 1994/10/30 21:48:23 cgd Exp $ */ /*- * Copyright (c) 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. */ -#include #include #include #include "stand.h" int nodev(void) { return (ENXIO); } void nullsys(void) { } /* ARGSUSED */ int noioctl(struct open_file *f __unused, u_long cmd __unused, void *data __unused) { return (EINVAL); } char * devformat(struct devdesc *d) { static char name[DEV_DEVLEN]; if (d->d_dev->dv_fmtdev != NULL) return (d->d_dev->dv_fmtdev(d)); snprintf(name, sizeof(name), "%s%d:", d->d_dev->dv_name, d->d_unit); return (name); } /* NB: devspec points to the remainder of the device name after dv_name */ static int default_parsedev(struct devdesc **dev, const char *devspec, const char **path) { struct devdesc *idev; int unit, err; char *cp; idev = malloc(sizeof(struct devdesc)); if (idev == NULL) return (ENOMEM); unit = 0; cp = (char *)devspec; /* strtol interface, alas */ if (*devspec != '\0' && *devspec != ':') { errno = 0; unit = strtol(devspec, &cp, 0); if (errno != 0 || cp == devspec) { err = EUNIT; goto fail; } } if (*cp != '\0' && *cp != ':') { err = EINVAL; goto fail; } idev->d_unit = unit; if (path != NULL) *path = (*cp == 0) ? cp : cp + 1; *dev = idev; return (0); fail: free(idev); return (err); } /* NB: devspec points to the whole device spec, and possible trailing path */ int devparse(struct devdesc **dev, const char *devspec, const char **path) { struct devdesc *idev; struct devsw *dv; int i, err; const char *np; /* minimum length check */ if (strlen(devspec) < 2) return (EINVAL); /* look for a device that matches */ for (i = 0; devsw[i] != NULL; i++) { dv = devsw[i]; if (dv->dv_match != NULL) { if (dv->dv_match(dv, devspec) != 0) break; } else { if (!strncmp(devspec, dv->dv_name, strlen(dv->dv_name))) break; } } if (devsw[i] == NULL) return (ENOENT); idev = NULL; err = 0; if (dv->dv_parsedev) { err = dv->dv_parsedev(&idev, devspec, path); } else { np = devspec + strlen(dv->dv_name); err = default_parsedev(&idev, np, path); } if (err != 0) return (err); idev->d_dev = dv; if (dev != NULL) *dev = idev; else free(idev); return (0); } int devinit(void) { int err = 0; /* * March through the device switch probing for things. */ for (int i = 0; devsw[i] != NULL; i++) { if (devsw[i]->dv_init != NULL) { if ((devsw[i]->dv_init)() != 0) { err++; } } } return (err); } 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/libsa/ether.c b/stand/libsa/ether.c index e9dae723e99d..08ef131e9e0e 100644 --- a/stand/libsa/ether.c +++ b/stand/libsa/ether.c @@ -1,143 +1,142 @@ /* $NetBSD: ether.c,v 1.11 1997/07/07 15:52:50 drochner Exp $ */ /* * Copyright (c) 1992 Regents of the University of California. * All rights reserved. * * This software was developed by the Computer Systems Engineering group * at Lawrence Berkeley Laboratory under DARPA contract BG 91-66 and * contributed to Berkeley. * * 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. */ -#include #include #include #include #include #include #include #include #include #include "stand.h" #include "net.h" #include "netif.h" /* Caller must leave room for ethernet header in front!! */ ssize_t sendether(struct iodesc *d, void *pkt, size_t len, uint8_t *dea, int etype) { ssize_t n; struct ether_header *eh; #ifdef ETHER_DEBUG if (debug) printf("sendether: called\n"); #endif eh = (struct ether_header *)pkt - 1; len += sizeof(*eh); MACPY(d->myea, eh->ether_shost); /* by byte */ MACPY(dea, eh->ether_dhost); /* by byte */ eh->ether_type = htons(etype); n = netif_put(d, eh, len); if (n == -1 || n < sizeof(*eh)) return (-1); n -= sizeof(*eh); return (n); } /* * Get a packet of any Ethernet type, with our address or * the broadcast address. Save the Ether type in etype. * Unless there is an error, we pass the whole packet and the unencapsulated * data. */ ssize_t readether(struct iodesc *d, void **pkt, void **payload, time_t tleft, uint16_t *etype) { ssize_t n; struct ether_header *eh; void *ptr; #ifdef ETHER_DEBUG if (debug) printf("readether: called\n"); #endif ptr = NULL; n = netif_get(d, &ptr, tleft); if (n == -1 || n < sizeof(*eh)) { free(ptr); return (-1); } eh = (struct ether_header *)((uintptr_t)ptr + ETHER_ALIGN); /* Validate Ethernet address. */ if (bcmp(d->myea, eh->ether_dhost, 6) != 0 && bcmp(bcea, eh->ether_dhost, 6) != 0) { #ifdef ETHER_DEBUG if (debug) printf("readether: not ours (ea=%s)\n", ether_sprintf(eh->ether_dhost)); #endif free(ptr); return (-1); } *pkt = ptr; *payload = (void *)((uintptr_t)eh + sizeof(*eh)); *etype = ntohs(eh->ether_type); n -= sizeof(*eh); return (n); } /* * Convert Ethernet address to printable (loggable) representation. */ static char digits[] = "0123456789abcdef"; char * ether_sprintf(u_char *ap) { int i; static char etherbuf[18]; char *cp = etherbuf; for (i = 0; i < 6; i++) { *cp++ = digits[*ap >> 4]; *cp++ = digits[*ap++ & 0xf]; *cp++ = ':'; } *--cp = 0; return (etherbuf); } diff --git a/stand/libsa/globals.c b/stand/libsa/globals.c index ca8d190eeeef..2797045d4faf 100644 --- a/stand/libsa/globals.c +++ b/stand/libsa/globals.c @@ -1,36 +1,35 @@ /* $NetBSD: globals.c,v 1.3 1995/09/18 21:19:27 pk Exp $ */ /* * globals.c: * * global variables should be separate, so nothing else * must be included extraneously. */ -#include #include #include #include #include "stand.h" #include "net.h" u_char bcea[6] = BA; /* broadcast ethernet address */ char rootpath[FNAME_SIZE] = "/"; /* root mount path */ char bootfile[FNAME_SIZE]; /* bootp says to boot this */ char hostname[FNAME_SIZE]; /* our hostname */ int hostnamelen; char domainname[FNAME_SIZE]; /* our DNS domain */ int domainnamelen; int netproto = NET_NONE; /* Network prototol */ char ifname[IFNAME_SIZE]; /* name of interface (e.g. "le0") */ struct in_addr myip; /* my ip address */ struct in_addr nameip; /* DNS server ip address */ struct in_addr rootip; /* root ip address */ struct in_addr swapip; /* swap ip address */ struct in_addr gateip; /* gateway ip address */ n_long netmask = 0xffffff00; /* subnet or net mask */ u_int intf_mtu; /* interface mtu from bootp/dhcp */ int errno; /* our old friend */ diff --git a/stand/libsa/gpt.c b/stand/libsa/gpt.c index 14e9821eef8e..0ba3f0ee1985 100644 --- a/stand/libsa/gpt.c +++ b/stand/libsa/gpt.c @@ -1,383 +1,382 @@ /*- * Copyright (c) 2010 Pawel Jakub Dawidek * 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 #include #include #ifndef LITTLE_ENDIAN #error gpt.c works only for little endian architectures #endif #include "stand.h" #include "zlib.h" #include "drv.h" #include "gpt.h" static struct gpt_hdr hdr_primary, hdr_backup, *gpthdr; static uint64_t hdr_primary_lba, hdr_backup_lba; static struct gpt_ent table_primary[MAXTBLENTS], table_backup[MAXTBLENTS]; static struct gpt_ent *gpttable; static int curent, bootonce; /* * Buffer below 64kB passed on gptread(), which can hold at least * one sector of data (512 bytes). */ static char *secbuf; static void gptupdate(const char *which, struct dsk *dskp, struct gpt_hdr *hdr, struct gpt_ent *table) { int entries_per_sec, firstent; daddr_t slba; /* * We need to update the following for both primary and backup GPT: * 1. Sector on disk that contains current partition. * 2. Partition table checksum. * 3. Header checksum. * 4. Header on disk. */ entries_per_sec = DEV_BSIZE / hdr->hdr_entsz; slba = curent / entries_per_sec; firstent = slba * entries_per_sec; bcopy(&table[firstent], secbuf, DEV_BSIZE); slba += hdr->hdr_lba_table; if (drvwrite(dskp, secbuf, slba, 1)) { printf("%s: unable to update %s GPT partition table\n", BOOTPROG, which); return; } hdr->hdr_crc_table = crc32(0, Z_NULL, 0); hdr->hdr_crc_table = crc32(hdr->hdr_crc_table, (const Bytef *)table, hdr->hdr_entries * hdr->hdr_entsz); hdr->hdr_crc_self = crc32(0, Z_NULL, 0); hdr->hdr_crc_self = crc32(hdr->hdr_crc_self, (const Bytef *)hdr, hdr->hdr_size); bzero(secbuf, DEV_BSIZE); bcopy(hdr, secbuf, hdr->hdr_size); if (drvwrite(dskp, secbuf, hdr->hdr_lba_self, 1)) { printf("%s: unable to update %s GPT header\n", BOOTPROG, which); return; } } int gptfind(const uuid_t *uuid, struct dsk *dskp, int part) { struct gpt_ent *ent; int firsttry; if (part >= 0) { if (part == 0 || part > gpthdr->hdr_entries) { printf("%s: invalid partition index\n", BOOTPROG); return (-1); } ent = &gpttable[part - 1]; if (bcmp(&ent->ent_type, uuid, sizeof(uuid_t)) != 0) { printf("%s: specified partition is not UFS\n", BOOTPROG); return (-1); } curent = part - 1; goto found; } firsttry = (curent == -1); curent++; if (curent >= gpthdr->hdr_entries) { curent = gpthdr->hdr_entries; return (-1); } if (bootonce) { /* * First look for partition with both GPT_ENT_ATTR_BOOTME and * GPT_ENT_ATTR_BOOTONCE flags. */ for (; curent < gpthdr->hdr_entries; curent++) { ent = &gpttable[curent]; if (bcmp(&ent->ent_type, uuid, sizeof(uuid_t)) != 0) continue; if (!(ent->ent_attr & GPT_ENT_ATTR_BOOTME)) continue; if (!(ent->ent_attr & GPT_ENT_ATTR_BOOTONCE)) continue; /* Ok, found one. */ goto found; } bootonce = 0; curent = 0; } for (; curent < gpthdr->hdr_entries; curent++) { ent = &gpttable[curent]; if (bcmp(&ent->ent_type, uuid, sizeof(uuid_t)) != 0) continue; if (!(ent->ent_attr & GPT_ENT_ATTR_BOOTME)) continue; if (ent->ent_attr & GPT_ENT_ATTR_BOOTONCE) continue; /* Ok, found one. */ goto found; } if (firsttry) { /* * No partition with BOOTME flag was found, try to boot from * first UFS partition. */ for (curent = 0; curent < gpthdr->hdr_entries; curent++) { ent = &gpttable[curent]; if (bcmp(&ent->ent_type, uuid, sizeof(uuid_t)) != 0) continue; /* Ok, found one. */ goto found; } } return (-1); found: dskp->part = curent + 1; ent = &gpttable[curent]; dskp->start = ent->ent_lba_start; if (ent->ent_attr & GPT_ENT_ATTR_BOOTONCE) { /* * Clear BOOTME, but leave BOOTONCE set before trying to * boot from this partition. */ if (hdr_primary_lba > 0) { table_primary[curent].ent_attr &= ~GPT_ENT_ATTR_BOOTME; gptupdate("primary", dskp, &hdr_primary, table_primary); } if (hdr_backup_lba > 0) { table_backup[curent].ent_attr &= ~GPT_ENT_ATTR_BOOTME; gptupdate("backup", dskp, &hdr_backup, table_backup); } } return (0); } static int gptread_hdr(const char *which, struct dsk *dskp, struct gpt_hdr *hdr, uint64_t hdrlba) { uint32_t crc; if (drvread(dskp, secbuf, hdrlba, 1)) { printf("%s: unable to read %s GPT header\n", BOOTPROG, which); return (-1); } bcopy(secbuf, hdr, sizeof(*hdr)); if (bcmp(hdr->hdr_sig, GPT_HDR_SIG, sizeof(hdr->hdr_sig)) != 0 || hdr->hdr_lba_self != hdrlba || hdr->hdr_revision < 0x00010000 || hdr->hdr_entsz < sizeof(struct gpt_ent) || hdr->hdr_entries > MAXTBLENTS || DEV_BSIZE % hdr->hdr_entsz != 0) { printf("%s: invalid %s GPT header\n", BOOTPROG, which); return (-1); } crc = hdr->hdr_crc_self; hdr->hdr_crc_self = crc32(0, Z_NULL, 0); if (crc32(hdr->hdr_crc_self, (const Bytef *)hdr, hdr->hdr_size) != crc) { printf("%s: %s GPT header checksum mismatch\n", BOOTPROG, which); return (-1); } hdr->hdr_crc_self = crc; return (0); } void gptbootfailed(struct dsk *dskp) { if (!(gpttable[curent].ent_attr & GPT_ENT_ATTR_BOOTONCE)) return; if (hdr_primary_lba > 0) { table_primary[curent].ent_attr &= ~GPT_ENT_ATTR_BOOTONCE; table_primary[curent].ent_attr |= GPT_ENT_ATTR_BOOTFAILED; gptupdate("primary", dskp, &hdr_primary, table_primary); } if (hdr_backup_lba > 0) { table_backup[curent].ent_attr &= ~GPT_ENT_ATTR_BOOTONCE; table_backup[curent].ent_attr |= GPT_ENT_ATTR_BOOTFAILED; gptupdate("backup", dskp, &hdr_backup, table_backup); } } static void gptbootconv(const char *which, struct dsk *dskp, struct gpt_hdr *hdr, struct gpt_ent *table) { struct gpt_ent *ent; daddr_t slba; int table_updated, sector_updated; int entries_per_sec, nent, part; table_updated = 0; entries_per_sec = DEV_BSIZE / hdr->hdr_entsz; for (nent = 0, slba = hdr->hdr_lba_table; slba < hdr->hdr_lba_table + hdr->hdr_entries / entries_per_sec; slba++, nent += entries_per_sec) { sector_updated = 0; for (part = 0; part < entries_per_sec; part++) { ent = &table[nent + part]; if ((ent->ent_attr & (GPT_ENT_ATTR_BOOTME | GPT_ENT_ATTR_BOOTONCE | GPT_ENT_ATTR_BOOTFAILED)) != GPT_ENT_ATTR_BOOTONCE) { continue; } ent->ent_attr &= ~GPT_ENT_ATTR_BOOTONCE; ent->ent_attr |= GPT_ENT_ATTR_BOOTFAILED; table_updated = 1; sector_updated = 1; } if (!sector_updated) continue; bcopy(&table[nent], secbuf, DEV_BSIZE); if (drvwrite(dskp, secbuf, slba, 1)) { printf("%s: unable to update %s GPT partition table\n", BOOTPROG, which); } } if (!table_updated) return; hdr->hdr_crc_table = crc32(0, Z_NULL, 0); hdr->hdr_crc_table = crc32(hdr->hdr_crc_table, (const Bytef *)table, hdr->hdr_entries * hdr->hdr_entsz); hdr->hdr_crc_self = crc32(0, Z_NULL, 0); hdr->hdr_crc_self = crc32(hdr->hdr_crc_self, (const Bytef *)hdr, hdr->hdr_size); bzero(secbuf, DEV_BSIZE); bcopy(hdr, secbuf, hdr->hdr_size); if (drvwrite(dskp, secbuf, hdr->hdr_lba_self, 1)) printf("%s: unable to update %s GPT header\n", BOOTPROG, which); } static int gptread_table(const char *which, struct dsk *dskp, struct gpt_hdr *hdr, struct gpt_ent *table) { struct gpt_ent *ent; int entries_per_sec; int part, nent; daddr_t slba; if (hdr->hdr_entries == 0) return (0); entries_per_sec = DEV_BSIZE / hdr->hdr_entsz; slba = hdr->hdr_lba_table; nent = 0; for (;;) { if (drvread(dskp, secbuf, slba, 1)) { printf("%s: unable to read %s GPT partition table\n", BOOTPROG, which); return (-1); } ent = (struct gpt_ent *)secbuf; for (part = 0; part < entries_per_sec; part++, ent++) { bcopy(ent, &table[nent], sizeof(table[nent])); if (++nent >= hdr->hdr_entries) break; } if (nent >= hdr->hdr_entries) break; slba++; } if (crc32(0, (const Bytef *)table, nent * hdr->hdr_entsz) != hdr->hdr_crc_table) { printf("%s: %s GPT table checksum mismatch\n", BOOTPROG, which); return (-1); } return (0); } int gptread(struct dsk *dskp, char *buf) { uint64_t altlba; /* * Read and verify both GPT headers: primary and backup. */ secbuf = buf; hdr_primary_lba = hdr_backup_lba = 0; curent = -1; bootonce = 1; dskp->start = 0; if (gptread_hdr("primary", dskp, &hdr_primary, 1) == 0 && gptread_table("primary", dskp, &hdr_primary, table_primary) == 0) { hdr_primary_lba = hdr_primary.hdr_lba_self; gpthdr = &hdr_primary; gpttable = table_primary; } if (hdr_primary_lba > 0) { /* * If primary header is valid, we can get backup * header location from there. */ altlba = hdr_primary.hdr_lba_alt; } else { altlba = drvsize(dskp); if (altlba > 0) altlba--; } if (altlba == 0) printf("%s: unable to locate backup GPT header\n", BOOTPROG); else if (gptread_hdr("backup", dskp, &hdr_backup, altlba) == 0 && gptread_table("backup", dskp, &hdr_backup, table_backup) == 0) { hdr_backup_lba = hdr_backup.hdr_lba_self; if (hdr_primary_lba == 0) { gpthdr = &hdr_backup; gpttable = table_backup; printf("%s: using backup GPT\n", BOOTPROG); } } /* * Convert all BOOTONCE without BOOTME flags into BOOTFAILED. * BOOTONCE without BOOTME means that we tried to boot from it, * but failed after leaving gptboot and machine was rebooted. * We don't want to leave partitions marked as BOOTONCE only, * because when we boot successfully start-up scripts should * find at most one partition with only BOOTONCE flag and this * will mean that we booted from that partition. */ if (hdr_primary_lba != 0) gptbootconv("primary", dskp, &hdr_primary, table_primary); if (hdr_backup_lba != 0) gptbootconv("backup", dskp, &hdr_backup, table_backup); if (hdr_primary_lba == 0 && hdr_backup_lba == 0) return (-1); return (0); } diff --git a/stand/libsa/in_cksum.c b/stand/libsa/in_cksum.c index f4d5a91b1b5a..15b2adb6080c 100644 --- a/stand/libsa/in_cksum.c +++ b/stand/libsa/in_cksum.c @@ -1,88 +1,87 @@ /* $NetBSD: in_cksum.c,v 1.6 2000/03/31 19:55:09 castor Exp $ */ /* * Copyright (c) 1992 Regents of the University of California. * All rights reserved. * * This software was developed by the Computer Systems Engineering group * at Lawrence Berkeley Laboratory under DARPA contract BG 91-66 and * contributed to Berkeley. * * 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. */ -#include #include #include #include "stand.h" /* * Checksum routine for Internet Protocol family headers. * This routine is very heavily used in the network * code and should be modified for each CPU to be as fast as possible. * In particular, it should not be this one. */ int in_cksum(void *p, int len) { int sum = 0, oddbyte = 0, v = 0; u_char *cp = p; /* we assume < 2^16 bytes being summed */ while (len > 0) { if (oddbyte) { sum += v + *cp++; len--; } if (((uintptr_t)cp & 1) == 0) { while ((len -= 2) >= 0) { sum += *(u_short *)cp; cp += 2; } } else { while ((len -= 2) >= 0) { #if BYTE_ORDER == BIG_ENDIAN sum += *cp++ << 8; sum += *cp++; #else sum += *cp++; sum += *cp++ << 8; #endif } } if ((oddbyte = len & 1) != 0) #if BYTE_ORDER == BIG_ENDIAN v = *cp << 8; #else v = *cp; #endif } if (oddbyte) sum += v; sum = (sum >> 16) + (sum & 0xffff); /* add in accumulated carries */ sum += sum >> 16; /* add potential last carry */ return (0xffff & ~sum); } diff --git a/stand/libsa/ip.c b/stand/libsa/ip.c index 753aeb1bae5e..2c2acf2eda16 100644 --- a/stand/libsa/ip.c +++ b/stand/libsa/ip.c @@ -1,425 +1,424 @@ /* * Copyright (c) 1992 Regents of the University of California. * All rights reserved. * * This software was developed by the Computer Systems Engineering group * at Lawrence Berkeley Laboratory under DARPA contract BG 91-66 and * contributed to Berkeley. * * 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. */ /* * The send and receive functions were originally implemented in udp.c and * moved here. Also it is likely some more cleanup can be done, especially * once we will implement the support for tcp. */ -#include #include #include #include #include #include #include #include #include #include #include #include #include #include "stand.h" #include "net.h" typedef STAILQ_HEAD(ipqueue, ip_queue) ip_queue_t; struct ip_queue { void *ipq_pkt; struct ip *ipq_hdr; STAILQ_ENTRY(ip_queue) ipq_next; }; /* * Fragment re-assembly queue. */ struct ip_reasm { struct in_addr ip_src; struct in_addr ip_dst; uint16_t ip_id; uint8_t ip_proto; uint8_t ip_ttl; size_t ip_total_size; ip_queue_t ip_queue; void *ip_pkt; struct ip *ip_hdr; STAILQ_ENTRY(ip_reasm) ip_next; }; STAILQ_HEAD(ire_list, ip_reasm) ire_list = STAILQ_HEAD_INITIALIZER(ire_list); /* Caller must leave room for ethernet and ip headers in front!! */ ssize_t sendip(struct iodesc *d, void *pkt, size_t len, uint8_t proto) { ssize_t cc; struct ip *ip; u_char *ea; #ifdef NET_DEBUG if (debug) { printf("sendip: proto: %x d=%p called.\n", proto, (void *)d); if (d) { printf("saddr: %s:%d", inet_ntoa(d->myip), ntohs(d->myport)); printf(" daddr: %s:%d\n", inet_ntoa(d->destip), ntohs(d->destport)); } } #endif ip = (struct ip *)pkt - 1; len += sizeof(*ip); bzero(ip, sizeof(*ip)); ip->ip_v = IPVERSION; /* half-char */ ip->ip_hl = sizeof(*ip) >> 2; /* half-char */ ip->ip_len = htons(len); ip->ip_p = proto; /* char */ ip->ip_ttl = IPDEFTTL; /* char */ ip->ip_src = d->myip; ip->ip_dst = d->destip; ip->ip_sum = in_cksum(ip, sizeof(*ip)); /* short, but special */ if (ip->ip_dst.s_addr == INADDR_BROADCAST || ip->ip_src.s_addr == 0 || netmask == 0 || SAMENET(ip->ip_src, ip->ip_dst, netmask)) ea = arpwhohas(d, ip->ip_dst); else ea = arpwhohas(d, gateip); cc = sendether(d, ip, len, ea, ETHERTYPE_IP); if (cc == -1) return (-1); if (cc != len) panic("sendip: bad write (%zd != %zd)", cc, len); return (cc - sizeof(*ip)); } static void ip_reasm_free(struct ip_reasm *ipr) { struct ip_queue *ipq; while ((ipq = STAILQ_FIRST(&ipr->ip_queue)) != NULL) { STAILQ_REMOVE_HEAD(&ipr->ip_queue, ipq_next); free(ipq->ipq_pkt); free(ipq); } free(ipr->ip_pkt); free(ipr); } static int ip_reasm_add(struct ip_reasm *ipr, void *pkt, struct ip *ip) { struct ip_queue *ipq, *prev, *p; if ((ipq = calloc(1, sizeof (*ipq))) == NULL) return (1); ipq->ipq_pkt = pkt; ipq->ipq_hdr = ip; prev = NULL; STAILQ_FOREACH(p, &ipr->ip_queue, ipq_next) { if ((ntohs(p->ipq_hdr->ip_off) & IP_OFFMASK) < (ntohs(ip->ip_off) & IP_OFFMASK)) { prev = p; continue; } if (prev == NULL) break; STAILQ_INSERT_AFTER(&ipr->ip_queue, prev, ipq, ipq_next); return (0); } STAILQ_INSERT_HEAD(&ipr->ip_queue, ipq, ipq_next); return (0); } /* * Receive a IP packet and validate it is for us. */ static ssize_t readipv4(struct iodesc *d, void **pkt, void **payload, time_t tleft, uint8_t proto) { ssize_t n; size_t hlen; struct ether_header *eh; struct ip *ip; struct udphdr *uh; uint16_t etype; /* host order */ char *ptr; struct ip_reasm *ipr; struct ip_queue *ipq, *last; #ifdef NET_DEBUG if (debug) printf("readip: called\n"); #endif ip = NULL; ptr = NULL; n = readether(d, (void **)&ptr, (void **)&ip, tleft, &etype); if (n == -1 || n < sizeof(*ip) + sizeof(*uh)) { free(ptr); return (-1); } /* Ethernet address checks now in readether() */ /* Need to respond to ARP requests. */ if (etype == ETHERTYPE_ARP) { struct arphdr *ah = (void *)ip; if (ah->ar_op == htons(ARPOP_REQUEST)) { /* Send ARP reply */ arp_reply(d, ah); } free(ptr); errno = EAGAIN; /* Call me again. */ return (-1); } if (etype != ETHERTYPE_IP) { #ifdef NET_DEBUG if (debug) printf("readip: not IP. ether_type=%x\n", etype); #endif free(ptr); return (-1); } /* Check ip header */ if (ip->ip_v != IPVERSION || /* half char */ ip->ip_p != proto) { #ifdef NET_DEBUG if (debug) { printf("readip: IP version or proto. ip_v=%d ip_p=%d\n", ip->ip_v, ip->ip_p); } #endif free(ptr); return (-1); } hlen = ip->ip_hl << 2; if (hlen < sizeof(*ip) || in_cksum(ip, hlen) != 0) { #ifdef NET_DEBUG if (debug) printf("readip: short hdr or bad cksum.\n"); #endif free(ptr); return (-1); } if (n < ntohs(ip->ip_len)) { #ifdef NET_DEBUG if (debug) printf("readip: bad length %d < %d.\n", (int)n, ntohs(ip->ip_len)); #endif free(ptr); return (-1); } if (d->myip.s_addr && ip->ip_dst.s_addr != d->myip.s_addr) { #ifdef NET_DEBUG if (debug) { printf("readip: bad saddr %s != ", inet_ntoa(d->myip)); printf("%s\n", inet_ntoa(ip->ip_dst)); } #endif free(ptr); return (-1); } /* Unfragmented packet. */ if ((ntohs(ip->ip_off) & IP_MF) == 0 && (ntohs(ip->ip_off) & IP_OFFMASK) == 0) { uh = (struct udphdr *)((uintptr_t)ip + sizeof (*ip)); /* If there were ip options, make them go away */ if (hlen != sizeof(*ip)) { bcopy(((u_char *)ip) + hlen, uh, uh->uh_ulen - hlen); ip->ip_len = htons(sizeof(*ip)); n -= hlen - sizeof(*ip); } n = (n > (ntohs(ip->ip_len) - sizeof(*ip))) ? ntohs(ip->ip_len) - sizeof(*ip) : n; *pkt = ptr; *payload = (void *)((uintptr_t)ip + sizeof(*ip)); return (n); } STAILQ_FOREACH(ipr, &ire_list, ip_next) { if (ipr->ip_src.s_addr == ip->ip_src.s_addr && ipr->ip_dst.s_addr == ip->ip_dst.s_addr && ipr->ip_id == ip->ip_id && ipr->ip_proto == ip->ip_p) break; } /* Allocate new reassembly entry */ if (ipr == NULL) { if ((ipr = calloc(1, sizeof (*ipr))) == NULL) { free(ptr); return (-1); } ipr->ip_src = ip->ip_src; ipr->ip_dst = ip->ip_dst; ipr->ip_id = ip->ip_id; ipr->ip_proto = ip->ip_p; ipr->ip_ttl = MAXTTL; STAILQ_INIT(&ipr->ip_queue); STAILQ_INSERT_TAIL(&ire_list, ipr, ip_next); } if (ip_reasm_add(ipr, ptr, ip) != 0) { STAILQ_REMOVE(&ire_list, ipr, ip_reasm, ip_next); free(ipr); free(ptr); return (-1); } if ((ntohs(ip->ip_off) & IP_MF) == 0) { ipr->ip_total_size = (8 * (ntohs(ip->ip_off) & IP_OFFMASK)); ipr->ip_total_size += n + sizeof (*ip); ipr->ip_total_size += sizeof (struct ether_header); ipr->ip_pkt = malloc(ipr->ip_total_size + 2); if (ipr->ip_pkt == NULL) { STAILQ_REMOVE(&ire_list, ipr, ip_reasm, ip_next); ip_reasm_free(ipr); return (-1); } } /* * If we do not have re-assembly buffer ipr->ip_pkt, we are still * missing fragments, so just restart the read. */ if (ipr->ip_pkt == NULL) { errno = EAGAIN; return (-1); } /* * Walk the packet list in reassembly queue, if we got all the * fragments, build the packet. */ n = 0; last = NULL; STAILQ_FOREACH(ipq, &ipr->ip_queue, ipq_next) { if ((ntohs(ipq->ipq_hdr->ip_off) & IP_OFFMASK) != n / 8) { STAILQ_REMOVE(&ire_list, ipr, ip_reasm, ip_next); ip_reasm_free(ipr); return (-1); } n += ntohs(ipq->ipq_hdr->ip_len) - (ipq->ipq_hdr->ip_hl << 2); last = ipq; } if ((ntohs(last->ipq_hdr->ip_off) & IP_MF) != 0) { errno = EAGAIN; return (-1); } ipq = STAILQ_FIRST(&ipr->ip_queue); /* Fabricate ethernet header */ eh = (struct ether_header *)((uintptr_t)ipr->ip_pkt + 2); bcopy((void *)((uintptr_t)ipq->ipq_pkt + 2), eh, sizeof (*eh)); /* Fabricate IP header */ ipr->ip_hdr = (struct ip *)((uintptr_t)eh + sizeof (*eh)); bcopy(ipq->ipq_hdr, ipr->ip_hdr, sizeof (*ipr->ip_hdr)); ipr->ip_hdr->ip_hl = sizeof (*ipr->ip_hdr) >> 2; ipr->ip_hdr->ip_len = htons(n); ipr->ip_hdr->ip_sum = 0; ipr->ip_hdr->ip_sum = in_cksum(ipr->ip_hdr, sizeof (*ipr->ip_hdr)); n = 0; ptr = (char *)((uintptr_t)ipr->ip_hdr + sizeof (*ipr->ip_hdr)); STAILQ_FOREACH(ipq, &ipr->ip_queue, ipq_next) { char *data; size_t len; hlen = ipq->ipq_hdr->ip_hl << 2; len = ntohs(ipq->ipq_hdr->ip_len) - hlen; data = (char *)((uintptr_t)ipq->ipq_hdr + hlen); bcopy(data, ptr + n, len); n += len; } *pkt = ipr->ip_pkt; ipr->ip_pkt = NULL; /* Avoid free from ip_reasm_free() */ *payload = ptr; /* Clean up the reassembly list */ while ((ipr = STAILQ_FIRST(&ire_list)) != NULL) { STAILQ_REMOVE_HEAD(&ire_list, ip_next); ip_reasm_free(ipr); } return (n); } /* * Receive a IP packet. */ ssize_t readip(struct iodesc *d, void **pkt, void **payload, time_t tleft, uint8_t proto) { time_t t; ssize_t ret = -1; t = getsecs(); while ((getsecs() - t) < tleft) { errno = 0; ret = readipv4(d, pkt, payload, tleft, proto); if (ret >= 0) return (ret); /* Bubble up the error if it wasn't successful */ if (errno != EAGAIN) return (-1); } /* We've exhausted tleft; timeout */ errno = ETIMEDOUT; return (-1); } diff --git a/stand/libsa/net.c b/stand/libsa/net.c index 20731d90b6aa..76b31c50d1e7 100644 --- a/stand/libsa/net.c +++ b/stand/libsa/net.c @@ -1,297 +1,296 @@ /* $NetBSD: net.c,v 1.20 1997/12/26 22:41:30 scottr Exp $ */ /* * Copyright (c) 1992 Regents of the University of California. * All rights reserved. * * This software was developed by the Computer Systems Engineering group * at Lawrence Berkeley Laboratory under DARPA contract BG 91-66 and * contributed to Berkeley. * * 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. */ -#include #include #include #include #include #include #include #include #include #include #include #include #include "stand.h" #include "net.h" /* * Maximum wait time for sending and receiving before we give up and timeout. * If set to 0, operations will eventually timeout completely, but send/recv * timeouts must progress exponentially from MINTMO to MAXTMO before final * timeout is hit. */ #ifndef MAXWAIT #define MAXWAIT 300 /* seconds */ #endif #if MAXWAIT < 0 #error MAXWAIT must not be a negative number #endif /* * Send a packet and wait for a reply, with exponential backoff. * * The send routine must return the actual number of bytes written, * or -1 on error. * * The receive routine can indicate success by returning the number of * bytes read; it can return 0 to indicate EOF; it can return -1 with a * non-zero errno to indicate failure; finally, it can return -1 with a * zero errno to indicate it isn't done yet. */ ssize_t sendrecv(struct iodesc *d, ssize_t (*sproc)(struct iodesc *, void *, size_t), void *sbuf, size_t ssize, ssize_t (*rproc)(struct iodesc *, void **, void **, time_t, void *), void **pkt, void **payload, void *recv_extra) { ssize_t cc; time_t t, tmo, tlast; time_t tref; long tleft; #ifdef NET_DEBUG if (debug) printf("sendrecv: called\n"); #endif tmo = MINTMO; tlast = 0; tleft = 0; tref = t = getsecs(); for (;;) { if (MAXWAIT > 0 && (t - tref) >= MAXWAIT) { errno = ETIMEDOUT; return -1; } if (tleft <= 0) { if (tmo >= MAXTMO) { errno = ETIMEDOUT; return -1; } cc = (*sproc)(d, sbuf, ssize); if (cc != -1 && cc < ssize) panic("sendrecv: short write! (%zd < %zd)", cc, ssize); tleft = tmo; tmo += MINTMO; if (tmo > MAXTMO) tmo = MAXTMO; if (cc == -1) { /* Error on transmit; wait before retrying */ while ((getsecs() - t) < tmo) ; tleft = 0; continue; } tlast = t; } /* Try to get a packet and process it. */ cc = (*rproc)(d, pkt, payload, tleft, recv_extra); /* Return on data, EOF or real error. */ if (cc != -1 || (errno != 0 && errno != ETIMEDOUT)) return (cc); /* Timed out or didn't get the packet we're waiting for */ t = getsecs(); tleft -= t - tlast; tlast = t; } } /* * Like inet_addr() in the C library, but we only accept base-10. * Return values are in network order. */ n_long inet_addr(char *cp) { u_long val; int n; char c; u_int parts[4]; u_int *pp = parts; for (;;) { /* * Collect number up to ``.''. * Values are specified as for C: * 0x=hex, 0=octal, other=decimal. */ val = 0; while ((c = *cp) != '\0') { if (c >= '0' && c <= '9') { val = (val * 10) + (c - '0'); cp++; continue; } break; } if (*cp == '.') { /* * Internet format: * a.b.c.d * a.b.c (with c treated as 16-bits) * a.b (with b treated as 24 bits) */ if (pp >= parts + 3 || val > 0xff) goto bad; *pp++ = val, cp++; } else break; } /* * Check for trailing characters. */ if (*cp != '\0') goto bad; /* * Concoct the address according to * the number of parts specified. */ n = pp - parts + 1; switch (n) { case 1: /* a -- 32 bits */ break; case 2: /* a.b -- 8.24 bits */ if (val > 0xffffff) goto bad; val |= parts[0] << 24; break; case 3: /* a.b.c -- 8.8.16 bits */ if (val > 0xffff) goto bad; val |= (parts[0] << 24) | (parts[1] << 16); break; case 4: /* a.b.c.d -- 8.8.8.8 bits */ if (val > 0xff) goto bad; val |= (parts[0] << 24) | (parts[1] << 16) | (parts[2] << 8); break; } return (htonl(val)); bad: return (htonl(INADDR_NONE)); } char * inet_ntoa(struct in_addr ia) { return (intoa(ia.s_addr)); } /* Similar to inet_ntoa() */ char * intoa(n_long addr) { char *cp; u_int byte; int n; static char buf[17]; /* strlen(".255.255.255.255") + 1 */ addr = ntohl(addr); cp = &buf[sizeof buf]; *--cp = '\0'; n = 4; do { byte = addr & 0xff; *--cp = byte % 10 + '0'; byte /= 10; if (byte > 0) { *--cp = byte % 10 + '0'; byte /= 10; if (byte > 0) *--cp = byte + '0'; } *--cp = '.'; addr >>= 8; } while (--n > 0); return (cp+1); } static char * number(char *s, n_long *n) { for (*n = 0; isdigit(*s); s++) *n = (*n * 10) + *s - '0'; return s; } n_long ip_convertaddr(char *p) { #define IP_ANYADDR 0 n_long addr = 0, n; if (p == NULL || *p == '\0') return IP_ANYADDR; p = number(p, &n); addr |= (n << 24) & 0xff000000; if (*p == '\0' || *p++ != '.') return IP_ANYADDR; p = number(p, &n); addr |= (n << 16) & 0xff0000; if (*p == '\0' || *p++ != '.') return IP_ANYADDR; p = number(p, &n); addr |= (n << 8) & 0xff00; if (*p == '\0' || *p++ != '.') return IP_ANYADDR; p = number(p, &n); addr |= n & 0xff; if (*p != '\0') return IP_ANYADDR; return htonl(addr); } diff --git a/stand/libsa/netif.c b/stand/libsa/netif.c index 30e1528bdfea..d8398b861af0 100644 --- a/stand/libsa/netif.c +++ b/stand/libsa/netif.c @@ -1,384 +1,383 @@ /* $NetBSD: netif.c,v 1.10 1997/09/06 13:57:14 drochner Exp $ */ /* * Copyright (c) 1993 Adam Glass * 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 Adam Glass. * 4. The name of the Author may not be used to endorse or promote products * derived from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY Adam Glass ``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 #include #include #include #include #include #include #include "stand.h" #include "net.h" #include "netif.h" typedef TAILQ_HEAD(socket_list, iodesc) socket_list_t; /* * Open socket list. The current implementation and assumption is, * we only remove entries from tail and we only add new entries to tail. * This decision is to keep iodesc id management simple - we get list * entries ordered by continiously growing io_id field. * If we do have multiple sockets open and we do close socket not from tail, * this entry will be marked unused. netif_open() will reuse unused entry, or * netif_close() will free all unused tail entries. */ static socket_list_t sockets = TAILQ_HEAD_INITIALIZER(sockets); #ifdef NETIF_DEBUG int netif_debug = 0; #endif /* * netif_init: * * initialize the generic network interface layer */ void netif_init(void) { struct netif_driver *drv; int d, i; #ifdef NETIF_DEBUG if (netif_debug) printf("netif_init: called\n"); #endif for (d = 0; netif_drivers[d]; d++) { drv = netif_drivers[d]; for (i = 0; i < drv->netif_nifs; i++) drv->netif_ifs[i].dif_used = 0; } } int netif_match(struct netif *nif, void *machdep_hint) { struct netif_driver *drv = nif->nif_driver; #ifdef NETIF_DEBUG if (netif_debug) printf("%s%d: netif_match (%d)\n", drv->netif_bname, nif->nif_unit, nif->nif_sel); #endif return drv->netif_match(nif, machdep_hint); } struct netif * netif_select(void *machdep_hint) { int d, u, s; struct netif_driver *drv; struct netif cur_if; static struct netif best_if; int best_val; int val; best_val = 0; best_if.nif_driver = NULL; for (d = 0; netif_drivers[d] != NULL; d++) { cur_if.nif_driver = netif_drivers[d]; drv = cur_if.nif_driver; for (u = 0; u < drv->netif_nifs; u++) { cur_if.nif_unit = u; #ifdef NETIF_DEBUG if (netif_debug) printf("\t%s%d:", drv->netif_bname, cur_if.nif_unit); #endif for (s = 0; s < drv->netif_ifs[u].dif_nsel; s++) { cur_if.nif_sel = s; if (drv->netif_ifs[u].dif_used & (1 << s)) { #ifdef NETIF_DEBUG if (netif_debug) printf(" [%d used]", s); #endif continue; } val = netif_match(&cur_if, machdep_hint); #ifdef NETIF_DEBUG if (netif_debug) printf(" [%d -> %d]", s, val); #endif if (val > best_val) { best_val = val; best_if = cur_if; } } #ifdef NETIF_DEBUG if (netif_debug) printf("\n"); #endif } } if (best_if.nif_driver == NULL) return NULL; best_if.nif_driver-> netif_ifs[best_if.nif_unit].dif_used |= (1 << best_if.nif_sel); #ifdef NETIF_DEBUG if (netif_debug) printf("netif_select: %s%d(%d) wins\n", best_if.nif_driver->netif_bname, best_if.nif_unit, best_if.nif_sel); #endif return &best_if; } int netif_probe(struct netif *nif, void *machdep_hint) { struct netif_driver *drv = nif->nif_driver; #ifdef NETIF_DEBUG if (netif_debug) printf("%s%d: netif_probe\n", drv->netif_bname, nif->nif_unit); #endif return drv->netif_probe(nif, machdep_hint); } void netif_attach(struct netif *nif, struct iodesc *desc, void *machdep_hint) { struct netif_driver *drv = nif->nif_driver; #ifdef NETIF_DEBUG if (netif_debug) printf("%s%d: netif_attach\n", drv->netif_bname, nif->nif_unit); #endif desc->io_netif = nif; #ifdef PARANOID if (drv->netif_init == NULL) panic("%s%d: no netif_init support", drv->netif_bname, nif->nif_unit); #endif drv->netif_init(desc, machdep_hint); bzero(drv->netif_ifs[nif->nif_unit].dif_stats, sizeof(struct netif_stats)); } void netif_detach(struct netif *nif) { struct netif_driver *drv = nif->nif_driver; #ifdef NETIF_DEBUG if (netif_debug) printf("%s%d: netif_detach\n", drv->netif_bname, nif->nif_unit); #endif #ifdef PARANOID if (drv->netif_end == NULL) panic("%s%d: no netif_end support", drv->netif_bname, nif->nif_unit); #endif drv->netif_end(nif); } ssize_t netif_get(struct iodesc *desc, void **pkt, time_t timo) { #ifdef NETIF_DEBUG struct netif *nif = desc->io_netif; #endif struct netif_driver *drv = desc->io_netif->nif_driver; ssize_t rv; #ifdef NETIF_DEBUG if (netif_debug) printf("%s%d: netif_get\n", drv->netif_bname, nif->nif_unit); #endif #ifdef PARANOID if (drv->netif_get == NULL) panic("%s%d: no netif_get support", drv->netif_bname, nif->nif_unit); #endif rv = drv->netif_get(desc, pkt, timo); #ifdef NETIF_DEBUG if (netif_debug) printf("%s%d: netif_get returning %d\n", drv->netif_bname, nif->nif_unit, (int)rv); #endif return (rv); } ssize_t netif_put(struct iodesc *desc, void *pkt, size_t len) { #ifdef NETIF_DEBUG struct netif *nif = desc->io_netif; #endif struct netif_driver *drv = desc->io_netif->nif_driver; ssize_t rv; #ifdef NETIF_DEBUG if (netif_debug) printf("%s%d: netif_put\n", drv->netif_bname, nif->nif_unit); #endif #ifdef PARANOID if (drv->netif_put == NULL) panic("%s%d: no netif_put support", drv->netif_bname, nif->nif_unit); #endif rv = drv->netif_put(desc, pkt, len); #ifdef NETIF_DEBUG if (netif_debug) printf("%s%d: netif_put returning %d\n", drv->netif_bname, nif->nif_unit, (int)rv); #endif return (rv); } /* * socktodesc_impl: * * Walk socket list and return pointer to iodesc structure. * if id is < 0, return first unused iodesc. */ static struct iodesc * socktodesc_impl(int socket) { struct iodesc *s; TAILQ_FOREACH(s, &sockets, io_link) { /* search by socket id */ if (socket >= 0) { if (s->io_id == socket) break; continue; } /* search for first unused entry */ if (s->io_netif == NULL) break; } return (s); } struct iodesc * socktodesc(int sock) { struct iodesc *desc; if (sock < 0) desc = NULL; else desc = socktodesc_impl(sock); if (desc == NULL) errno = EBADF; return (desc); } int netif_open(void *machdep_hint) { struct iodesc *s; struct netif *nif; /* find a free socket */ s = socktodesc_impl(-1); if (s == NULL) { struct iodesc *last; s = calloc(1, sizeof (*s)); if (s == NULL) return (-1); last = TAILQ_LAST(&sockets, socket_list); if (last != NULL) s->io_id = last->io_id + 1; TAILQ_INSERT_TAIL(&sockets, s, io_link); } netif_init(); nif = netif_select(machdep_hint); if (!nif) panic("netboot: no interfaces left untried"); if (netif_probe(nif, machdep_hint)) { printf("netboot: couldn't probe %s%d\n", nif->nif_driver->netif_bname, nif->nif_unit); errno = EINVAL; return (-1); } netif_attach(nif, s, machdep_hint); return (s->io_id); } int netif_close(int sock) { struct iodesc *s, *last; int err; err = 0; s = socktodesc_impl(sock); if (s == NULL || sock < 0) { err = EBADF; return (-1); } netif_detach(s->io_netif); bzero(&s->destip, sizeof (s->destip)); bzero(&s->myip, sizeof (s->myip)); s->destport = 0; s->myport = 0; s->xid = 0; bzero(s->myea, sizeof (s->myea)); s->io_netif = NULL; /* free unused entries from tail. */ TAILQ_FOREACH_REVERSE_SAFE(last, &sockets, socket_list, io_link, s) { if (last->io_netif != NULL) break; TAILQ_REMOVE(&sockets, last, io_link); free(last); } if (err) { errno = err; return (-1); } return (0); } diff --git a/stand/libsa/nfs.c b/stand/libsa/nfs.c index 1cc8fb009944..ee6af8a726c7 100644 --- a/stand/libsa/nfs.c +++ b/stand/libsa/nfs.c @@ -1,850 +1,849 @@ /* $NetBSD: nfs.c,v 1.2 1998/01/24 12:43:09 drochner Exp $ */ /*- * Copyright (c) 1993 John Brezak * 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. The name of the author may not be used to endorse or promote products * derived from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR `AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ -#include #include #include #include #include #include #include #include #include #include "rpcv2.h" #include "nfsv2.h" #include "stand.h" #include "net.h" #include "netif.h" #include "rpc.h" #define NFS_DEBUGxx #define NFSREAD_MIN_SIZE 1024 #define NFSREAD_MAX_SIZE 16384 /* NFSv3 definitions */ #define NFS_V3MAXFHSIZE 64 #define NFS_VER3 3 #define RPCMNT_VER3 3 #define NFSPROCV3_LOOKUP 3 #define NFSPROCV3_READLINK 5 #define NFSPROCV3_READ 6 #define NFSPROCV3_READDIR 16 typedef struct { uint32_t val[2]; } n_quad; struct nfsv3_time { uint32_t nfs_sec; uint32_t nfs_nsec; }; struct nfsv3_fattrs { uint32_t fa_type; uint32_t fa_mode; uint32_t fa_nlink; uint32_t fa_uid; uint32_t fa_gid; n_quad fa_size; n_quad fa_used; n_quad fa_rdev; n_quad fa_fsid; n_quad fa_fileid; struct nfsv3_time fa_atime; struct nfsv3_time fa_mtime; struct nfsv3_time fa_ctime; }; /* * For NFSv3, the file handle is variable in size, so most fixed sized * structures for arguments won't work. For most cases, a structure * that starts with any fixed size section is followed by an array * that covers the maximum size required. */ struct nfsv3_readdir_repl { uint32_t errno; uint32_t ok; struct nfsv3_fattrs fa; uint32_t cookiev0; uint32_t cookiev1; }; struct nfsv3_readdir_entry { uint32_t follows; uint32_t fid0; uint32_t fid1; uint32_t len; uint32_t nameplus[0]; }; struct nfs_iodesc { struct iodesc *iodesc; off_t off; uint32_t fhsize; u_char fh[NFS_V3MAXFHSIZE]; struct nfsv3_fattrs fa; /* all in network order */ uint64_t cookie; }; /* * XXX interactions with tftp? See nfswrapper.c for a confusing * issue. */ int nfs_open(const char *path, struct open_file *f); static int nfs_close(struct open_file *f); static int nfs_read(struct open_file *f, void *buf, size_t size, size_t *resid); static off_t nfs_seek(struct open_file *f, off_t offset, int where); static int nfs_stat(struct open_file *f, struct stat *sb); static int nfs_readdir(struct open_file *f, struct dirent *d); struct nfs_iodesc nfs_root_node; struct fs_ops nfs_fsops = { .fs_name = "nfs", .fo_open = nfs_open, .fo_close = nfs_close, .fo_read = nfs_read, .fo_write = null_write, .fo_seek = nfs_seek, .fo_stat = nfs_stat, .fo_readdir = nfs_readdir, }; static int nfs_read_size = NFSREAD_MIN_SIZE; /* * Improve boot performance over NFS */ static void set_nfs_read_size(void) { char *env, *end; char buf[10]; if ((env = getenv("nfs.read_size")) != NULL) { errno = 0; nfs_read_size = (int)strtol(env, &end, 0); if (errno != 0 || *env == '\0' || *end != '\0') { printf("%s: bad value: \"%s\", defaulting to %d\n", "nfs.read_size", env, NFSREAD_MIN_SIZE); nfs_read_size = NFSREAD_MIN_SIZE; } } if (nfs_read_size < NFSREAD_MIN_SIZE) { printf("%s: bad value: \"%d\", defaulting to %d\n", "nfs.read_size", nfs_read_size, NFSREAD_MIN_SIZE); nfs_read_size = NFSREAD_MIN_SIZE; } if (nfs_read_size > NFSREAD_MAX_SIZE) { printf("%s: bad value: \"%d\", defaulting to %d\n", "nfs.read_size", nfs_read_size, NFSREAD_MIN_SIZE); nfs_read_size = NFSREAD_MAX_SIZE; } snprintf(buf, sizeof (buf), "%d", nfs_read_size); setenv("nfs.read_size", buf, 1); } /* * Fetch the root file handle (call mount daemon) * Return zero or error number. */ int nfs_getrootfh(struct iodesc *d, char *path, uint32_t *fhlenp, u_char *fhp) { void *pkt = NULL; int len; struct args { uint32_t len; char path[FNAME_SIZE]; } *args; struct repl { uint32_t errno; uint32_t fhsize; u_char fh[NFS_V3MAXFHSIZE]; uint32_t authcnt; uint32_t auth[7]; } *repl; struct { uint32_t h[RPC_HEADER_WORDS]; struct args d; } sdata; size_t cc; #ifdef NFS_DEBUG if (debug) printf("nfs_getrootfh: %s\n", path); #endif args = &sdata.d; bzero(args, sizeof(*args)); len = strlen(path); if (len > sizeof(args->path)) len = sizeof(args->path); args->len = htonl(len); bcopy(path, args->path, len); len = sizeof(uint32_t) + roundup(len, sizeof(uint32_t)); cc = rpc_call(d, RPCPROG_MNT, RPCMNT_VER3, RPCMNT_MOUNT, args, len, (void **)&repl, &pkt); if (cc == -1) { free(pkt); /* errno was set by rpc_call */ return (errno); } if (cc < 2 * sizeof (uint32_t)) { free(pkt); return (EBADRPC); } if (repl->errno != 0) { free(pkt); return (ntohl(repl->errno)); } *fhlenp = ntohl(repl->fhsize); bcopy(repl->fh, fhp, *fhlenp); set_nfs_read_size(); free(pkt); return (0); } /* * Lookup a file. Store handle and attributes. * Return zero or error number. */ int nfs_lookupfh(struct nfs_iodesc *d, const char *name, struct nfs_iodesc *newfd) { void *pkt = NULL; int len, pos; struct args { uint32_t fhsize; uint32_t fhplusname[1 + (NFS_V3MAXFHSIZE + FNAME_SIZE) / sizeof(uint32_t)]; } *args; struct repl { uint32_t errno; uint32_t fhsize; uint32_t fhplusattr[(NFS_V3MAXFHSIZE + 2 * (sizeof(uint32_t) + sizeof(struct nfsv3_fattrs))) / sizeof(uint32_t)]; } *repl; struct { uint32_t h[RPC_HEADER_WORDS]; struct args d; } sdata; ssize_t cc; #ifdef NFS_DEBUG if (debug) printf("lookupfh: called\n"); #endif args = &sdata.d; bzero(args, sizeof(*args)); args->fhsize = htonl(d->fhsize); bcopy(d->fh, args->fhplusname, d->fhsize); len = strlen(name); if (len > FNAME_SIZE) len = FNAME_SIZE; pos = roundup(d->fhsize, sizeof(uint32_t)) / sizeof(uint32_t); args->fhplusname[pos++] = htonl(len); bcopy(name, &args->fhplusname[pos], len); len = sizeof(uint32_t) + pos * sizeof(uint32_t) + roundup(len, sizeof(uint32_t)); cc = rpc_call(d->iodesc, NFS_PROG, NFS_VER3, NFSPROCV3_LOOKUP, args, len, (void **)&repl, &pkt); if (cc == -1) { free(pkt); return (errno); /* XXX - from rpc_call */ } if (cc < 2 * sizeof(uint32_t)) { free(pkt); return (EIO); } if (repl->errno != 0) { free(pkt); /* saerrno.h now matches NFS error numbers. */ return (ntohl(repl->errno)); } newfd->fhsize = ntohl(repl->fhsize); bcopy(repl->fhplusattr, &newfd->fh, newfd->fhsize); pos = roundup(newfd->fhsize, sizeof(uint32_t)) / sizeof(uint32_t); if (repl->fhplusattr[pos++] == 0) { free(pkt); return (EIO); } bcopy(&repl->fhplusattr[pos], &newfd->fa, sizeof(newfd->fa)); free(pkt); return (0); } #ifndef NFS_NOSYMLINK /* * Get the destination of a symbolic link. */ int nfs_readlink(struct nfs_iodesc *d, char *buf) { void *pkt = NULL; struct args { uint32_t fhsize; u_char fh[NFS_V3MAXFHSIZE]; } *args; struct repl { uint32_t errno; uint32_t ok; struct nfsv3_fattrs fa; uint32_t len; u_char path[NFS_MAXPATHLEN]; } *repl; struct { uint32_t h[RPC_HEADER_WORDS]; struct args d; } sdata; ssize_t cc; int rc = 0; #ifdef NFS_DEBUG if (debug) printf("readlink: called\n"); #endif args = &sdata.d; bzero(args, sizeof(*args)); args->fhsize = htonl(d->fhsize); bcopy(d->fh, args->fh, d->fhsize); cc = rpc_call(d->iodesc, NFS_PROG, NFS_VER3, NFSPROCV3_READLINK, args, sizeof(uint32_t) + roundup(d->fhsize, sizeof(uint32_t)), (void **)&repl, &pkt); if (cc == -1) return (errno); if (cc < 2 * sizeof(uint32_t)) { rc = EIO; goto done; } if (repl->errno != 0) { rc = ntohl(repl->errno); goto done; } if (repl->ok == 0) { rc = EIO; goto done; } repl->len = ntohl(repl->len); if (repl->len > NFS_MAXPATHLEN) { rc = ENAMETOOLONG; goto done; } bcopy(repl->path, buf, repl->len); buf[repl->len] = 0; done: free(pkt); return (rc); } #endif /* * Read data from a file. * Return transfer count or -1 (and set errno) */ ssize_t nfs_readdata(struct nfs_iodesc *d, off_t off, void *addr, size_t len) { void *pkt = NULL; struct args { uint32_t fhsize; uint32_t fhoffcnt[NFS_V3MAXFHSIZE / sizeof(uint32_t) + 3]; } *args; struct repl { uint32_t errno; uint32_t ok; struct nfsv3_fattrs fa; uint32_t count; uint32_t eof; uint32_t len; u_char data[NFSREAD_MAX_SIZE]; } *repl; struct { uint32_t h[RPC_HEADER_WORDS]; struct args d; } sdata; size_t cc; long x; int hlen, rlen, pos; args = &sdata.d; bzero(args, sizeof(*args)); args->fhsize = htonl(d->fhsize); bcopy(d->fh, args->fhoffcnt, d->fhsize); pos = roundup(d->fhsize, sizeof(uint32_t)) / sizeof(uint32_t); args->fhoffcnt[pos++] = 0; args->fhoffcnt[pos++] = htonl((uint32_t)off); if (len > nfs_read_size) len = nfs_read_size; args->fhoffcnt[pos] = htonl((uint32_t)len); hlen = offsetof(struct repl, data[0]); cc = rpc_call(d->iodesc, NFS_PROG, NFS_VER3, NFSPROCV3_READ, args, 4 * sizeof(uint32_t) + roundup(d->fhsize, sizeof(uint32_t)), (void **)&repl, &pkt); if (cc == -1) { /* errno was already set by rpc_call */ return (-1); } if (cc < hlen) { errno = EBADRPC; free(pkt); return (-1); } if (repl->errno != 0) { errno = ntohl(repl->errno); free(pkt); return (-1); } rlen = cc - hlen; x = ntohl(repl->count); if (rlen < x) { printf("nfsread: short packet, %d < %ld\n", rlen, x); errno = EBADRPC; free(pkt); return (-1); } bcopy(repl->data, addr, x); free(pkt); return (x); } /* * Open a file. * return zero or error number */ int nfs_open(const char *upath, struct open_file *f) { struct devdesc *dev; struct iodesc *desc; struct nfs_iodesc *currfd = NULL; char buf[2 * NFS_V3MAXFHSIZE + 3]; u_char *fh; char *cp; int i; #ifndef NFS_NOSYMLINK struct nfs_iodesc *newfd = NULL; char *ncp; int c; char namebuf[NFS_MAXPATHLEN + 1]; char linkbuf[NFS_MAXPATHLEN + 1]; int nlinks = 0; #endif int error; char *path = NULL; if (netproto != NET_NFS) return (EINVAL); dev = f->f_devdata; #ifdef NFS_DEBUG if (debug) printf("nfs_open: %s (rootip=%s rootpath=%s)\n", upath, inet_ntoa(rootip), rootpath); #endif if (!rootpath[0]) { printf("no rootpath, no nfs\n"); return (ENXIO); } if (f->f_dev->dv_type != DEVT_NET) return (EINVAL); if (!(desc = socktodesc(*(int *)(dev->d_opendata)))) return (EINVAL); /* Bind to a reserved port. */ desc->myport = htons(--rpc_port); desc->destip = rootip; if ((error = nfs_getrootfh(desc, rootpath, &nfs_root_node.fhsize, nfs_root_node.fh))) return (error); nfs_root_node.fa.fa_type = htonl(NFDIR); nfs_root_node.fa.fa_mode = htonl(0755); nfs_root_node.fa.fa_nlink = htonl(2); nfs_root_node.iodesc = desc; fh = &nfs_root_node.fh[0]; buf[0] = 'X'; cp = &buf[1]; for (i = 0; i < nfs_root_node.fhsize; i++, cp += 2) sprintf(cp, "%02x", fh[i]); sprintf(cp, "X"); setenv("boot.nfsroot.server", inet_ntoa(rootip), 1); setenv("boot.nfsroot.path", rootpath, 1); setenv("boot.nfsroot.nfshandle", buf, 1); sprintf(buf, "%d", nfs_root_node.fhsize); setenv("boot.nfsroot.nfshandlelen", buf, 1); /* Allocate file system specific data structure */ currfd = malloc(sizeof(*newfd)); if (currfd == NULL) { error = ENOMEM; goto out; } #ifndef NFS_NOSYMLINK bcopy(&nfs_root_node, currfd, sizeof(*currfd)); newfd = NULL; cp = path = strdup(upath); if (path == NULL) { error = ENOMEM; goto out; } while (*cp) { /* * Remove extra separators */ while (*cp == '/') cp++; if (*cp == '\0') break; /* * Check that current node is a directory. */ if (currfd->fa.fa_type != htonl(NFDIR)) { error = ENOTDIR; goto out; } /* allocate file system specific data structure */ newfd = malloc(sizeof(*newfd)); if (newfd == NULL) { error = ENOMEM; goto out; } newfd->iodesc = currfd->iodesc; /* * Get next component of path name. */ { int len = 0; ncp = cp; while ((c = *cp) != '\0' && c != '/') { if (++len > NFS_MAXNAMLEN) { error = ENOENT; goto out; } cp++; } *cp = '\0'; } /* lookup a file handle */ error = nfs_lookupfh(currfd, ncp, newfd); *cp = c; if (error) goto out; /* * Check for symbolic link */ if (newfd->fa.fa_type == htonl(NFLNK)) { int link_len, len; error = nfs_readlink(newfd, linkbuf); if (error) goto out; link_len = strlen(linkbuf); len = strlen(cp); if (link_len + len > MAXPATHLEN || ++nlinks > MAXSYMLINKS) { error = ENOENT; goto out; } bcopy(cp, &namebuf[link_len], len + 1); bcopy(linkbuf, namebuf, link_len); /* * If absolute pathname, restart at root. * If relative pathname, restart at parent directory. */ cp = namebuf; if (*cp == '/') bcopy(&nfs_root_node, currfd, sizeof(*currfd)); free(newfd); newfd = NULL; continue; } free(currfd); currfd = newfd; newfd = NULL; } error = 0; out: free(newfd); free(path); #else currfd->iodesc = desc; error = nfs_lookupfh(&nfs_root_node, upath, currfd); #endif if (!error) { currfd->off = 0; currfd->cookie = 0; f->f_fsdata = (void *)currfd; return (0); } #ifdef NFS_DEBUG if (debug) printf("nfs_open: %s lookupfh failed: %s\n", path, strerror(error)); #endif free(currfd); return (error); } int nfs_close(struct open_file *f) { struct nfs_iodesc *fp = (struct nfs_iodesc *)f->f_fsdata; #ifdef NFS_DEBUG if (debug) printf("nfs_close: fp=0x%lx\n", (u_long)fp); #endif free(fp); f->f_fsdata = NULL; return (0); } /* * read a portion of a file */ int nfs_read(struct open_file *f, void *buf, size_t size, size_t *resid) { struct nfs_iodesc *fp = (struct nfs_iodesc *)f->f_fsdata; ssize_t cc; char *addr = buf; #ifdef NFS_DEBUG if (debug) printf("nfs_read: size=%lu off=%d\n", (u_long)size, (int)fp->off); #endif while ((int)size > 0) { twiddle(16); cc = nfs_readdata(fp, fp->off, (void *)addr, size); /* XXX maybe should retry on certain errors */ if (cc == -1) { #ifdef NFS_DEBUG if (debug) printf("nfs_read: read: %s\n", strerror(errno)); #endif return (errno); /* XXX - from nfs_readdata */ } if (cc == 0) { #ifdef NFS_DEBUG if (debug) printf("nfs_read: hit EOF unexpectedly\n"); #endif goto ret; } fp->off += cc; addr += cc; size -= cc; } ret: if (resid) *resid = size; return (0); } off_t nfs_seek(struct open_file *f, off_t offset, int where) { struct nfs_iodesc *d = (struct nfs_iodesc *)f->f_fsdata; uint32_t size = ntohl(d->fa.fa_size.val[1]); switch (where) { case SEEK_SET: d->off = offset; break; case SEEK_CUR: d->off += offset; break; case SEEK_END: d->off = size - offset; break; default: errno = EINVAL; return (-1); } return (d->off); } /* NFNON=0, NFREG=1, NFDIR=2, NFBLK=3, NFCHR=4, NFLNK=5, NFSOCK=6, NFFIFO=7 */ int nfs_stat_types[9] = { 0, S_IFREG, S_IFDIR, S_IFBLK, S_IFCHR, S_IFLNK, S_IFSOCK, S_IFIFO, 0 }; int nfs_stat(struct open_file *f, struct stat *sb) { struct nfs_iodesc *fp = (struct nfs_iodesc *)f->f_fsdata; uint32_t ftype, mode; ftype = ntohl(fp->fa.fa_type); mode = ntohl(fp->fa.fa_mode); mode |= nfs_stat_types[ftype & 7]; sb->st_mode = mode; sb->st_nlink = ntohl(fp->fa.fa_nlink); sb->st_uid = ntohl(fp->fa.fa_uid); sb->st_gid = ntohl(fp->fa.fa_gid); sb->st_size = ntohl(fp->fa.fa_size.val[1]); return (0); } static int nfs_readdir(struct open_file *f, struct dirent *d) { struct nfs_iodesc *fp = (struct nfs_iodesc *)f->f_fsdata; struct nfsv3_readdir_repl *repl; struct nfsv3_readdir_entry *rent; static void *pkt = NULL; static char *buf; static struct nfs_iodesc *pfp = NULL; static uint64_t cookie = 0; size_t cc; int pos, rc; struct args { uint32_t fhsize; uint32_t fhpluscookie[5 + NFS_V3MAXFHSIZE]; } *args; struct { uint32_t h[RPC_HEADER_WORDS]; struct args d; } sdata; if (fp != pfp || fp->off != cookie) { pfp = NULL; refill: free(pkt); pkt = NULL; args = &sdata.d; bzero(args, sizeof(*args)); args->fhsize = htonl(fp->fhsize); bcopy(fp->fh, args->fhpluscookie, fp->fhsize); pos = roundup(fp->fhsize, sizeof(uint32_t)) / sizeof(uint32_t); args->fhpluscookie[pos++] = htonl(fp->off >> 32); args->fhpluscookie[pos++] = htonl(fp->off); args->fhpluscookie[pos++] = htonl(fp->cookie >> 32); args->fhpluscookie[pos++] = htonl(fp->cookie); args->fhpluscookie[pos] = htonl(NFS_READDIRSIZE); cc = rpc_call(fp->iodesc, NFS_PROG, NFS_VER3, NFSPROCV3_READDIR, args, 6 * sizeof(uint32_t) + roundup(fp->fhsize, sizeof(uint32_t)), (void **)&buf, &pkt); if (cc == -1) { rc = errno; goto err; } repl = (struct nfsv3_readdir_repl *)buf; if (repl->errno != 0) { rc = ntohl(repl->errno); goto err; } pfp = fp; cookie = fp->off; fp->cookie = ((uint64_t)ntohl(repl->cookiev0) << 32) | ntohl(repl->cookiev1); buf += sizeof (struct nfsv3_readdir_repl); } rent = (struct nfsv3_readdir_entry *)buf; if (rent->follows == 0) { /* fid0 is actually eof */ if (rent->fid0 != 0) { rc = ENOENT; goto err; } goto refill; } d->d_namlen = ntohl(rent->len); bcopy(rent->nameplus, d->d_name, d->d_namlen); d->d_name[d->d_namlen] = '\0'; pos = roundup(d->d_namlen, sizeof(uint32_t)) / sizeof(uint32_t); fp->off = cookie = ((uint64_t)ntohl(rent->nameplus[pos]) << 32) | ntohl(rent->nameplus[pos + 1]); pos += 2; buf = (char *)&rent->nameplus[pos]; return (0); err: free(pkt); pkt = NULL; pfp = NULL; cookie = 0; return (rc); } diff --git a/stand/libsa/preload.c b/stand/libsa/preload.c index 9ca80e2c6ab2..499075c0efac 100644 --- a/stand/libsa/preload.c +++ b/stand/libsa/preload.c @@ -1,43 +1,42 @@ /*- * Copyright (c) 2021 Beckhoff Automation GmbH & Co. KG * * 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 #include "stand.h" void preload(int fd) { struct open_file *f; f = fd2open_file(fd); if (f == NULL) { errno = EBADF; return; } if (f->f_ops->fo_preload) (f->f_ops->fo_preload)(f); } diff --git a/stand/libsa/random.c b/stand/libsa/random.c index a6d903ed2e02..d17d636e1597 100644 --- a/stand/libsa/random.c +++ b/stand/libsa/random.c @@ -1,66 +1,65 @@ /*- * Copyright (c) 1992, 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. */ -#include #include static long randseed = 1; void srandom(unsigned int seed) { randseed = seed; } /* * Pseudo-random number generator for randomizing the profiling clock, * and whatever else we might use it for. The result is uniform on * [0, 2^31 - 1]. */ long random(void) { long x, hi, lo, t; /* * Compute x[n + 1] = (7^5 * x[n]) mod (2^31 - 1). * From "Random number generators: good ones are hard to find", * Park and Miller, Communications of the ACM, vol. 31, no. 10, * October 1988, p. 1195. */ x = randseed; hi = x / 127773; lo = x % 127773; t = 16807 * lo - 2836 * hi; if (t <= 0) t += 0x7fffffff; randseed = t; return (t); } diff --git a/stand/libsa/rarp.c b/stand/libsa/rarp.c index 9c5daad5cd42..20bbd56de6e6 100644 --- a/stand/libsa/rarp.c +++ b/stand/libsa/rarp.c @@ -1,215 +1,214 @@ /* $NetBSD: rarp.c,v 1.16 1997/07/07 15:52:52 drochner Exp $ */ /* * Copyright (c) 1992 Regents of the University of California. * All rights reserved. * * This software was developed by the Computer Systems Engineering group * at Lawrence Berkeley Laboratory under DARPA contract BG 91-66 and * contributed to Berkeley. * * 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. */ -#include #include #include #include #include #include #include #include #include "stand.h" #include "net.h" #include "netif.h" static ssize_t rarpsend(struct iodesc *, void *, size_t); static ssize_t rarprecv(struct iodesc *, void **, void **, time_t, void *); /* * Ethernet (Reverse) Address Resolution Protocol (see RFC 903, and 826). */ int rarp_getipaddress(int sock) { struct iodesc *d; struct ether_arp *ap; void *pkt; struct { u_char header[ETHER_SIZE]; struct { struct ether_arp arp; u_char pad[18]; /* 60 - sizeof(arp) */ } data; } wbuf; #ifdef RARP_DEBUG if (debug) printf("rarp: socket=%d\n", sock); #endif if (!(d = socktodesc(sock))) { printf("rarp: bad socket. %d\n", sock); return (-1); } #ifdef RARP_DEBUG if (debug) printf("rarp: d=%lx\n", (long)d); #endif bzero((char*)&wbuf.data, sizeof(wbuf.data)); ap = &wbuf.data.arp; ap->arp_hrd = htons(ARPHRD_ETHER); ap->arp_pro = htons(ETHERTYPE_IP); ap->arp_hln = sizeof(ap->arp_sha); /* hardware address length */ ap->arp_pln = sizeof(ap->arp_spa); /* protocol address length */ ap->arp_op = htons(ARPOP_REVREQUEST); bcopy(d->myea, ap->arp_sha, 6); bcopy(d->myea, ap->arp_tha, 6); pkt = NULL; if (sendrecv(d, rarpsend, &wbuf.data, sizeof(wbuf.data), rarprecv, &pkt, (void *)&ap, NULL) < 0) { printf("No response for RARP request\n"); return (-1); } bcopy(ap->arp_tpa, (char *)&myip, sizeof(myip)); #if 0 /* XXX - Can NOT assume this is our root server! */ bcopy(ap->arp_spa, (char *)&rootip, sizeof(rootip)); #endif free(pkt); /* Compute our "natural" netmask. */ if (IN_CLASSA(myip.s_addr)) netmask = IN_CLASSA_NET; else if (IN_CLASSB(myip.s_addr)) netmask = IN_CLASSB_NET; else netmask = IN_CLASSC_NET; d->myip = myip; return (0); } /* * Broadcast a RARP request (i.e. who knows who I am) */ static ssize_t rarpsend(struct iodesc *d, void *pkt, size_t len) { #ifdef RARP_DEBUG if (debug) printf("rarpsend: called\n"); #endif return (sendether(d, pkt, len, bcea, ETHERTYPE_REVARP)); } /* * Returns 0 if this is the packet we're waiting for * else -1 (and errno == 0) */ static ssize_t rarprecv(struct iodesc *d, void **pkt, void **payload, time_t tleft, void *extra) { ssize_t n; struct ether_arp *ap; void *ptr = NULL; uint16_t etype; /* host order */ #ifdef RARP_DEBUG if (debug) printf("rarprecv: "); #endif n = readether(d, &ptr, (void **)&ap, tleft, &etype); errno = 0; /* XXX */ if (n == -1 || n < sizeof(struct ether_arp)) { #ifdef RARP_DEBUG if (debug) printf("bad len=%zd\n", n); #endif free(ptr); return (-1); } if (etype != ETHERTYPE_REVARP) { #ifdef RARP_DEBUG if (debug) printf("bad type=0x%x\n", etype); #endif free(ptr); return (-1); } if (ap->arp_hrd != htons(ARPHRD_ETHER) || ap->arp_pro != htons(ETHERTYPE_IP) || ap->arp_hln != sizeof(ap->arp_sha) || ap->arp_pln != sizeof(ap->arp_spa) ) { #ifdef RARP_DEBUG if (debug) printf("bad hrd/pro/hln/pln\n"); #endif free(ptr); return (-1); } if (ap->arp_op != htons(ARPOP_REVREPLY)) { #ifdef RARP_DEBUG if (debug) printf("bad op=0x%x\n", ntohs(ap->arp_op)); #endif free(ptr); return (-1); } /* Is the reply for our Ethernet address? */ if (bcmp(ap->arp_tha, d->myea, 6)) { #ifdef RARP_DEBUG if (debug) printf("unwanted address\n"); #endif free(ptr); return (-1); } /* We have our answer. */ #ifdef RARP_DEBUG if (debug) printf("got it\n"); #endif *pkt = ptr; *payload = ap; return (n); } diff --git a/stand/libsa/read.c b/stand/libsa/read.c index df150f8f0fbe..e610f8a0779c 100644 --- a/stand/libsa/read.c +++ b/stand/libsa/read.c @@ -1,138 +1,137 @@ /* $NetBSD: read.c,v 1.8 1997/01/22 00:38:12 cgd Exp $ */ /*- * Copyright (c) 1993 * The Regents of the University of California. All rights reserved. * * This code is derived from software contributed to Berkeley by * The Mach Operating System project at Carnegie-Mellon University. * * 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. * * * Copyright (c) 1989, 1990, 1991 Carnegie Mellon University * All Rights Reserved. * * Author: Alessandro Forin * * Permission to use, copy, modify and distribute this software and its * documentation is hereby granted, provided that both the copyright * notice and this permission notice appear in all copies of the * software, derivative works or modified versions, and any portions * thereof, and that both notices appear in supporting documentation. * * CARNEGIE MELLON ALLOWS FREE USE OF THIS SOFTWARE IN ITS "AS IS" * CONDITION. CARNEGIE MELLON DISCLAIMS ANY LIABILITY OF ANY KIND FOR * ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS SOFTWARE. * * Carnegie Mellon requests users of this software to return to * * Software Distribution Coordinator or Software.Distribution@CS.CMU.EDU * School of Computer Science * Carnegie Mellon University * Pittsburgh PA 15213-3890 * * any improvements or extensions that they make and grant Carnegie the * rights to redistribute these changes. */ -#include #include #include "stand.h" ssize_t read(int fd, void *dest, size_t bcount) { struct open_file *f; size_t resid; TSENTER(); f = fd2open_file(fd); if (f == NULL || !(f->f_flags & F_READ)) { errno = EBADF; return (-1); } if (f->f_flags & F_RAW) { twiddle(4); errno = (f->f_dev->dv_strategy)(f->f_devdata, F_READ, btodb(f->f_offset), bcount, dest, &resid); if (errno) return (-1); f->f_offset += resid; TSEXIT(); return (resid); } /* * Optimise reads from regular files using a readahead buffer. * If the request can't be satisfied from the current buffer contents, * check to see if it should be bypassed, or refill the buffer and * complete the request. */ resid = bcount; for (;;) { size_t ccount, cresid; /* how much can we supply? */ ccount = imin(f->f_ralen, resid); if (ccount > 0) { bcopy(f->f_rabuf + f->f_raoffset, dest, ccount); f->f_raoffset += ccount; f->f_ralen -= ccount; resid -= ccount; if (resid == 0) { TSEXIT(); return (bcount); } dest = (char *)dest + ccount; } /* will filling the readahead buffer again not help? */ if (f->f_rabuf == NULL || resid >= SOPEN_RASIZE) { /* * bypass the rest of the request and leave the * buffer empty */ errno = (f->f_ops->fo_read)(f, dest, resid, &cresid); if (errno != 0) return (-1); TSEXIT(); return (bcount - cresid); } /* fetch more data */ errno = (f->f_ops->fo_read)(f, f->f_rabuf, SOPEN_RASIZE, &cresid); if (errno != 0) return (-1); f->f_raoffset = 0; f->f_ralen = SOPEN_RASIZE - cresid; /* no more data, return what we had */ if (f->f_ralen == 0) { TSEXIT(); return (bcount - resid); } } } diff --git a/stand/libsa/readdir.c b/stand/libsa/readdir.c index 56c2b880d214..558ff99b265c 100644 --- a/stand/libsa/readdir.c +++ b/stand/libsa/readdir.c @@ -1,50 +1,49 @@ /*- * Copyright (c) 1999,2000 Jonathan Lemon * 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 #include "stand.h" struct dirent * readdirfd(int fd) { static struct dirent dir; /* XXX not thread safe */ struct open_file *f; f = fd2open_file(fd); if (f == NULL || !(f->f_flags & F_READ)) { errno = EBADF; return (NULL); } if (f->f_flags & F_RAW) { errno = EIO; return (NULL); } errno = (f->f_ops->fo_readdir)(f, &dir); if (errno) return (NULL); return (&dir); } diff --git a/stand/libsa/tslog.c b/stand/libsa/tslog.c index 4501746474ea..7a92ee6c23f3 100644 --- a/stand/libsa/tslog.c +++ b/stand/libsa/tslog.c @@ -1,118 +1,117 @@ /*- * Copyright (c) 2021 Colin Percival * 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 #if defined(__amd64__) || defined(__i386__) #include #elif defined(__aarch64__) #include #endif #include /* Buffer for holding tslog data in string format. */ static char * tslog_buf = NULL; static size_t tslog_buflen = 0; static size_t tslog_bufpos = 0; static size_t tsccat(char * buf, uint64_t tsc) { size_t len; /* Handle upper digits. */ if (tsc >= 10) len = tsccat(buf, tsc / 10); else len = 0; /* Write the last digit. */ buf[len] = "0123456789"[tsc % 10]; /* Return the length written. */ return (len + 1); } void tslog_setbuf(void * buf, size_t len) { tslog_buf = (char *)buf; tslog_buflen = len; tslog_bufpos = 0; } void tslog_getbuf(void ** buf, size_t * len) { *buf = (void *)tslog_buf; *len = tslog_bufpos; } void tslog(const char * type, const char * f, const char * s) { #if defined(__amd64__) || defined(__i386__) uint64_t tsc = rdtsc(); #elif defined(__aarch64__) uint64_t tsc = READ_SPECIALREG(cntvct_el0); #else uint64_t tsc = 0; #endif /* If we have no buffer, do nothing. */ if (tslog_buf == NULL) return; /* Check that we have enough space. */ if (tslog_buflen - tslog_bufpos < 32 + strlen(type) + strlen(f) + (s ? strlen(s) : 0)) return; /* Append to existing buffer. */ strcpy(&tslog_buf[tslog_bufpos], "0x0 "); tslog_bufpos += 4; tslog_bufpos += tsccat(&tslog_buf[tslog_bufpos], tsc); strcpy(&tslog_buf[tslog_bufpos], " "); tslog_bufpos += 1; strcpy(&tslog_buf[tslog_bufpos], type); tslog_bufpos += strlen(type); strcpy(&tslog_buf[tslog_bufpos], " "); tslog_bufpos += 1; strcpy(&tslog_buf[tslog_bufpos], f); tslog_bufpos += strlen(f); if (s != NULL) { strcpy(&tslog_buf[tslog_bufpos], " "); tslog_bufpos += 1; strcpy(&tslog_buf[tslog_bufpos], s); tslog_bufpos += strlen(s); } strcpy(&tslog_buf[tslog_bufpos], "\n"); tslog_bufpos += 1; } diff --git a/stand/libsa/twiddle.c b/stand/libsa/twiddle.c index 43cdca25a367..9ecfbeac3389 100644 --- a/stand/libsa/twiddle.c +++ b/stand/libsa/twiddle.c @@ -1,72 +1,71 @@ /*- * Copyright (c) 1986, 1988, 1991, 1993 * The Regents of the University of California. All rights reserved. * (c) UNIX System Laboratories, Inc. * All or some portions of this file are derived from material licensed * to the University of California by American Telephone and Telegraph * Co. or Unix System Laboratories, Inc. and are reproduced herein with * the permission of UNIX System Laboratories, Inc. * * 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. */ -#include #include #include "stand.h" /* Extra functions from NetBSD standalone printf.c */ static u_int globaldiv = 16; void twiddle(u_int callerdiv) { static u_int callercnt, globalcnt, pos; TSENTER(); callercnt++; if (callerdiv > 1 && (callercnt % callerdiv) != 0) { TSEXIT(); return; } globalcnt++; if (globaldiv > 1 && (globalcnt % globaldiv) != 0) { TSEXIT(); return; } putchar("|/-\\"[pos++ & 3]); putchar('\b'); TSEXIT(); } void twiddle_divisor(u_int gdiv) { globaldiv = gdiv; } diff --git a/stand/libsa/udp.c b/stand/libsa/udp.c index 7a5942bbef95..a0a662a19976 100644 --- a/stand/libsa/udp.c +++ b/stand/libsa/udp.c @@ -1,175 +1,174 @@ /* Taken from $NetBSD: net.c,v 1.20 1997/12/26 22:41:30 scottr Exp $ */ /* * Copyright (c) 1992 Regents of the University of California. * All rights reserved. * * This software was developed by the Computer Systems Engineering group * at Lawrence Berkeley Laboratory under DARPA contract BG 91-66 and * contributed to Berkeley. * * 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. */ -#include #include #include #include #include #include #include #include #include #include #include #include #include "stand.h" #include "net.h" /* Caller must leave room for ethernet, ip and udp headers in front!! */ ssize_t sendudp(struct iodesc *d, void *pkt, size_t len) { ssize_t cc; struct udpiphdr *ui; struct udphdr *uh; #ifdef NET_DEBUG if (debug) { printf("sendudp: d=%lx called.\n", (long)d); if (d) { printf("saddr: %s:%d", inet_ntoa(d->myip), ntohs(d->myport)); printf(" daddr: %s:%d\n", inet_ntoa(d->destip), ntohs(d->destport)); } } #endif ui = (struct udpiphdr *)pkt - 1; bzero(ui, sizeof(*ui)); uh = (struct udphdr *)pkt - 1; len += sizeof(*uh); uh->uh_sport = d->myport; uh->uh_dport = d->destport; uh->uh_ulen = htons(len); ui->ui_pr = IPPROTO_UDP; ui->ui_len = uh->uh_ulen; ui->ui_src = d->myip; ui->ui_dst = d->destip; #ifndef UDP_NO_CKSUM uh->uh_sum = in_cksum(ui, len + sizeof (struct ip)); #endif cc = sendip(d, uh, len, IPPROTO_UDP); if (cc == -1) return (-1); if (cc != len) panic("sendudp: bad write (%zd != %zd)", cc, len); return (cc - sizeof(*uh)); } /* * Receive a UDP packet and validate it is for us. */ ssize_t readudp(struct iodesc *d, void **pkt, void **payload, time_t tleft) { ssize_t n; struct udphdr *uh; void *ptr; #ifdef NET_DEBUG if (debug) printf("readudp: called\n"); #endif uh = NULL; ptr = NULL; n = readip(d, &ptr, (void **)&uh, tleft, IPPROTO_UDP); if (n == -1 || n < sizeof(*uh) || n != ntohs(uh->uh_ulen)) { free(ptr); return (-1); } if (uh->uh_dport != d->myport) { #ifdef NET_DEBUG if (debug) printf("readudp: bad dport %d != %d\n", d->myport, ntohs(uh->uh_dport)); #endif free(ptr); return (-1); } #ifndef UDP_NO_CKSUM if (uh->uh_sum) { struct udpiphdr *ui; struct ip *ip; struct ip tip; n = ntohs(uh->uh_ulen) + sizeof(*ip); /* Check checksum (must save and restore ip header) */ ip = (struct ip *)uh - 1; tip = *ip; ui = (struct udpiphdr *)ip; bzero(&ui->ui_x1, sizeof(ui->ui_x1)); ui->ui_len = uh->uh_ulen; if (in_cksum(ui, n) != 0) { #ifdef NET_DEBUG if (debug) printf("readudp: bad cksum\n"); #endif free(ptr); return (-1); } *ip = tip; } #endif if (ntohs(uh->uh_ulen) < sizeof(*uh)) { #ifdef NET_DEBUG if (debug) printf("readudp: bad udp len %d < %d\n", ntohs(uh->uh_ulen), (int)sizeof(*uh)); #endif free(ptr); return (-1); } n = (n > (ntohs(uh->uh_ulen) - sizeof(*uh))) ? ntohs(uh->uh_ulen) - sizeof(*uh) : n; *pkt = ptr; *payload = (void *)((uintptr_t)uh + sizeof(*uh)); return (n); } diff --git a/stand/libsa/write.c b/stand/libsa/write.c index 16a6fc7eaa60..06b9bfc63ac2 100644 --- a/stand/libsa/write.c +++ b/stand/libsa/write.c @@ -1,90 +1,89 @@ /* $NetBSD: write.c,v 1.7 1996/06/21 20:29:30 pk Exp $ */ /*- * Copyright (c) 1993 * The Regents of the University of California. All rights reserved. * * This code is derived from software contributed to Berkeley by * The Mach Operating System project at Carnegie-Mellon University. * * 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. * * * Copyright (c) 1989, 1990, 1991 Carnegie Mellon University * All Rights Reserved. * * Author: Alessandro Forin * * Permission to use, copy, modify and distribute this software and its * documentation is hereby granted, provided that both the copyright * notice and this permission notice appear in all copies of the * software, derivative works or modified versions, and any portions * thereof, and that both notices appear in supporting documentation. * * CARNEGIE MELLON ALLOWS FREE USE OF THIS SOFTWARE IN ITS "AS IS" * CONDITION. CARNEGIE MELLON DISCLAIMS ANY LIABILITY OF ANY KIND FOR * ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS SOFTWARE. * * Carnegie Mellon requests users of this software to return to * * Software Distribution Coordinator or Software.Distribution@CS.CMU.EDU * School of Computer Science * Carnegie Mellon University * Pittsburgh PA 15213-3890 * * any improvements or extensions that they make and grant Carnegie the * rights to redistribute these changes. */ -#include #include #include "stand.h" ssize_t write(int fd, const void *dest, size_t bcount) { struct open_file *f; size_t resid; f = fd2open_file(fd); if (f == NULL || !(f->f_flags & F_WRITE)) { errno = EBADF; return (-1); } if (f->f_flags & F_RAW) { twiddle(4); errno = (f->f_dev->dv_strategy)(f->f_devdata, F_WRITE, btodb(f->f_offset), bcount, __DECONST(void *, dest), &resid); if (errno) return (-1); f->f_offset += resid; return (resid); } resid = bcount; if ((errno = (f->f_ops->fo_write)(f, dest, bcount, &resid))) return (-1); return (bcount - resid); } diff --git a/stand/libsa/zalloc.c b/stand/libsa/zalloc.c index 3bee66c02201..98738cf1f357 100644 --- a/stand/libsa/zalloc.c +++ b/stand/libsa/zalloc.c @@ -1,337 +1,336 @@ /* * This module derived from code donated to the FreeBSD Project by * Matthew Dillon * * Copyright (c) 1998 The FreeBSD Project * 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 /* * LIB/MEMORY/ZALLOC.C - self contained low-overhead memory pool/allocation * subsystem * * This subsystem implements memory pools and memory allocation * routines. * * Pools are managed via a linked list of 'free' areas. Allocating * memory creates holes in the freelist, freeing memory fills them. * Since the freelist consists only of free memory areas, it is possible * to allocate the entire pool without incuring any structural overhead. * * The system works best when allocating similarly-sized chunks of * memory. Care must be taken to avoid fragmentation when * allocating/deallocating dissimilar chunks. * * When a memory pool is first allocated, the entire pool is marked as * allocated. This is done mainly because we do not want to modify any * portion of a pool's data area until we are given permission. The * caller must explicitly deallocate portions of the pool to make them * available. * * z[n]xalloc() works like z[n]alloc() but the allocation is made from * within the specified address range. If the segment could not be * allocated, NULL is returned. WARNING! The address range will be * aligned to an 8 or 16 byte boundry depending on the cpu so if you * give an unaligned address range, unexpected results may occur. * * If a standard allocation fails, the reclaim function will be called * to recover some space. This usually causes other portions of the * same pool to be released. Memory allocations at this low level * should not block but you can do that too in your reclaim function * if you want. Reclaim does not function when z[n]xalloc() is used, * only for z[n]alloc(). * * Allocation and frees of 0 bytes are valid operations. */ #include "zalloc_defs.h" /* * Objects in the pool must be aligned to at least the size of struct MemNode. * They must also be aligned to MALLOCALIGN, which should normally be larger * than the struct, so assert that to be so at compile time. */ typedef char assert_align[(sizeof(struct MemNode) <= MALLOCALIGN) ? 1 : -1]; #define MEMNODE_SIZE_MASK MALLOCALIGN_MASK /* * znalloc() - allocate memory (without zeroing) from pool. Call reclaim * and retry if appropriate, return NULL if unable to allocate * memory. */ void * znalloc(MemPool *mp, uintptr_t bytes, size_t align) { MemNode **pmn; MemNode *mn; /* * align according to pool object size (can be 0). This is * inclusive of the MEMNODE_SIZE_MASK minimum alignment. * */ bytes = (bytes + MEMNODE_SIZE_MASK) & ~MEMNODE_SIZE_MASK; if (bytes == 0) return ((void *)-1); /* * locate freelist entry big enough to hold the object. If all objects * are the same size, this is a constant-time function. */ if (bytes > mp->mp_Size - mp->mp_Used) return (NULL); for (pmn = &mp->mp_First; (mn = *pmn) != NULL; pmn = &mn->mr_Next) { char *ptr = (char *)mn; uintptr_t dptr; char *aligned; size_t extra; dptr = (uintptr_t)(ptr + MALLOCALIGN); /* pointer to data */ aligned = (char *)(roundup2(dptr, align) - MALLOCALIGN); extra = aligned - ptr; if (bytes + extra > mn->mr_Bytes) continue; /* * Cut extra from head and create new memory node from * remainder. */ if (extra != 0) { MemNode *new; new = (MemNode *)aligned; new->mr_Next = mn->mr_Next; new->mr_Bytes = mn->mr_Bytes - extra; /* And update current memory node */ mn->mr_Bytes = extra; mn->mr_Next = new; /* In next iteration, we will get our aligned address */ continue; } /* * Cut a chunk of memory out of the beginning of this * block and fixup the link appropriately. */ if (mn->mr_Bytes == bytes) { *pmn = mn->mr_Next; } else { mn = (MemNode *)((char *)mn + bytes); mn->mr_Next = ((MemNode *)ptr)->mr_Next; mn->mr_Bytes = ((MemNode *)ptr)->mr_Bytes - bytes; *pmn = mn; } mp->mp_Used += bytes; return(ptr); } /* * Memory pool is full, return NULL. */ return (NULL); } /* * zfree() - free previously allocated memory */ void zfree(MemPool *mp, void *ptr, uintptr_t bytes) { MemNode **pmn; MemNode *mn; /* * align according to pool object size (can be 0). This is * inclusive of the MEMNODE_SIZE_MASK minimum alignment. */ bytes = (bytes + MEMNODE_SIZE_MASK) & ~MEMNODE_SIZE_MASK; if (bytes == 0) return; /* * panic if illegal pointer */ if ((char *)ptr < (char *)mp->mp_Base || (char *)ptr + bytes > (char *)mp->mp_End || ((uintptr_t)ptr & MEMNODE_SIZE_MASK) != 0) panic("zfree(%p,%ju): wild pointer", ptr, (uintmax_t)bytes); /* * free the segment */ mp->mp_Used -= bytes; for (pmn = &mp->mp_First; (mn = *pmn) != NULL; pmn = &mn->mr_Next) { /* * If area between last node and current node * - check range * - check merge with next area * - check merge with previous area */ if ((char *)ptr <= (char *)mn) { /* * range check */ if ((char *)ptr + bytes > (char *)mn) { panic("zfree(%p,%ju): corrupt memlist1", ptr, (uintmax_t)bytes); } /* * merge against next area or create independent area */ if ((char *)ptr + bytes == (char *)mn) { ((MemNode *)ptr)->mr_Next = mn->mr_Next; ((MemNode *)ptr)->mr_Bytes = bytes + mn->mr_Bytes; } else { ((MemNode *)ptr)->mr_Next = mn; ((MemNode *)ptr)->mr_Bytes = bytes; } *pmn = mn = (MemNode *)ptr; /* * merge against previous area (if there is a previous * area). */ if (pmn != &mp->mp_First) { if ((char *)pmn + ((MemNode*)pmn)->mr_Bytes == (char *)ptr) { ((MemNode *)pmn)->mr_Next = mn->mr_Next; ((MemNode *)pmn)->mr_Bytes += mn->mr_Bytes; mn = (MemNode *)pmn; } } return; } if ((char *)ptr < (char *)mn + mn->mr_Bytes) { panic("zfree(%p,%ju): corrupt memlist2", ptr, (uintmax_t)bytes); } } /* * We are beyond the last MemNode, append new MemNode. Merge against * previous area if possible. */ if (pmn == &mp->mp_First || (char *)pmn + ((MemNode *)pmn)->mr_Bytes != (char *)ptr) { ((MemNode *)ptr)->mr_Next = NULL; ((MemNode *)ptr)->mr_Bytes = bytes; *pmn = (MemNode *)ptr; mn = (MemNode *)ptr; } else { ((MemNode *)pmn)->mr_Bytes += bytes; mn = (MemNode *)pmn; } } /* * zextendPool() - extend memory pool to cover additional space. * * Note: the added memory starts out as allocated, you * must free it to make it available to the memory subsystem. * * Note: mp_Size may not reflect (mp_End - mp_Base) range * due to other parts of the system doing their own sbrk() * calls. */ void zextendPool(MemPool *mp, void *base, uintptr_t bytes) { if (mp->mp_Size == 0) { mp->mp_Base = base; mp->mp_Used = bytes; mp->mp_End = (char *)base + bytes; mp->mp_Size = bytes; } else { void *pend = (char *)mp->mp_Base + mp->mp_Size; if (base < mp->mp_Base) { mp->mp_Size += (char *)mp->mp_Base - (char *)base; mp->mp_Used += (char *)mp->mp_Base - (char *)base; mp->mp_Base = base; } base = (char *)base + bytes; if (base > pend) { mp->mp_Size += (char *)base - (char *)pend; mp->mp_Used += (char *)base - (char *)pend; mp->mp_End = (char *)base; } } } #ifdef ZALLOCDEBUG void zallocstats(MemPool *mp) { int abytes = 0; int hbytes = 0; int fcount = 0; MemNode *mn; printf("%d bytes reserved", (int)mp->mp_Size); mn = mp->mp_First; if ((void *)mn != (void *)mp->mp_Base) { abytes += (char *)mn - (char *)mp->mp_Base; } while (mn != NULL) { if ((char *)mn + mn->mr_Bytes != mp->mp_End) { hbytes += mn->mr_Bytes; ++fcount; } if (mn->mr_Next != NULL) { abytes += (char *)mn->mr_Next - ((char *)mn + mn->mr_Bytes); } mn = mn->mr_Next; } printf(" %d bytes allocated\n%d fragments (%d bytes fragmented)\n", abytes, fcount, hbytes); } #endif diff --git a/stand/libsa/zfs/nvlist.c b/stand/libsa/zfs/nvlist.c index e6634ec969d4..6cb496a57b26 100644 --- a/stand/libsa/zfs/nvlist.c +++ b/stand/libsa/zfs/nvlist.c @@ -1,1699 +1,1698 @@ /*- * Copyright 2020 Toomas Soome * * 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 #include #include #ifdef _STANDALONE #include #else #include #include #include #include #include #endif #include "nvlist.h" enum xdr_op { XDR_OP_ENCODE = 1, XDR_OP_DECODE = 2 }; typedef struct xdr { enum xdr_op xdr_op; int (*xdr_getint)(struct xdr *, int *); int (*xdr_putint)(struct xdr *, int); int (*xdr_getuint)(struct xdr *, unsigned *); int (*xdr_putuint)(struct xdr *, unsigned); const uint8_t *xdr_buf; uint8_t *xdr_idx; size_t xdr_buf_size; } xdr_t; static int nvlist_xdr_nvlist(xdr_t *, nvlist_t *); static bool nvlist_size_xdr(xdr_t *, size_t *); static bool nvlist_size_native(xdr_t *, size_t *); static bool xdr_int(xdr_t *, int *); static bool xdr_u_int(xdr_t *, unsigned *); typedef bool (*xdrproc_t)(xdr_t *, void *); /* Basic primitives for XDR translation operations, getint and putint. */ static int _getint(struct xdr *xdr, int *ip) { *ip = be32dec(xdr->xdr_idx); return (sizeof(int)); } static int _putint(struct xdr *xdr, int i) { int *ip = (int *)xdr->xdr_idx; *ip = htobe32(i); return (sizeof(int)); } static int _getuint(struct xdr *xdr, unsigned *ip) { *ip = be32dec(xdr->xdr_idx); return (sizeof(unsigned)); } static int _putuint(struct xdr *xdr, unsigned i) { unsigned *up = (unsigned *)xdr->xdr_idx; *up = htobe32(i); return (sizeof(int)); } static int _getint_mem(struct xdr *xdr, int *ip) { *ip = *(int *)xdr->xdr_idx; return (sizeof(int)); } static int _putint_mem(struct xdr *xdr, int i) { int *ip = (int *)xdr->xdr_idx; *ip = i; return (sizeof(int)); } static int _getuint_mem(struct xdr *xdr, unsigned *ip) { *ip = *(unsigned *)xdr->xdr_idx; return (sizeof(unsigned)); } static int _putuint_mem(struct xdr *xdr, unsigned i) { unsigned *up = (unsigned *)xdr->xdr_idx; *up = i; return (sizeof(int)); } /* * XDR data translations. */ static bool xdr_short(xdr_t *xdr, short *ip) { int i; bool rv; i = *ip; if ((rv = xdr_int(xdr, &i))) { if (xdr->xdr_op == XDR_OP_DECODE) *ip = i; } return (rv); } static bool xdr_u_short(xdr_t *xdr, unsigned short *ip) { unsigned u; bool rv; u = *ip; if ((rv = xdr_u_int(xdr, &u))) { if (xdr->xdr_op == XDR_OP_DECODE) *ip = u; } return (rv); } /* * translate xdr->xdr_idx, increment it by size of int. */ static bool xdr_int(xdr_t *xdr, int *ip) { bool rv = false; int *i = (int *)xdr->xdr_idx; if (xdr->xdr_idx + sizeof(int) > xdr->xdr_buf + xdr->xdr_buf_size) return (rv); switch (xdr->xdr_op) { case XDR_OP_ENCODE: /* Encode value *ip, store to buf */ xdr->xdr_idx += xdr->xdr_putint(xdr, *ip); rv = true; break; case XDR_OP_DECODE: /* Decode buf, return value to *ip */ xdr->xdr_idx += xdr->xdr_getint(xdr, i); *ip = *i; rv = true; break; } return (rv); } /* * translate xdr->xdr_idx, increment it by size of unsigned int. */ static bool xdr_u_int(xdr_t *xdr, unsigned *ip) { bool rv = false; unsigned *u = (unsigned *)xdr->xdr_idx; if (xdr->xdr_idx + sizeof(unsigned) > xdr->xdr_buf + xdr->xdr_buf_size) return (rv); switch (xdr->xdr_op) { case XDR_OP_ENCODE: /* Encode value *ip, store to buf */ xdr->xdr_idx += xdr->xdr_putuint(xdr, *ip); rv = true; break; case XDR_OP_DECODE: /* Decode buf, return value to *ip */ xdr->xdr_idx += xdr->xdr_getuint(xdr, u); *ip = *u; rv = true; break; } return (rv); } static bool xdr_int64(xdr_t *xdr, int64_t *lp) { bool rv = false; if (xdr->xdr_idx + sizeof(int64_t) > xdr->xdr_buf + xdr->xdr_buf_size) return (rv); switch (xdr->xdr_op) { case XDR_OP_ENCODE: /* Encode value *lp, store to buf */ if (xdr->xdr_putint == _putint) *(int64_t *)xdr->xdr_idx = htobe64(*lp); else *(int64_t *)xdr->xdr_idx = *lp; xdr->xdr_idx += sizeof(int64_t); rv = true; break; case XDR_OP_DECODE: /* Decode buf, return value to *ip */ if (xdr->xdr_getint == _getint) *lp = be64toh(*(int64_t *)xdr->xdr_idx); else *lp = *(int64_t *)xdr->xdr_idx; xdr->xdr_idx += sizeof(int64_t); rv = true; } return (rv); } static bool xdr_uint64(xdr_t *xdr, uint64_t *lp) { bool rv = false; if (xdr->xdr_idx + sizeof(uint64_t) > xdr->xdr_buf + xdr->xdr_buf_size) return (rv); switch (xdr->xdr_op) { case XDR_OP_ENCODE: /* Encode value *ip, store to buf */ if (xdr->xdr_putint == _putint) *(uint64_t *)xdr->xdr_idx = htobe64(*lp); else *(uint64_t *)xdr->xdr_idx = *lp; xdr->xdr_idx += sizeof(uint64_t); rv = true; break; case XDR_OP_DECODE: /* Decode buf, return value to *ip */ if (xdr->xdr_getuint == _getuint) *lp = be64toh(*(uint64_t *)xdr->xdr_idx); else *lp = *(uint64_t *)xdr->xdr_idx; xdr->xdr_idx += sizeof(uint64_t); rv = true; } return (rv); } static bool xdr_char(xdr_t *xdr, char *cp) { int i; bool rv = false; i = *cp; if ((rv = xdr_int(xdr, &i))) { if (xdr->xdr_op == XDR_OP_DECODE) *cp = i; } return (rv); } static bool xdr_string(xdr_t *xdr, nv_string_t *s) { int size = 0; bool rv = false; switch (xdr->xdr_op) { case XDR_OP_ENCODE: size = s->nv_size; if (xdr->xdr_idx + sizeof(unsigned) + NV_ALIGN4(size) > xdr->xdr_buf + xdr->xdr_buf_size) break; xdr->xdr_idx += xdr->xdr_putuint(xdr, s->nv_size); xdr->xdr_idx += NV_ALIGN4(size); rv = true; break; case XDR_OP_DECODE: if (xdr->xdr_idx + sizeof(unsigned) > xdr->xdr_buf + xdr->xdr_buf_size) break; size = xdr->xdr_getuint(xdr, &s->nv_size); size = NV_ALIGN4(size + s->nv_size); if (xdr->xdr_idx + size > xdr->xdr_buf + xdr->xdr_buf_size) break; xdr->xdr_idx += size; rv = true; break; } return (rv); } static bool xdr_array(xdr_t *xdr, const unsigned nelem, const xdrproc_t elproc) { bool rv = true; unsigned c = nelem; if (!xdr_u_int(xdr, &c)) return (false); for (unsigned i = 0; i < nelem; i++) { if (!elproc(xdr, xdr->xdr_idx)) return (false); } return (rv); } /* * nvlist management functions. */ void nvlist_destroy(nvlist_t *nvl) { if (nvl != NULL) { /* Free data if it was allocated by us. */ if (nvl->nv_asize > 0) free(nvl->nv_data); } free(nvl); } char * nvstring_get(nv_string_t *nvs) { char *s; s = malloc(nvs->nv_size + 1); if (s != NULL) { bcopy(nvs->nv_data, s, nvs->nv_size); s[nvs->nv_size] = '\0'; } return (s); } /* * Create empty nvlist. * The nvlist is terminated by 2x zeros (8 bytes). */ nvlist_t * nvlist_create(int flag) { nvlist_t *nvl; nvs_data_t *nvs; nvl = calloc(1, sizeof(*nvl)); if (nvl == NULL) return (nvl); nvl->nv_header.nvh_encoding = NV_ENCODE_XDR; nvl->nv_header.nvh_endian = _BYTE_ORDER == _LITTLE_ENDIAN; nvl->nv_asize = nvl->nv_size = sizeof(*nvs); nvs = calloc(1, nvl->nv_asize); if (nvs == NULL) { free(nvl); return (NULL); } /* data in nvlist is byte stream */ nvl->nv_data = (uint8_t *)nvs; nvs->nvl_version = NV_VERSION; nvs->nvl_nvflag = flag; return (nvl); } static bool nvlist_xdr_nvp(xdr_t *xdr, nvlist_t *nvl) { nv_string_t *nv_string; nv_pair_data_t *nvp_data; nvlist_t nvlist; unsigned type, nelem; xdr_t nv_xdr; nv_string = (nv_string_t *)xdr->xdr_idx; if (!xdr_string(xdr, nv_string)) { return (false); } nvp_data = (nv_pair_data_t *)xdr->xdr_idx; type = nvp_data->nv_type; nelem = nvp_data->nv_nelem; if (!xdr_u_int(xdr, &type) || !xdr_u_int(xdr, &nelem)) return (false); switch (type) { case DATA_TYPE_NVLIST: case DATA_TYPE_NVLIST_ARRAY: bzero(&nvlist, sizeof(nvlist)); nvlist.nv_data = xdr->xdr_idx; nvlist.nv_idx = nvlist.nv_data; /* Set up xdr for this nvlist. */ nv_xdr = *xdr; nv_xdr.xdr_buf = nvlist.nv_data; nv_xdr.xdr_idx = nvlist.nv_data; nv_xdr.xdr_buf_size = nvl->nv_data + nvl->nv_size - nvlist.nv_data; for (unsigned i = 0; i < nelem; i++) { if (xdr->xdr_op == XDR_OP_ENCODE) { if (!nvlist_size_native(&nv_xdr, &nvlist.nv_size)) return (false); } else { if (!nvlist_size_xdr(&nv_xdr, &nvlist.nv_size)) return (false); } if (nvlist_xdr_nvlist(xdr, &nvlist) != 0) return (false); nvlist.nv_data = nv_xdr.xdr_idx; nvlist.nv_idx = nv_xdr.xdr_idx; nv_xdr.xdr_buf = nv_xdr.xdr_idx; nv_xdr.xdr_buf_size = nvl->nv_data + nvl->nv_size - nvlist.nv_data; } break; case DATA_TYPE_BOOLEAN: /* BOOLEAN does not take value space */ break; case DATA_TYPE_BYTE: case DATA_TYPE_INT8: case DATA_TYPE_UINT8: if (!xdr_char(xdr, (char *)&nvp_data->nv_data[0])) return (false); break; case DATA_TYPE_INT16: if (!xdr_short(xdr, (short *)&nvp_data->nv_data[0])) return (false); break; case DATA_TYPE_UINT16: if (!xdr_u_short(xdr, (unsigned short *)&nvp_data->nv_data[0])) return (false); break; case DATA_TYPE_BOOLEAN_VALUE: case DATA_TYPE_INT32: if (!xdr_int(xdr, (int *)&nvp_data->nv_data[0])) return (false); break; case DATA_TYPE_UINT32: if (!xdr_u_int(xdr, (unsigned *)&nvp_data->nv_data[0])) return (false); break; case DATA_TYPE_HRTIME: case DATA_TYPE_INT64: if (!xdr_int64(xdr, (int64_t *)&nvp_data->nv_data[0])) return (false); break; case DATA_TYPE_UINT64: if (!xdr_uint64(xdr, (uint64_t *)&nvp_data->nv_data[0])) return (false); break; case DATA_TYPE_BYTE_ARRAY: case DATA_TYPE_STRING: nv_string = (nv_string_t *)&nvp_data->nv_data[0]; if (!xdr_string(xdr, nv_string)) return (false); break; case DATA_TYPE_STRING_ARRAY: nv_string = (nv_string_t *)&nvp_data->nv_data[0]; for (unsigned i = 0; i < nelem; i++) { if (!xdr_string(xdr, nv_string)) return (false); nv_string = (nv_string_t *)xdr->xdr_idx; } break; case DATA_TYPE_INT8_ARRAY: case DATA_TYPE_UINT8_ARRAY: case DATA_TYPE_INT16_ARRAY: case DATA_TYPE_UINT16_ARRAY: case DATA_TYPE_BOOLEAN_ARRAY: case DATA_TYPE_INT32_ARRAY: case DATA_TYPE_UINT32_ARRAY: if (!xdr_array(xdr, nelem, (xdrproc_t)xdr_u_int)) return (false); break; case DATA_TYPE_INT64_ARRAY: case DATA_TYPE_UINT64_ARRAY: if (!xdr_array(xdr, nelem, (xdrproc_t)xdr_uint64)) return (false); break; } return (true); } static int nvlist_xdr_nvlist(xdr_t *xdr, nvlist_t *nvl) { nvp_header_t *nvph; nvs_data_t *nvs; unsigned encoded_size, decoded_size; int rv; nvs = (nvs_data_t *)xdr->xdr_idx; nvph = &nvs->nvl_pair; if (!xdr_u_int(xdr, &nvs->nvl_version)) return (EINVAL); if (!xdr_u_int(xdr, &nvs->nvl_nvflag)) return (EINVAL); encoded_size = nvph->encoded_size; decoded_size = nvph->decoded_size; if (xdr->xdr_op == XDR_OP_ENCODE) { if (!xdr_u_int(xdr, &nvph->encoded_size)) return (EINVAL); if (!xdr_u_int(xdr, &nvph->decoded_size)) return (EINVAL); } else { xdr->xdr_idx += 2 * sizeof(unsigned); } rv = 0; while (encoded_size && decoded_size) { if (!nvlist_xdr_nvp(xdr, nvl)) return (EINVAL); nvph = (nvp_header_t *)(xdr->xdr_idx); encoded_size = nvph->encoded_size; decoded_size = nvph->decoded_size; if (xdr->xdr_op == XDR_OP_ENCODE) { if (!xdr_u_int(xdr, &nvph->encoded_size)) return (EINVAL); if (!xdr_u_int(xdr, &nvph->decoded_size)) return (EINVAL); } else { xdr->xdr_idx += 2 * sizeof(unsigned); } } return (rv); } /* * Calculate nvlist size, translating encoded_size and decoded_size. */ static bool nvlist_size_xdr(xdr_t *xdr, size_t *size) { uint8_t *pair; unsigned encoded_size, decoded_size; xdr->xdr_idx += 2 * sizeof(unsigned); pair = xdr->xdr_idx; if (!xdr_u_int(xdr, &encoded_size) || !xdr_u_int(xdr, &decoded_size)) return (false); while (encoded_size && decoded_size) { xdr->xdr_idx = pair + encoded_size; pair = xdr->xdr_idx; if (!xdr_u_int(xdr, &encoded_size) || !xdr_u_int(xdr, &decoded_size)) return (false); } *size = xdr->xdr_idx - xdr->xdr_buf; return (true); } nvp_header_t * nvlist_next_nvpair(nvlist_t *nvl, nvp_header_t *nvh) { uint8_t *pair; unsigned encoded_size, decoded_size; xdr_t xdr; if (nvl == NULL) return (NULL); xdr.xdr_buf = nvl->nv_data; xdr.xdr_idx = nvl->nv_data; xdr.xdr_buf_size = nvl->nv_size; xdr.xdr_idx += 2 * sizeof(unsigned); /* Skip tp current pair */ if (nvh != NULL) { xdr.xdr_idx = (uint8_t *)nvh; } pair = xdr.xdr_idx; if (xdr.xdr_idx > xdr.xdr_buf + xdr.xdr_buf_size) return (NULL); encoded_size = *(unsigned *)xdr.xdr_idx; xdr.xdr_idx += sizeof(unsigned); if (xdr.xdr_idx > xdr.xdr_buf + xdr.xdr_buf_size) return (NULL); decoded_size = *(unsigned *)xdr.xdr_idx; xdr.xdr_idx += sizeof(unsigned); if (xdr.xdr_idx > xdr.xdr_buf + xdr.xdr_buf_size) return (NULL); while (encoded_size && decoded_size) { if (nvh == NULL) return ((nvp_header_t *)pair); xdr.xdr_idx = pair + encoded_size; nvh = (nvp_header_t *)xdr.xdr_idx; if (xdr.xdr_idx > xdr.xdr_buf + xdr.xdr_buf_size) return (NULL); encoded_size = *(unsigned *)xdr.xdr_idx; xdr.xdr_idx += sizeof(unsigned); if (xdr.xdr_idx > xdr.xdr_buf + xdr.xdr_buf_size) return (NULL); decoded_size = *(unsigned *)xdr.xdr_idx; xdr.xdr_idx += sizeof(unsigned); if (xdr.xdr_idx > xdr.xdr_buf + xdr.xdr_buf_size) return (NULL); if (encoded_size != 0 && decoded_size != 0) { return (nvh); } } return (NULL); } /* * Calculate nvlist size by walking in memory data. */ static bool nvlist_size_native(xdr_t *xdr, size_t *size) { uint8_t *pair; unsigned encoded_size, decoded_size; xdr->xdr_idx += 2 * sizeof(unsigned); pair = xdr->xdr_idx; if (xdr->xdr_idx > xdr->xdr_buf + xdr->xdr_buf_size) return (false); encoded_size = *(unsigned *)xdr->xdr_idx; xdr->xdr_idx += sizeof(unsigned); if (xdr->xdr_idx > xdr->xdr_buf + xdr->xdr_buf_size) return (false); decoded_size = *(unsigned *)xdr->xdr_idx; xdr->xdr_idx += sizeof(unsigned); while (encoded_size && decoded_size) { xdr->xdr_idx = pair + encoded_size; pair = xdr->xdr_idx; if (xdr->xdr_idx > xdr->xdr_buf + xdr->xdr_buf_size) return (false); encoded_size = *(unsigned *)xdr->xdr_idx; xdr->xdr_idx += sizeof(unsigned); if (xdr->xdr_idx > xdr->xdr_buf + xdr->xdr_buf_size) return (false); decoded_size = *(unsigned *)xdr->xdr_idx; xdr->xdr_idx += sizeof(unsigned); } *size = xdr->xdr_idx - xdr->xdr_buf; return (true); } /* * Export nvlist to byte stream format. */ int nvlist_export(nvlist_t *nvl) { int rv; xdr_t xdr = { .xdr_op = XDR_OP_ENCODE, .xdr_putint = _putint, .xdr_putuint = _putuint, .xdr_buf = nvl->nv_data, .xdr_idx = nvl->nv_data, .xdr_buf_size = nvl->nv_size }; if (nvl->nv_header.nvh_encoding != NV_ENCODE_XDR) return (ENOTSUP); nvl->nv_idx = nvl->nv_data; rv = nvlist_xdr_nvlist(&xdr, nvl); return (rv); } /* * Import nvlist from byte stream. * Determine the stream size and allocate private copy. * Then translate the data. */ nvlist_t * nvlist_import(const char *stream, size_t size) { nvlist_t *nvl; xdr_t xdr = { .xdr_op = XDR_OP_DECODE, .xdr_getint = _getint, .xdr_getuint = _getuint }; /* Check the nvlist head. */ if (stream[0] != NV_ENCODE_XDR || (stream[1] != '\0' && stream[1] != '\1') || stream[2] != '\0' || stream[3] != '\0' || be32toh(*(uint32_t *)(stream + 4)) != NV_VERSION || be32toh(*(uint32_t *)(stream + 8)) != NV_UNIQUE_NAME) return (NULL); nvl = malloc(sizeof(*nvl)); if (nvl == NULL) return (nvl); nvl->nv_header.nvh_encoding = stream[0]; nvl->nv_header.nvh_endian = stream[1]; nvl->nv_header.nvh_reserved1 = stream[2]; nvl->nv_header.nvh_reserved2 = stream[3]; xdr.xdr_buf = xdr.xdr_idx = (uint8_t *)stream + 4; xdr.xdr_buf_size = size - 4; if (!nvlist_size_xdr(&xdr, &nvl->nv_asize)) { free(nvl); return (NULL); } nvl->nv_size = nvl->nv_asize; nvl->nv_data = malloc(nvl->nv_asize); if (nvl->nv_data == NULL) { free(nvl); return (NULL); } nvl->nv_idx = nvl->nv_data; bcopy(stream + 4, nvl->nv_data, nvl->nv_asize); xdr.xdr_buf = xdr.xdr_idx = nvl->nv_data; xdr.xdr_buf_size = nvl->nv_asize; if (nvlist_xdr_nvlist(&xdr, nvl) != 0) { free(nvl->nv_data); free(nvl); nvl = NULL; } return (nvl); } /* * remove pair from this nvlist. */ int nvlist_remove(nvlist_t *nvl, const char *name, data_type_t type) { uint8_t *head, *tail; nvs_data_t *data; nvp_header_t *nvp; nv_string_t *nvp_name; nv_pair_data_t *nvp_data; size_t size; xdr_t xdr; if (nvl == NULL || nvl->nv_data == NULL || name == NULL) return (EINVAL); /* Make sure the nvlist size is set correct */ xdr.xdr_idx = nvl->nv_data; xdr.xdr_buf = xdr.xdr_idx; xdr.xdr_buf_size = nvl->nv_size; if (!nvlist_size_native(&xdr, &nvl->nv_size)) return (EINVAL); data = (nvs_data_t *)nvl->nv_data; nvp = &data->nvl_pair; /* first pair in nvlist */ head = (uint8_t *)nvp; while (nvp->encoded_size != 0 && nvp->decoded_size != 0) { nvp_name = (nv_string_t *)(nvp + 1); nvp_data = (nv_pair_data_t *)(&nvp_name->nv_data[0] + NV_ALIGN4(nvp_name->nv_size)); if (strlen(name) == nvp_name->nv_size && memcmp(nvp_name->nv_data, name, nvp_name->nv_size) == 0 && (nvp_data->nv_type == type || type == DATA_TYPE_UNKNOWN)) { /* * set tail to point to next nvpair and size * is the length of the tail. */ tail = head + nvp->encoded_size; size = nvl->nv_size - (tail - nvl->nv_data); /* adjust the size of the nvlist. */ nvl->nv_size -= nvp->encoded_size; bcopy(tail, head, size); return (0); } /* Not our pair, skip to next. */ head = head + nvp->encoded_size; nvp = (nvp_header_t *)head; } return (ENOENT); } static int clone_nvlist(const nvlist_t *nvl, const uint8_t *ptr, unsigned size, nvlist_t **nvlist) { nvlist_t *nv; nv = calloc(1, sizeof(*nv)); if (nv == NULL) return (ENOMEM); nv->nv_header = nvl->nv_header; nv->nv_asize = size; nv->nv_size = size; nv->nv_data = malloc(nv->nv_asize); if (nv->nv_data == NULL) { free(nv); return (ENOMEM); } bcopy(ptr, nv->nv_data, nv->nv_asize); *nvlist = nv; return (0); } /* * Return the next nvlist in an nvlist array. */ static uint8_t * nvlist_next(const uint8_t *ptr) { nvs_data_t *data; nvp_header_t *nvp; data = (nvs_data_t *)ptr; nvp = &data->nvl_pair; /* first pair in nvlist */ while (nvp->encoded_size != 0 && nvp->decoded_size != 0) { nvp = (nvp_header_t *)((uint8_t *)nvp + nvp->encoded_size); } return ((uint8_t *)nvp + sizeof(*nvp)); } /* * Note: nvlist and nvlist array must be freed by caller. */ int nvlist_find(const nvlist_t *nvl, const char *name, data_type_t type, int *elementsp, void *valuep, int *sizep) { nvs_data_t *data; nvp_header_t *nvp; nv_string_t *nvp_name; nv_pair_data_t *nvp_data; nvlist_t **nvlist, *nv; uint8_t *ptr; int rv; if (nvl == NULL || nvl->nv_data == NULL || name == NULL) return (EINVAL); data = (nvs_data_t *)nvl->nv_data; nvp = &data->nvl_pair; /* first pair in nvlist */ while (nvp->encoded_size != 0 && nvp->decoded_size != 0) { nvp_name = (nv_string_t *)((uint8_t *)nvp + sizeof(*nvp)); if (nvl->nv_data + nvl->nv_size < nvp_name->nv_data + nvp_name->nv_size) return (EIO); nvp_data = (nv_pair_data_t *) NV_ALIGN4((uintptr_t)&nvp_name->nv_data[0] + nvp_name->nv_size); if (strlen(name) == nvp_name->nv_size && memcmp(nvp_name->nv_data, name, nvp_name->nv_size) == 0 && (nvp_data->nv_type == type || type == DATA_TYPE_UNKNOWN)) { if (elementsp != NULL) *elementsp = nvp_data->nv_nelem; switch (nvp_data->nv_type) { case DATA_TYPE_UINT64: bcopy(nvp_data->nv_data, valuep, sizeof(uint64_t)); return (0); case DATA_TYPE_STRING: nvp_name = (nv_string_t *)nvp_data->nv_data; if (sizep != NULL) { *sizep = nvp_name->nv_size; } *(const uint8_t **)valuep = &nvp_name->nv_data[0]; return (0); case DATA_TYPE_NVLIST: ptr = &nvp_data->nv_data[0]; rv = clone_nvlist(nvl, ptr, nvlist_next(ptr) - ptr, &nv); if (rv == 0) { *(nvlist_t **)valuep = nv; } return (rv); case DATA_TYPE_NVLIST_ARRAY: nvlist = calloc(nvp_data->nv_nelem, sizeof(nvlist_t *)); if (nvlist == NULL) return (ENOMEM); ptr = &nvp_data->nv_data[0]; rv = 0; for (unsigned i = 0; i < nvp_data->nv_nelem; i++) { rv = clone_nvlist(nvl, ptr, nvlist_next(ptr) - ptr, &nvlist[i]); if (rv != 0) goto error; ptr = nvlist_next(ptr); } *(nvlist_t ***)valuep = nvlist; return (rv); } return (EIO); } /* Not our pair, skip to next. */ nvp = (nvp_header_t *)((uint8_t *)nvp + nvp->encoded_size); if (nvl->nv_data + nvl->nv_size < (uint8_t *)nvp) return (EIO); } return (ENOENT); error: for (unsigned i = 0; i < nvp_data->nv_nelem; i++) { free(nvlist[i]->nv_data); free(nvlist[i]); } free(nvlist); return (rv); } static int get_value_size(data_type_t type, const void *data, uint32_t nelem) { uint64_t value_sz = 0; switch (type) { case DATA_TYPE_BOOLEAN: value_sz = 0; break; case DATA_TYPE_BOOLEAN_VALUE: case DATA_TYPE_BYTE: case DATA_TYPE_INT8: case DATA_TYPE_UINT8: case DATA_TYPE_INT16: case DATA_TYPE_UINT16: case DATA_TYPE_INT32: case DATA_TYPE_UINT32: /* Our smallest data unit is 32-bit */ value_sz = sizeof(uint32_t); break; case DATA_TYPE_HRTIME: case DATA_TYPE_INT64: value_sz = sizeof(int64_t); break; case DATA_TYPE_UINT64: value_sz = sizeof(uint64_t); break; case DATA_TYPE_STRING: if (data == NULL) value_sz = 0; else value_sz = strlen(data) + 1; break; case DATA_TYPE_BYTE_ARRAY: value_sz = nelem * sizeof(uint8_t); break; case DATA_TYPE_BOOLEAN_ARRAY: case DATA_TYPE_INT8_ARRAY: case DATA_TYPE_UINT8_ARRAY: case DATA_TYPE_INT16_ARRAY: case DATA_TYPE_UINT16_ARRAY: case DATA_TYPE_INT32_ARRAY: case DATA_TYPE_UINT32_ARRAY: value_sz = (uint64_t)nelem * sizeof(uint32_t); break; case DATA_TYPE_INT64_ARRAY: value_sz = (uint64_t)nelem * sizeof(int64_t); break; case DATA_TYPE_UINT64_ARRAY: value_sz = (uint64_t)nelem * sizeof(uint64_t); break; case DATA_TYPE_STRING_ARRAY: value_sz = (uint64_t)nelem * sizeof(uint64_t); if (data != NULL) { char *const *strs = data; uint32_t i; for (i = 0; i < nelem; i++) { if (strs[i] == NULL) return (-1); value_sz += strlen(strs[i]) + 1; } } break; case DATA_TYPE_NVLIST: /* * The decoded size of nvlist is constant. */ value_sz = NV_ALIGN(6 * 4); /* sizeof nvlist_t */ break; case DATA_TYPE_NVLIST_ARRAY: value_sz = (uint64_t)nelem * sizeof(uint64_t) + (uint64_t)nelem * NV_ALIGN(6 * 4); /* sizeof nvlist_t */ break; default: return (-1); } return (value_sz > INT32_MAX ? -1 : (int)value_sz); } static int get_nvp_data_size(data_type_t type, const void *data, uint32_t nelem) { uint64_t value_sz = 0; xdr_t xdr; size_t size; switch (type) { case DATA_TYPE_BOOLEAN: value_sz = 0; break; case DATA_TYPE_BOOLEAN_VALUE: case DATA_TYPE_BYTE: case DATA_TYPE_INT8: case DATA_TYPE_UINT8: case DATA_TYPE_INT16: case DATA_TYPE_UINT16: case DATA_TYPE_INT32: case DATA_TYPE_UINT32: /* Our smallest data unit is 32-bit */ value_sz = sizeof(uint32_t); break; case DATA_TYPE_HRTIME: case DATA_TYPE_INT64: case DATA_TYPE_UINT64: value_sz = sizeof(uint64_t); break; case DATA_TYPE_STRING: value_sz = 4 + NV_ALIGN4(strlen(data)); break; case DATA_TYPE_BYTE_ARRAY: value_sz = NV_ALIGN4(nelem); break; case DATA_TYPE_BOOLEAN_ARRAY: case DATA_TYPE_INT8_ARRAY: case DATA_TYPE_UINT8_ARRAY: case DATA_TYPE_INT16_ARRAY: case DATA_TYPE_UINT16_ARRAY: case DATA_TYPE_INT32_ARRAY: case DATA_TYPE_UINT32_ARRAY: value_sz = 4 + (uint64_t)nelem * sizeof(uint32_t); break; case DATA_TYPE_INT64_ARRAY: case DATA_TYPE_UINT64_ARRAY: value_sz = 4 + (uint64_t)nelem * sizeof(uint64_t); break; case DATA_TYPE_STRING_ARRAY: if (data != NULL) { char *const *strs = data; uint32_t i; for (i = 0; i < nelem; i++) { value_sz += 4 + NV_ALIGN4(strlen(strs[i])); } } break; case DATA_TYPE_NVLIST: xdr.xdr_idx = ((nvlist_t *)data)->nv_data; xdr.xdr_buf = xdr.xdr_idx; xdr.xdr_buf_size = ((nvlist_t *)data)->nv_size; if (!nvlist_size_native(&xdr, &size)) return (-1); value_sz = size; break; case DATA_TYPE_NVLIST_ARRAY: value_sz = 0; for (uint32_t i = 0; i < nelem; i++) { xdr.xdr_idx = ((nvlist_t **)data)[i]->nv_data; xdr.xdr_buf = xdr.xdr_idx; xdr.xdr_buf_size = ((nvlist_t **)data)[i]->nv_size; if (!nvlist_size_native(&xdr, &size)) return (-1); value_sz += size; } break; default: return (-1); } return (value_sz > INT32_MAX ? -1 : (int)value_sz); } #define NVPE_SIZE(name_len, data_len) \ (4 + 4 + 4 + NV_ALIGN4(name_len) + 4 + 4 + data_len) #define NVP_SIZE(name_len, data_len) \ (NV_ALIGN((4 * 4) + (name_len)) + NV_ALIGN(data_len)) static int nvlist_add_common(nvlist_t *nvl, const char *name, data_type_t type, uint32_t nelem, const void *data) { nvs_data_t *nvs; nvp_header_t head, *hp; uint8_t *ptr; size_t namelen; int decoded_size, encoded_size; xdr_t xdr = { .xdr_op = XDR_OP_ENCODE, .xdr_putint = _putint_mem, .xdr_putuint = _putuint_mem, .xdr_buf = nvl->nv_data, .xdr_idx = nvl->nv_data, .xdr_buf_size = nvl->nv_size }; nvs = (nvs_data_t *)nvl->nv_data; if (nvs->nvl_nvflag & NV_UNIQUE_NAME) (void) nvlist_remove(nvl, name, type); xdr.xdr_buf = nvl->nv_data; xdr.xdr_idx = nvl->nv_data; xdr.xdr_buf_size = nvl->nv_size; if (!nvlist_size_native(&xdr, &nvl->nv_size)) return (EINVAL); namelen = strlen(name); if ((decoded_size = get_value_size(type, data, nelem)) < 0) return (EINVAL); if ((encoded_size = get_nvp_data_size(type, data, nelem)) < 0) return (EINVAL); /* * The encoded size is calculated as: * encode_size (4) + decode_size (4) + * name string size (4 + NV_ALIGN4(namelen) + * data type (4) + nelem size (4) + datalen * * The decoded size is calculated as: * Note: namelen is with terminating 0. * NV_ALIGN(sizeof(nvpair_t) (4 * 4) + namelen + 1) + * NV_ALIGN(data_len) */ head.encoded_size = NVPE_SIZE(namelen, encoded_size); head.decoded_size = NVP_SIZE(namelen + 1, decoded_size); if (nvl->nv_asize - nvl->nv_size < head.encoded_size + 8) { ptr = realloc(nvl->nv_data, nvl->nv_asize + head.encoded_size); if (ptr == NULL) return (ENOMEM); nvl->nv_data = ptr; nvl->nv_asize += head.encoded_size; } nvl->nv_idx = nvl->nv_data + nvl->nv_size - sizeof(*hp); bzero(nvl->nv_idx, head.encoded_size + 8); hp = (nvp_header_t *)nvl->nv_idx; *hp = head; nvl->nv_idx += sizeof(*hp); xdr.xdr_buf = nvl->nv_data; xdr.xdr_buf_size = nvl->nv_asize; xdr.xdr_idx = nvl->nv_idx; xdr.xdr_idx += xdr.xdr_putuint(&xdr, namelen); strlcpy((char *)xdr.xdr_idx, name, namelen + 1); xdr.xdr_idx += NV_ALIGN4(namelen); xdr.xdr_idx += xdr.xdr_putuint(&xdr, type); xdr.xdr_idx += xdr.xdr_putuint(&xdr, nelem); switch (type) { case DATA_TYPE_BOOLEAN: break; case DATA_TYPE_BYTE_ARRAY: xdr.xdr_idx += xdr.xdr_putuint(&xdr, encoded_size); bcopy(data, xdr.xdr_idx, nelem); xdr.xdr_idx += NV_ALIGN4(encoded_size); break; case DATA_TYPE_STRING: encoded_size = strlen(data); xdr.xdr_idx += xdr.xdr_putuint(&xdr, encoded_size); strlcpy((char *)xdr.xdr_idx, data, encoded_size + 1); xdr.xdr_idx += NV_ALIGN4(encoded_size); break; case DATA_TYPE_STRING_ARRAY: for (uint32_t i = 0; i < nelem; i++) { encoded_size = strlen(((char **)data)[i]); xdr.xdr_idx += xdr.xdr_putuint(&xdr, encoded_size); strlcpy((char *)xdr.xdr_idx, ((char **)data)[i], encoded_size + 1); xdr.xdr_idx += NV_ALIGN4(encoded_size); } break; case DATA_TYPE_BYTE: case DATA_TYPE_INT8: case DATA_TYPE_UINT8: xdr_char(&xdr, (char *)data); break; case DATA_TYPE_INT8_ARRAY: case DATA_TYPE_UINT8_ARRAY: xdr_array(&xdr, nelem, (xdrproc_t)xdr_char); break; case DATA_TYPE_INT16: xdr_short(&xdr, (short *)data); break; case DATA_TYPE_UINT16: xdr_u_short(&xdr, (unsigned short *)data); break; case DATA_TYPE_INT16_ARRAY: xdr_array(&xdr, nelem, (xdrproc_t)xdr_short); break; case DATA_TYPE_UINT16_ARRAY: xdr_array(&xdr, nelem, (xdrproc_t)xdr_u_short); break; case DATA_TYPE_BOOLEAN_VALUE: case DATA_TYPE_INT32: xdr_int(&xdr, (int *)data); break; case DATA_TYPE_UINT32: xdr_u_int(&xdr, (unsigned int *)data); break; case DATA_TYPE_BOOLEAN_ARRAY: case DATA_TYPE_INT32_ARRAY: xdr_array(&xdr, nelem, (xdrproc_t)xdr_int); break; case DATA_TYPE_UINT32_ARRAY: xdr_array(&xdr, nelem, (xdrproc_t)xdr_u_int); break; case DATA_TYPE_INT64: xdr_int64(&xdr, (int64_t *)data); break; case DATA_TYPE_UINT64: xdr_uint64(&xdr, (uint64_t *)data); break; case DATA_TYPE_INT64_ARRAY: xdr_array(&xdr, nelem, (xdrproc_t)xdr_int64); break; case DATA_TYPE_UINT64_ARRAY: xdr_array(&xdr, nelem, (xdrproc_t)xdr_uint64); break; case DATA_TYPE_NVLIST: bcopy(((nvlist_t *)data)->nv_data, xdr.xdr_idx, encoded_size); break; case DATA_TYPE_NVLIST_ARRAY: { size_t size; xdr_t xdr_nv; for (uint32_t i = 0; i < nelem; i++) { xdr_nv.xdr_idx = ((nvlist_t **)data)[i]->nv_data; xdr_nv.xdr_buf = xdr_nv.xdr_idx; xdr_nv.xdr_buf_size = ((nvlist_t **)data)[i]->nv_size; if (!nvlist_size_native(&xdr_nv, &size)) return (EINVAL); bcopy(((nvlist_t **)data)[i]->nv_data, xdr.xdr_idx, size); xdr.xdr_idx += size; } break; } default: bcopy(data, xdr.xdr_idx, encoded_size); } nvl->nv_size += head.encoded_size; return (0); } int nvlist_add_boolean_value(nvlist_t *nvl, const char *name, int value) { return (nvlist_add_common(nvl, name, DATA_TYPE_BOOLEAN_VALUE, 1, &value)); } int nvlist_add_byte(nvlist_t *nvl, const char *name, uint8_t value) { return (nvlist_add_common(nvl, name, DATA_TYPE_BYTE, 1, &value)); } int nvlist_add_int8(nvlist_t *nvl, const char *name, int8_t value) { return (nvlist_add_common(nvl, name, DATA_TYPE_INT8, 1, &value)); } int nvlist_add_uint8(nvlist_t *nvl, const char *name, uint8_t value) { return (nvlist_add_common(nvl, name, DATA_TYPE_UINT8, 1, &value)); } int nvlist_add_int16(nvlist_t *nvl, const char *name, int16_t value) { return (nvlist_add_common(nvl, name, DATA_TYPE_INT16, 1, &value)); } int nvlist_add_uint16(nvlist_t *nvl, const char *name, uint16_t value) { return (nvlist_add_common(nvl, name, DATA_TYPE_UINT16, 1, &value)); } int nvlist_add_int32(nvlist_t *nvl, const char *name, int32_t value) { return (nvlist_add_common(nvl, name, DATA_TYPE_INT32, 1, &value)); } int nvlist_add_uint32(nvlist_t *nvl, const char *name, uint32_t value) { return (nvlist_add_common(nvl, name, DATA_TYPE_UINT32, 1, &value)); } int nvlist_add_int64(nvlist_t *nvl, const char *name, int64_t value) { return (nvlist_add_common(nvl, name, DATA_TYPE_INT64, 1, &value)); } int nvlist_add_uint64(nvlist_t *nvl, const char *name, uint64_t value) { return (nvlist_add_common(nvl, name, DATA_TYPE_UINT64, 1, &value)); } int nvlist_add_string(nvlist_t *nvl, const char *name, const char *value) { return (nvlist_add_common(nvl, name, DATA_TYPE_STRING, 1, value)); } int nvlist_add_boolean_array(nvlist_t *nvl, const char *name, int *a, uint32_t n) { return (nvlist_add_common(nvl, name, DATA_TYPE_BOOLEAN_ARRAY, n, a)); } int nvlist_add_byte_array(nvlist_t *nvl, const char *name, uint8_t *a, uint32_t n) { return (nvlist_add_common(nvl, name, DATA_TYPE_BYTE_ARRAY, n, a)); } int nvlist_add_int8_array(nvlist_t *nvl, const char *name, int8_t *a, uint32_t n) { return (nvlist_add_common(nvl, name, DATA_TYPE_INT8_ARRAY, n, a)); } int nvlist_add_uint8_array(nvlist_t *nvl, const char *name, uint8_t *a, uint32_t n) { return (nvlist_add_common(nvl, name, DATA_TYPE_UINT8_ARRAY, n, a)); } int nvlist_add_int16_array(nvlist_t *nvl, const char *name, int16_t *a, uint32_t n) { return (nvlist_add_common(nvl, name, DATA_TYPE_INT16_ARRAY, n, a)); } int nvlist_add_uint16_array(nvlist_t *nvl, const char *name, uint16_t *a, uint32_t n) { return (nvlist_add_common(nvl, name, DATA_TYPE_UINT16_ARRAY, n, a)); } int nvlist_add_int32_array(nvlist_t *nvl, const char *name, int32_t *a, uint32_t n) { return (nvlist_add_common(nvl, name, DATA_TYPE_INT32_ARRAY, n, a)); } int nvlist_add_uint32_array(nvlist_t *nvl, const char *name, uint32_t *a, uint32_t n) { return (nvlist_add_common(nvl, name, DATA_TYPE_UINT32_ARRAY, n, a)); } int nvlist_add_int64_array(nvlist_t *nvl, const char *name, int64_t *a, uint32_t n) { return (nvlist_add_common(nvl, name, DATA_TYPE_INT64_ARRAY, n, a)); } int nvlist_add_uint64_array(nvlist_t *nvl, const char *name, uint64_t *a, uint32_t n) { return (nvlist_add_common(nvl, name, DATA_TYPE_UINT64_ARRAY, n, a)); } int nvlist_add_string_array(nvlist_t *nvl, const char *name, char * const *a, uint32_t n) { return (nvlist_add_common(nvl, name, DATA_TYPE_STRING_ARRAY, n, a)); } int nvlist_add_nvlist(nvlist_t *nvl, const char *name, nvlist_t *val) { return (nvlist_add_common(nvl, name, DATA_TYPE_NVLIST, 1, val)); } int nvlist_add_nvlist_array(nvlist_t *nvl, const char *name, nvlist_t **a, uint32_t n) { return (nvlist_add_common(nvl, name, DATA_TYPE_NVLIST_ARRAY, n, a)); } static const char *typenames[] = { "DATA_TYPE_UNKNOWN", "DATA_TYPE_BOOLEAN", "DATA_TYPE_BYTE", "DATA_TYPE_INT16", "DATA_TYPE_UINT16", "DATA_TYPE_INT32", "DATA_TYPE_UINT32", "DATA_TYPE_INT64", "DATA_TYPE_UINT64", "DATA_TYPE_STRING", "DATA_TYPE_BYTE_ARRAY", "DATA_TYPE_INT16_ARRAY", "DATA_TYPE_UINT16_ARRAY", "DATA_TYPE_INT32_ARRAY", "DATA_TYPE_UINT32_ARRAY", "DATA_TYPE_INT64_ARRAY", "DATA_TYPE_UINT64_ARRAY", "DATA_TYPE_STRING_ARRAY", "DATA_TYPE_HRTIME", "DATA_TYPE_NVLIST", "DATA_TYPE_NVLIST_ARRAY", "DATA_TYPE_BOOLEAN_VALUE", "DATA_TYPE_INT8", "DATA_TYPE_UINT8", "DATA_TYPE_BOOLEAN_ARRAY", "DATA_TYPE_INT8_ARRAY", "DATA_TYPE_UINT8_ARRAY" }; int nvpair_type_from_name(const char *name) { unsigned i; for (i = 0; i < nitems(typenames); i++) { if (strcmp(name, typenames[i]) == 0) return (i); } return (0); } nvp_header_t * nvpair_find(nvlist_t *nv, const char *name) { nvp_header_t *nvh; nvh = NULL; while ((nvh = nvlist_next_nvpair(nv, nvh)) != NULL) { nv_string_t *nvp_name; nvp_name = (nv_string_t *)(nvh + 1); if (nvp_name->nv_size == strlen(name) && memcmp(nvp_name->nv_data, name, nvp_name->nv_size) == 0) break; } return (nvh); } void nvpair_print(nvp_header_t *nvp, unsigned int indent) { nv_string_t *nvp_name; nv_pair_data_t *nvp_data; nvlist_t nvlist; unsigned i, j; xdr_t xdr = { .xdr_op = XDR_OP_DECODE, .xdr_getint = _getint_mem, .xdr_getuint = _getuint_mem, .xdr_buf = (const uint8_t *)nvp, .xdr_idx = NULL, .xdr_buf_size = nvp->encoded_size }; nvp_name = (nv_string_t *)((uintptr_t)nvp + sizeof(*nvp)); nvp_data = (nv_pair_data_t *) NV_ALIGN4((uintptr_t)&nvp_name->nv_data[0] + nvp_name->nv_size); for (i = 0; i < indent; i++) printf(" "); printf("%s [%d] %.*s", typenames[nvp_data->nv_type], nvp_data->nv_nelem, nvp_name->nv_size, nvp_name->nv_data); xdr.xdr_idx = nvp_data->nv_data; switch (nvp_data->nv_type) { case DATA_TYPE_BYTE: case DATA_TYPE_INT8: case DATA_TYPE_UINT8: { char c; if (xdr_char(&xdr, &c)) printf(" = 0x%x\n", c); break; } case DATA_TYPE_INT16: case DATA_TYPE_UINT16: { unsigned short u; if (xdr_u_short(&xdr, &u)) printf(" = 0x%hx\n", u); break; } case DATA_TYPE_BOOLEAN_VALUE: case DATA_TYPE_INT32: case DATA_TYPE_UINT32: { unsigned u; if (xdr_u_int(&xdr, &u)) printf(" = 0x%x\n", u); break; } case DATA_TYPE_INT64: case DATA_TYPE_UINT64: { uint64_t u; if (xdr_uint64(&xdr, &u)) printf(" = 0x%jx\n", (uintmax_t)u); break; } case DATA_TYPE_INT64_ARRAY: case DATA_TYPE_UINT64_ARRAY: { uint64_t *u; if (xdr_array(&xdr, nvp_data->nv_nelem, (xdrproc_t)xdr_uint64)) { u = (uint64_t *)(nvp_data->nv_data + sizeof(unsigned)); for (i = 0; i < nvp_data->nv_nelem; i++) printf(" [%u] = 0x%jx", i, (uintmax_t)u[i]); printf("\n"); } break; } case DATA_TYPE_STRING: case DATA_TYPE_STRING_ARRAY: nvp_name = (nv_string_t *)&nvp_data->nv_data[0]; for (i = 0; i < nvp_data->nv_nelem; i++) { printf(" = \"%.*s\"\n", nvp_name->nv_size, nvp_name->nv_data); } break; case DATA_TYPE_NVLIST: printf("\n"); nvlist.nv_data = &nvp_data->nv_data[0]; nvlist_print(&nvlist, indent + 2); break; case DATA_TYPE_NVLIST_ARRAY: nvlist.nv_data = &nvp_data->nv_data[0]; for (j = 0; j < nvp_data->nv_nelem; j++) { size_t size; printf("[%d]\n", j); nvlist_print(&nvlist, indent + 2); if (j != nvp_data->nv_nelem - 1) { for (i = 0; i < indent; i++) printf(" "); printf("%s %.*s", typenames[nvp_data->nv_type], nvp_name->nv_size, nvp_name->nv_data); } xdr.xdr_idx = nvlist.nv_data; xdr.xdr_buf = xdr.xdr_idx; xdr.xdr_buf_size = nvp->encoded_size - (xdr.xdr_idx - (uint8_t *)nvp); if (!nvlist_size_native(&xdr, &size)) return; nvlist.nv_data += size; } break; default: printf("\n"); } } void nvlist_print(const nvlist_t *nvl, unsigned int indent) { nvs_data_t *data; nvp_header_t *nvp; data = (nvs_data_t *)nvl->nv_data; nvp = &data->nvl_pair; /* first pair in nvlist */ while (nvp->encoded_size != 0 && nvp->decoded_size != 0) { nvpair_print(nvp, indent); nvp = (nvp_header_t *)((uint8_t *)nvp + nvp->encoded_size); } printf("%*s\n", indent + 13, "End of nvlist"); } diff --git a/stand/powerpc/boot1.chrp/boot1.c b/stand/powerpc/boot1.chrp/boot1.c index 2804a2a2befd..cdacb05c31ce 100644 --- a/stand/powerpc/boot1.chrp/boot1.c +++ b/stand/powerpc/boot1.chrp/boot1.c @@ -1,865 +1,864 @@ /*- * Copyright (c) 1998 Robert Nordier * All rights reserved. * Copyright (c) 2001 Robert Drehmel * All rights reserved. * * Redistribution and use in source and binary forms are freely * permitted provided that the above copyright notice and this * paragraph and the following disclaimer are duplicated in all * such forms. * * This software is provided "AS IS" and without any express or * implied warranties, including, without limitation, the implied * warranties of merchantability and fitness for a particular * purpose. */ -#include #include #include #include #include #include #include #include #include "paths.h" #define BSIZEMAX 16384 typedef int putc_func_t(char c, void *arg); typedef int32_t ofwh_t; struct sp_data { char *sp_buf; u_int sp_len; u_int sp_size; }; static const char digits[] = "0123456789abcdef"; static char bootpath[128]; static char bootargs[128]; static ofwh_t bootdev; static struct fs fs; static char blkbuf[BSIZEMAX]; static unsigned int fsblks; static uint32_t fs_off; int main(int ac, char **av); static void exit(int) __dead2; static void load(const char *); static int dskread(void *, uint64_t, int); static void usage(void) __dead2; static void bcopy(const void *src, void *dst, size_t len); static void bzero(void *b, size_t len); static int domount(const char *device, int quiet); static void panic(const char *fmt, ...) __dead2; static int printf(const char *fmt, ...); static int putchar(char c, void *arg); static int vprintf(const char *fmt, va_list ap); static int vsnprintf(char *str, size_t sz, const char *fmt, va_list ap); static int __printf(const char *fmt, putc_func_t *putc, void *arg, va_list ap); static int __putc(char c, void *arg); static int __puts(const char *s, putc_func_t *putc, void *arg); static int __sputc(char c, void *arg); static char *__uitoa(char *buf, u_int val, int base); static char *__ultoa(char *buf, u_long val, int base); /* * Open Firmware interface functions */ typedef uint32_t ofwcell_t; typedef uint32_t u_ofwh_t; typedef int (*ofwfp_t)(ofwcell_t *); ofwfp_t ofw; /* the prom Open Firmware entry */ ofwh_t chosenh; void ofw_init(void *, int, ofwfp_t, char *, int); static ofwh_t ofw_finddevice(const char *); static ofwh_t ofw_open(const char *); static int ofw_close(ofwh_t); static int ofw_getprop(ofwh_t, const char *, void *, size_t); static int ofw_setprop(ofwh_t, const char *, void *, size_t); static int ofw_read(ofwh_t, void *, size_t); static int ofw_write(ofwh_t, const void *, size_t); static int ofw_claim(void *virt, size_t len, u_int align); static int ofw_seek(ofwh_t, uint64_t); static void ofw_exit(void) __dead2; ofwh_t bootdevh; ofwh_t stdinh, stdouth; /* * Note about the entry point: * * For some odd reason, the first page of the load appears to have trouble * when entering in LE. The first five instructions decode weirdly. * I suspect it is some cache weirdness between the ELF headers and .text. * * Ensure we have a gap between the start of .text and the entry as a * workaround. */ __asm(" \n\ .data \n\ .align 4 \n\ stack: \n\ .space 16384 \n\ \n\ .text \n\ /* SLOF cache hack */ \n\ .space 4096 \n\ .globl _start \n\ _start: \n\ lis %r1,stack@ha \n\ addi %r1,%r1,stack@l \n\ addi %r1,%r1,8192 \n\ \n\ b ofw_init \n\ "); ofwfp_t realofw; #if BYTE_ORDER == LITTLE_ENDIAN /* * Minimal endianness-swap trampoline for LE. */ __attribute__((naked)) int ofwtramp(void *buf, ofwfp_t cb) { __asm(" \n\ mflr %r0 \n\ stw %r0, 4(%r1) \n\ stwu %r1, -16(%r1) \n\ stw %r30, 8(%r1) \n\ /* Save current MSR for restoration post-call. */ \n\ mfmsr %r30 \n\ mr %r5, %r30 \n\ /* Remove LE bit from MSR. */ \n\ clrrwi %r5, %r5, 1 \n\ mtsrr0 %r4 \n\ mtsrr1 %r5 \n\ bcl 20, 31, .+4 /* LOAD_LR_NIA */ \n\ 1: \n\ mflr %r4 \n\ addi %r4, %r4, (2f - 1b) \n\ mtlr %r4 \n\ /* Switch to BE and transfer control to OF entry */ \n\ rfid \n\ 2: \n\ /* Control is returned here, but in BE. */ \n\ .long 0x05009f42 /* LOAD_LR_NIA */\n\ /* 0: */\n\ .long 0xa603db7f /* mtsrr1 %r30 */\n\ .long 0xa602c87f /* mflr %r30 */\n\ .long 0x1400de3b /* addi %r30, %r30, (1f - 0b) */\n\ .long 0xa603da7f /* mtsrr0 %r30 */\n\ .long 0x2400004c /* rfid */\n\ /* 1: */\n\ 1: \n\ /* Back to normal. Tidy up for return. */ \n\ lwz %r30, 8(%r1) \n\ lwz %r0, 20(%r1) \n\ addi %r1, %r1, 16 \n\ mtlr %r0 \n\ blr \n\ "); } /* * Little-endian OFW entrypoint replacement. * * We are doing all the byteswapping in one place here to save space. * This means instance handles will be byteswapped as well. */ int call_ofw(ofwcell_t* buf) { int ret, i, ncells; ncells = 3 + buf[1] + buf[2]; for (i = 0; i < ncells; i++) buf[i] = htobe32(buf[i]); ret = (ofwtramp(buf, realofw)); for (i = 0; i < ncells; i++) buf[i] = be32toh(buf[i]); return (ret); } #endif void ofw_init(void *vpd, int res, ofwfp_t openfirm, char *arg, int argl) { char *av[16]; char *p; int ac; #if BYTE_ORDER == LITTLE_ENDIAN realofw = openfirm; ofw = call_ofw; #else realofw = ofw = openfirm; #endif chosenh = ofw_finddevice("/chosen"); ofw_getprop(chosenh, "stdin", &stdinh, sizeof(stdinh)); stdinh = be32toh(stdinh); ofw_getprop(chosenh, "stdout", &stdouth, sizeof(stdouth)); stdouth = be32toh(stdouth); ofw_getprop(chosenh, "bootargs", bootargs, sizeof(bootargs)); ofw_getprop(chosenh, "bootpath", bootpath, sizeof(bootpath)); bootargs[sizeof(bootargs) - 1] = '\0'; bootpath[sizeof(bootpath) - 1] = '\0'; p = bootpath; while (*p != '\0') { /* Truncate partition ID */ if (*p == ':') { ofw_close(bootdev); *(++p) = '\0'; break; } p++; } ac = 0; p = bootargs; for (;;) { while (*p == ' ' && *p != '\0') p++; if (*p == '\0' || ac >= 16) break; av[ac++] = p; while (*p != ' ' && *p != '\0') p++; if (*p != '\0') *p++ = '\0'; } exit(main(ac, av)); } static ofwh_t ofw_finddevice(const char *name) { ofwcell_t args[] = { (ofwcell_t)"finddevice", 1, 1, (ofwcell_t)name, 0 }; if ((*ofw)(args)) { printf("ofw_finddevice: name=\"%s\"\n", name); return (1); } return (args[4]); } static int ofw_getprop(ofwh_t ofwh, const char *name, void *buf, size_t len) { ofwcell_t args[] = { (ofwcell_t)"getprop", 4, 1, (u_ofwh_t)ofwh, (ofwcell_t)name, (ofwcell_t)buf, len, 0 }; if ((*ofw)(args)) { printf("ofw_getprop: ofwh=0x%x buf=%p len=%u\n", ofwh, buf, len); return (1); } return (0); } static int ofw_setprop(ofwh_t ofwh, const char *name, void *buf, size_t len) { ofwcell_t args[] = { (ofwcell_t)"setprop", 4, 1, (u_ofwh_t)ofwh, (ofwcell_t)name, (ofwcell_t)buf, len, 0 }; if ((*ofw)(args)) { printf("ofw_setprop: ofwh=0x%x buf=%p len=%u\n", ofwh, buf, len); return (1); } return (0); } static ofwh_t ofw_open(const char *path) { ofwcell_t args[] = { (ofwcell_t)"open", 1, 1, (ofwcell_t)path, 0 }; if ((*ofw)(args)) { printf("ofw_open: path=\"%s\"\n", path); return (-1); } return (args[4]); } static int ofw_close(ofwh_t devh) { ofwcell_t args[] = { (ofwcell_t)"close", 1, 0, (u_ofwh_t)devh }; if ((*ofw)(args)) { printf("ofw_close: devh=0x%x\n", devh); return (1); } return (0); } static int ofw_claim(void *virt, size_t len, u_int align) { ofwcell_t args[] = { (ofwcell_t)"claim", 3, 1, (ofwcell_t)virt, len, align, 0, 0 }; if ((*ofw)(args)) { printf("ofw_claim: virt=%p len=%u\n", virt, len); return (1); } return (0); } static int ofw_read(ofwh_t devh, void *buf, size_t len) { ofwcell_t args[] = { (ofwcell_t)"read", 3, 1, (u_ofwh_t)devh, (ofwcell_t)buf, len, 0 }; if ((*ofw)(args)) { printf("ofw_read: devh=0x%x buf=%p len=%u\n", devh, buf, len); return (1); } return (0); } static int ofw_write(ofwh_t devh, const void *buf, size_t len) { ofwcell_t args[] = { (ofwcell_t)"write", 3, 1, (u_ofwh_t)devh, (ofwcell_t)buf, len, 0 }; if ((*ofw)(args)) { printf("ofw_write: devh=0x%x buf=%p len=%u\n", devh, buf, len); return (1); } return (0); } static int ofw_seek(ofwh_t devh, uint64_t off) { ofwcell_t args[] = { (ofwcell_t)"seek", 3, 1, (u_ofwh_t)devh, off >> 32, off, 0 }; if ((*ofw)(args)) { printf("ofw_seek: devh=0x%x off=0x%lx\n", devh, off); return (1); } return (0); } static void ofw_exit(void) { ofwcell_t args[3]; args[0] = (ofwcell_t)"exit"; args[1] = 0; args[2] = 0; for (;;) (*ofw)(args); } static void bcopy(const void *src, void *dst, size_t len) { const char *s = src; char *d = dst; while (len-- != 0) *d++ = *s++; } static void memcpy(void *dst, const void *src, size_t len) { bcopy(src, dst, len); } static void bzero(void *b, size_t len) { char *p = b; while (len-- != 0) *p++ = 0; } static int strcmp(const char *s1, const char *s2) { for (; *s1 == *s2 && *s1; s1++, s2++) ; return ((u_char)*s1 - (u_char)*s2); } #include "ufsread.c" int main(int ac, char **av) { const char *path; char bootpath_full[255]; int i, len; path = PATH_LOADER; for (i = 0; i < ac; i++) { switch (av[i][0]) { case '-': switch (av[i][1]) { default: usage(); } break; default: path = av[i]; break; } } printf(" \n>> FreeBSD/powerpc Open Firmware boot block\n" " Boot path: %s\n" " Boot loader: %s\n", bootpath, path); len = 0; while (bootpath[len] != '\0') len++; memcpy(bootpath_full,bootpath,len+1); if (bootpath_full[len-1] != ':') { /* First try full volume */ if (domount(bootpath_full,1) == 0) goto out; /* Add a : so that we try partitions if that fails */ if (bootdev > 0) ofw_close(bootdev); bootpath_full[len] = ':'; len += 1; } /* Loop through first 16 partitions to find a UFS one */ for (i = 0; i < 16; i++) { if (i < 10) { bootpath_full[len] = i + '0'; bootpath_full[len+1] = '\0'; } else { bootpath_full[len] = '1'; bootpath_full[len+1] = i - 10 + '0'; bootpath_full[len+2] = '\0'; } if (domount(bootpath_full,1) >= 0) break; if (bootdev > 0) ofw_close(bootdev); } if (i >= 16) panic("domount"); out: printf(" Boot volume: %s\n",bootpath_full); ofw_setprop(chosenh, "bootargs", bootpath_full, len+2); load(path); return (1); } static void usage(void) { printf("usage: boot device [/path/to/loader]\n"); exit(1); } static void exit(int code) { ofw_exit(); } static struct dmadat __dmadat; static int domount(const char *device, int quiet) { dmadat = &__dmadat; if ((bootdev = ofw_open(device)) == -1) { printf("domount: can't open device\n"); return (-1); } if (fsread(0, NULL, 0)) { if (!quiet) printf("domount: can't read superblock\n"); return (-1); } return (0); } static void load(const char *fname) { Elf32_Ehdr eh; Elf32_Phdr ph; caddr_t p; ufs_ino_t ino; int i; if ((ino = lookup(fname)) == 0) { printf("File %s not found\n", fname); return; } if (fsread(ino, &eh, sizeof(eh)) != sizeof(eh)) { printf("Can't read elf header\n"); return; } if (!IS_ELF(eh)) { printf("Not an ELF file\n"); return; } for (i = 0; i < eh.e_phnum; i++) { fs_off = eh.e_phoff + i * eh.e_phentsize; if (fsread(ino, &ph, sizeof(ph)) != sizeof(ph)) { printf("Can't read program header %d\n", i); return; } if (ph.p_type != PT_LOAD) continue; fs_off = ph.p_offset; p = (caddr_t)ph.p_vaddr; ofw_claim(p,(ph.p_filesz > ph.p_memsz) ? ph.p_filesz : ph.p_memsz,0); if (fsread(ino, p, ph.p_filesz) != ph.p_filesz) { printf("Can't read content of section %d\n", i); return; } if (ph.p_filesz != ph.p_memsz) bzero(p + ph.p_filesz, ph.p_memsz - ph.p_filesz); __syncicache(p, ph.p_memsz); } ofw_close(bootdev); (*(void (*)(void *, int, ofwfp_t, char *, int))eh.e_entry)(NULL, 0, realofw, NULL, 0); } static int dskread(void *buf, uint64_t lba, int nblk) { /* * The Open Firmware should open the correct partition for us. * That means, if we read from offset zero on an open instance handle, * we should read from offset zero of that partition. */ ofw_seek(bootdev, lba * DEV_BSIZE); ofw_read(bootdev, buf, nblk * DEV_BSIZE); return (0); } static void panic(const char *fmt, ...) { char buf[128]; va_list ap; va_start(ap, fmt); vsnprintf(buf, sizeof buf, fmt, ap); printf("panic: %s\n", buf); va_end(ap); exit(1); } static int printf(const char *fmt, ...) { va_list ap; int ret; va_start(ap, fmt); ret = vprintf(fmt, ap); va_end(ap); return (ret); } static int putchar(char c, void *arg) { char buf; if (c == '\n') { buf = '\r'; ofw_write(stdouth, &buf, 1); } buf = c; ofw_write(stdouth, &buf, 1); return (1); } static int vprintf(const char *fmt, va_list ap) { int ret; ret = __printf(fmt, putchar, 0, ap); return (ret); } static int vsnprintf(char *str, size_t sz, const char *fmt, va_list ap) { struct sp_data sp; int ret; sp.sp_buf = str; sp.sp_len = 0; sp.sp_size = sz; ret = __printf(fmt, __sputc, &sp, ap); return (ret); } static int __printf(const char *fmt, putc_func_t *putc, void *arg, va_list ap) { char buf[(sizeof(long) * 8) + 1]; char *nbuf; u_long ul; u_int ui; int lflag; int sflag; char *s; int pad; int ret; int c; nbuf = &buf[sizeof buf - 1]; ret = 0; while ((c = *fmt++) != 0) { if (c != '%') { ret += putc(c, arg); continue; } lflag = 0; sflag = 0; pad = 0; reswitch: c = *fmt++; switch (c) { case '#': sflag = 1; goto reswitch; case '%': ret += putc('%', arg); break; case 'c': c = va_arg(ap, int); ret += putc(c, arg); break; case 'd': if (lflag == 0) { ui = (u_int)va_arg(ap, int); if (ui < (int)ui) { ui = -ui; ret += putc('-', arg); } s = __uitoa(nbuf, ui, 10); } else { ul = (u_long)va_arg(ap, long); if (ul < (long)ul) { ul = -ul; ret += putc('-', arg); } s = __ultoa(nbuf, ul, 10); } ret += __puts(s, putc, arg); break; case 'l': lflag = 1; goto reswitch; case 'o': if (lflag == 0) { ui = (u_int)va_arg(ap, u_int); s = __uitoa(nbuf, ui, 8); } else { ul = (u_long)va_arg(ap, u_long); s = __ultoa(nbuf, ul, 8); } ret += __puts(s, putc, arg); break; case 'p': ul = (u_long)va_arg(ap, void *); s = __ultoa(nbuf, ul, 16); ret += __puts("0x", putc, arg); ret += __puts(s, putc, arg); break; case 's': s = va_arg(ap, char *); ret += __puts(s, putc, arg); break; case 'u': if (lflag == 0) { ui = va_arg(ap, u_int); s = __uitoa(nbuf, ui, 10); } else { ul = va_arg(ap, u_long); s = __ultoa(nbuf, ul, 10); } ret += __puts(s, putc, arg); break; case 'x': if (lflag == 0) { ui = va_arg(ap, u_int); s = __uitoa(nbuf, ui, 16); } else { ul = va_arg(ap, u_long); s = __ultoa(nbuf, ul, 16); } if (sflag) ret += __puts("0x", putc, arg); ret += __puts(s, putc, arg); break; case '0': case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': case '9': pad = pad * 10 + c - '0'; goto reswitch; default: break; } } return (ret); } static int __sputc(char c, void *arg) { struct sp_data *sp; sp = arg; if (sp->sp_len < sp->sp_size) sp->sp_buf[sp->sp_len++] = c; sp->sp_buf[sp->sp_len] = '\0'; return (1); } static int __puts(const char *s, putc_func_t *putc, void *arg) { const char *p; int ret; ret = 0; for (p = s; *p != '\0'; p++) ret += putc(*p, arg); return (ret); } static char * __uitoa(char *buf, u_int ui, int base) { char *p; p = buf; *p = '\0'; do *--p = digits[ui % base]; while ((ui /= base) != 0); return (p); } static char * __ultoa(char *buf, u_long ul, int base) { char *p; p = buf; *p = '\0'; do *--p = digits[ul % base]; while ((ul /= base) != 0); return (p); } diff --git a/stand/powerpc/ofw/elf_freebsd.c b/stand/powerpc/ofw/elf_freebsd.c index 5cd8a974cfb6..21ab834f76fa 100644 --- a/stand/powerpc/ofw/elf_freebsd.c +++ b/stand/powerpc/ofw/elf_freebsd.c @@ -1,105 +1,104 @@ /*- * Copyright (c) 2001 Benno Rice * 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 #include #include #include #if defined(__powerpc__) #include #endif #include #include "bootstrap.h" #include "libofw.h" #include "openfirm.h" #include "modinfo.h" extern char end[]; extern vm_offset_t reloc; /* From /conf.c */ int __elfN(ofw_loadfile)(char *filename, uint64_t dest, struct preloaded_file **result) { int r; r = __elfN(loadfile)(filename, dest, result); if (r != 0) return (r); #if defined(__powerpc__) /* * No need to sync the icache for modules: this will * be done by the kernel after relocation. */ if (!strcmp((*result)->f_type, "elf kernel")) __syncicache((void *) (*result)->f_addr, (*result)->f_size); #endif return (0); } int __elfN(ofw_exec)(struct preloaded_file *fp) { struct file_metadata *fmp; vm_offset_t mdp, dtbp; Elf_Ehdr *e; int error; intptr_t entry; if ((fmp = file_findmetadata(fp, MODINFOMD_ELFHDR)) == NULL) { return(EFTYPE); } e = (Elf_Ehdr *)&fmp->md_data; entry = e->e_entry; if ((error = md_load(fp->f_args, &mdp, &dtbp)) != 0) return (error); printf("Kernel entry at 0x%x ...\n", entry); dev_cleanup(); if (dtbp != 0) { OF_quiesce(); ((int (*)(u_long, u_long, u_long, void *, u_long))entry)(dtbp, 0, 0, (void *)mdp, 0xfb5d104d); } else { OF_chain((void *)reloc, end - (char *)reloc, (void *)entry, (void *)mdp, 0xfb5d104d); } panic("exec returned"); } struct file_format ofw_elf = { __elfN(ofw_loadfile), __elfN(ofw_exec) }; diff --git a/stand/uboot/copy.c b/stand/uboot/copy.c index cc12cfaeef35..e5e985c85b27 100644 --- a/stand/uboot/copy.c +++ b/stand/uboot/copy.c @@ -1,165 +1,164 @@ /*- * Copyright (c) 1998 Michael Smith * Copyright (c) 2007 Semihalf, Rafal Jaworowski * 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 #include #include #include "api_public.h" #include "glue.h" #include "libuboot.h" /* * MD primitives supporting placement of module data */ #ifdef __arm__ #define KERN_ALIGN (2 * 1024 * 1024) #else #define KERN_ALIGN PAGE_SIZE #endif /* * Avoid low memory, u-boot puts things like args and dtb blobs there. */ #define KERN_MINADDR max(KERN_ALIGN, (1024 * 1024)) extern void _start(void); /* ubldr entry point address. */ /* * This is called for every object loaded (kernel, module, dtb file, etc). The * expected return value is the next address at or after the given addr which is * appropriate for loading the given object described by type and data. On each * call the addr is the next address following the previously loaded object. * * The first call is for loading the kernel, and the addr argument will be zero, * and we search for a big block of ram to load the kernel and modules. * * On subsequent calls the addr will be non-zero, and we just round it up so * that each object begins on a page boundary. */ uint64_t uboot_loadaddr(u_int type, void *data, uint64_t addr) { struct sys_info *si; uint64_t sblock, eblock, subldr, eubldr; uint64_t biggest_block, this_block; uint64_t biggest_size, this_size; int i; char *envstr; if (addr == 0) { /* * If the loader_kernaddr environment variable is set, blindly * honor it. It had better be right. We force interpretation * of the value in base-16 regardless of any leading 0x prefix, * because that's the U-Boot convention. */ envstr = ub_env_get("loader_kernaddr"); if (envstr != NULL) return (strtoul(envstr, NULL, 16)); /* * Find addr/size of largest DRAM block. Carve our own address * range out of the block, because loading the kernel over the * top ourself is a poor memory-conservation strategy. Avoid * memory at beginning of the first block of physical ram, * since u-boot likes to pass args and data there. Assume that * u-boot has moved itself to the very top of ram and * optimistically assume that we won't run into it up there. */ if ((si = ub_get_sys_info()) == NULL) panic("could not retrieve system info"); biggest_block = 0; biggest_size = 0; subldr = rounddown2((uintptr_t)_start, KERN_ALIGN); eubldr = roundup2((uint64_t)uboot_heap_end, KERN_ALIGN); for (i = 0; i < si->mr_no; i++) { if (si->mr[i].flags != MR_ATTR_DRAM) continue; sblock = roundup2((uint64_t)si->mr[i].start, KERN_ALIGN); eblock = rounddown2((uint64_t)si->mr[i].start + si->mr[i].size, KERN_ALIGN); if (biggest_size == 0) sblock += KERN_MINADDR; if (subldr >= sblock && subldr < eblock) { if (subldr - sblock > eblock - eubldr) { this_block = sblock; this_size = subldr - sblock; } else { this_block = eubldr; this_size = eblock - eubldr; } } else if (subldr < sblock && eubldr < eblock) { /* Loader is below or engulfs the sblock */ this_block = (eubldr < sblock) ? sblock : eubldr; this_size = eblock - this_block; } else { this_block = 0; this_size = 0; } if (biggest_size < this_size) { biggest_block = this_block; biggest_size = this_size; } } if (biggest_size == 0) panic("Not enough DRAM to load kernel"); #if 0 printf("Loading kernel into region 0x%08jx-0x%08jx (%ju MiB)\n", (uintmax_t)biggest_block, (uintmax_t)biggest_block + biggest_size - 1, (uintmax_t)biggest_size / 1024 / 1024); #endif return (biggest_block); } return roundup2(addr, PAGE_SIZE); } ssize_t uboot_copyin(const void *src, vm_offset_t dest, const size_t len) { bcopy(src, (void *)dest, len); return (len); } ssize_t uboot_copyout(const vm_offset_t src, void *dest, const size_t len) { bcopy((void *)src, dest, len); return (len); } ssize_t uboot_readin(readin_handle_t fd, vm_offset_t dest, const size_t len) { return (VECTX_READ(fd, (void *)dest, len)); } diff --git a/stand/uboot/elf_freebsd.c b/stand/uboot/elf_freebsd.c index 42ed4080c363..6c764d143881 100644 --- a/stand/uboot/elf_freebsd.c +++ b/stand/uboot/elf_freebsd.c @@ -1,93 +1,92 @@ /*- * Copyright (c) 2001 Benno Rice * Copyright (c) 2007 Semihalf, Rafal Jaworowski * 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 #include #include #include #include #include #include "bootstrap.h" #include "libuboot.h" #include "modinfo.h" int __elfN(uboot_load)(char *filename, uint64_t dest, struct preloaded_file **result) { int r; r = __elfN(loadfile)(filename, dest, result); if (r != 0) return (r); #if defined(__powerpc__) /* * No need to sync the icache for modules: this will * be done by the kernel after relocation. */ if (!strcmp((*result)->f_type, "elf kernel")) __syncicache((void *) (*result)->f_addr, (*result)->f_size); #endif return (0); } int __elfN(uboot_exec)(struct preloaded_file *fp) { struct file_metadata *fmp; vm_offset_t mdp; Elf_Ehdr *e; int error; void (*entry)(void *); if ((fmp = file_findmetadata(fp, MODINFOMD_ELFHDR)) == NULL) return (EFTYPE); e = (Elf_Ehdr *)&fmp->md_data; if ((error = md_load(fp->f_args, &mdp, NULL)) != 0) return (error); entry = (void *)e->e_entry; printf("Kernel entry at %p...\n", entry); dev_cleanup(); printf("Kernel args: %s\n", fp->f_args); (*entry)((void *)mdp); panic("exec returned"); } struct file_format uboot_elf = { __elfN(uboot_load), __elfN(uboot_exec) }; diff --git a/stand/uboot/glue.c b/stand/uboot/glue.c index 782636817001..8d8e2a57d1fc 100644 --- a/stand/uboot/glue.c +++ b/stand/uboot/glue.c @@ -1,564 +1,563 @@ /*- * Copyright (c) 2007-2008 Semihalf, Rafal Jaworowski * 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 #include #include #include "api_public.h" #include "glue.h" #ifdef DEBUG #define debugf(fmt, args...) do { printf("%s(): ", __func__); printf(fmt,##args); } while (0) #else #define debugf(fmt, args...) #endif /* Some random address used by U-Boot. */ extern long uboot_address; static int valid_sig(struct api_signature *sig) { uint32_t checksum; struct api_signature s; if (sig == NULL) return (0); /* * Clear the checksum field (in the local copy) so as to calculate the * CRC with the same initial contents as at the time when the sig was * produced */ s = *sig; s.checksum = crc32(0, Z_NULL, 0); checksum = crc32(s.checksum, (void *)&s, sizeof(struct api_signature)); if (checksum != sig->checksum) return (0); return (1); } /* * Checks to see if API signature's address was given to us as a command line * argument by U-Boot. * * returns 1/0 depending on found/not found result */ int api_parse_cmdline_sig(int argc, char **argv, struct api_signature **sig) { unsigned long api_address; int c; api_address = 0; opterr = 0; optreset = 1; optind = 1; while ((c = getopt (argc, argv, "a:")) != -1) switch (c) { case 'a': api_address = strtoul(optarg, NULL, 16); break; default: break; } if (api_address != 0) { *sig = (struct api_signature *)api_address; if (valid_sig(*sig)) return (1); } return (0); } /* * Searches for the U-Boot API signature * * returns 1/0 depending on found/not found result */ int api_search_sig(struct api_signature **sig) { unsigned char *sp, *spend; if (sig == NULL) return (0); if (uboot_address == 0) uboot_address = 255 * 1024 * 1024; sp = (void *)(uboot_address & API_SIG_SEARCH_MASK); spend = sp + API_SIG_SEARCH_LEN - API_SIG_MAGLEN; while (sp < spend) { if (!bcmp(sp, API_SIG_MAGIC, API_SIG_MAGLEN)) { *sig = (struct api_signature *)sp; if (valid_sig(*sig)) return (1); } sp += API_SIG_MAGLEN; } *sig = NULL; return (0); } /**************************************** * * console * ****************************************/ int ub_getc(void) { int c; if (!syscall(API_GETC, NULL, &c)) return (-1); return (c); } int ub_tstc(void) { int t; if (!syscall(API_TSTC, NULL, &t)) return (-1); return (t); } void ub_putc(const char c) { syscall(API_PUTC, NULL, &c); } void ub_puts(const char *s) { syscall(API_PUTS, NULL, s); } /**************************************** * * system * ****************************************/ void ub_reset(void) { syscall(API_RESET, NULL); while (1); /* fallback if API_RESET failed */ __unreachable(); } static struct mem_region mr[UB_MAX_MR]; static struct sys_info si; struct sys_info * ub_get_sys_info(void) { int err = 0; memset(&si, 0, sizeof(struct sys_info)); si.mr = mr; si.mr_no = UB_MAX_MR; memset(&mr, 0, sizeof(mr)); if (!syscall(API_GET_SYS_INFO, &err, &si)) return (NULL); return ((err) ? NULL : &si); } /**************************************** * * timing * ****************************************/ void ub_udelay(unsigned long usec) { syscall(API_UDELAY, NULL, &usec); } unsigned long ub_get_timer(unsigned long base) { unsigned long cur; if (!syscall(API_GET_TIMER, NULL, &cur, &base)) return (0); return (cur); } /**************************************************************************** * * devices * * Devices are identified by handles: numbers 0, 1, 2, ..., UB_MAX_DEV-1 * ***************************************************************************/ static struct device_info devices[UB_MAX_DEV]; struct device_info * ub_dev_get(int i) { return ((i < 0 || i >= UB_MAX_DEV) ? NULL : &devices[i]); } /* * Enumerates the devices: fills out device_info elements in the devices[] * array. * * returns: number of devices found */ int ub_dev_enum(void) { struct device_info *di; int n = 0; memset(&devices, 0, sizeof(struct device_info) * UB_MAX_DEV); di = &devices[0]; if (!syscall(API_DEV_ENUM, NULL, di)) return (0); while (di->cookie != NULL) { if (++n >= UB_MAX_DEV) break; /* take another device_info */ di++; /* pass on the previous cookie */ di->cookie = devices[n - 1].cookie; if (!syscall(API_DEV_ENUM, NULL, di)) return (0); } return (n); } /* * handle: 0-based id of the device * * returns: 0 when OK, err otherwise */ int ub_dev_open(int handle) { struct device_info *di; int err = 0; if (handle < 0 || handle >= UB_MAX_DEV) return (API_EINVAL); di = &devices[handle]; if (!syscall(API_DEV_OPEN, &err, di)) return (-1); return (err); } int ub_dev_close(int handle) { struct device_info *di; if (handle < 0 || handle >= UB_MAX_DEV) return (API_EINVAL); di = &devices[handle]; if (!syscall(API_DEV_CLOSE, NULL, di)) return (-1); return (0); } /* * Validates device for read/write, it has to: * * - have sane handle * - be opened * * returns: 0/1 accordingly */ static int dev_valid(int handle) { if (handle < 0 || handle >= UB_MAX_DEV) return (0); if (devices[handle].state != DEV_STA_OPEN) return (0); return (1); } static int dev_stor_valid(int handle) { if (!dev_valid(handle)) return (0); if (!(devices[handle].type & DEV_TYP_STOR)) return (0); return (1); } int ub_dev_read(int handle, void *buf, lbasize_t len, lbastart_t start, lbasize_t *rlen) { struct device_info *di; lbasize_t act_len; int err = 0; if (!dev_stor_valid(handle)) return (API_ENODEV); di = &devices[handle]; if (!syscall(API_DEV_READ, &err, di, buf, &len, &start, &act_len)) return (API_ESYSC); if (!err && rlen) *rlen = act_len; return (err); } static int dev_net_valid(int handle) { if (!dev_valid(handle)) return (0); if (devices[handle].type != DEV_TYP_NET) return (0); return (1); } int ub_dev_recv(int handle, void *buf, int len, int *rlen) { struct device_info *di; int err = 0, act_len; if (!dev_net_valid(handle)) return (API_ENODEV); di = &devices[handle]; if (!syscall(API_DEV_READ, &err, di, buf, &len, &act_len)) return (API_ESYSC); if (!err) *rlen = act_len; return (err); } int ub_dev_send(int handle, void *buf, int len) { struct device_info *di; int err = 0; if (!dev_net_valid(handle)) return (API_ENODEV); di = &devices[handle]; if (!syscall(API_DEV_WRITE, &err, di, buf, &len)) return (API_ESYSC); return (err); } char * ub_stor_type(int type) { if (type & DT_STOR_IDE) return ("IDE"); if (type & DT_STOR_SCSI) return ("SCSI"); if (type & DT_STOR_USB) return ("USB"); if (type & DT_STOR_MMC) return ("MMC"); if (type & DT_STOR_SATA) return ("SATA"); return ("Unknown"); } char * ub_mem_type(int flags) { switch (flags & 0x000F) { case MR_ATTR_FLASH: return ("FLASH"); case MR_ATTR_DRAM: return ("DRAM"); case MR_ATTR_SRAM: return ("SRAM"); default: return ("Unknown"); } } void ub_dump_di(int handle) { struct device_info *di = ub_dev_get(handle); int i; printf("device info (%d):\n", handle); printf(" cookie\t= %p\n", di->cookie); printf(" type\t\t= 0x%08x\n", di->type); if (di->type == DEV_TYP_NET) { printf(" hwaddr\t= "); for (i = 0; i < 6; i++) printf("%02x ", di->di_net.hwaddr[i]); printf("\n"); } else if (di->type & DEV_TYP_STOR) { printf(" type\t\t= %s\n", ub_stor_type(di->type)); printf(" blk size\t\t= %ld\n", di->di_stor.block_size); printf(" blk count\t\t= %ld\n", di->di_stor.block_count); } } void ub_dump_si(struct sys_info *si) { int i; printf("sys info:\n"); printf(" clkbus\t= %ld MHz\n", si->clk_bus / 1000 / 1000); printf(" clkcpu\t= %ld MHz\n", si->clk_cpu / 1000 / 1000); printf(" bar\t\t= 0x%08lx\n", si->bar); printf("---\n"); for (i = 0; i < si->mr_no; i++) { if (si->mr[i].flags == 0) break; printf(" start\t= 0x%08lx\n", si->mr[i].start); printf(" size\t= 0x%08lx\n", si->mr[i].size); printf(" type\t= %s\n", ub_mem_type(si->mr[i].flags)); printf("---\n"); } } /**************************************** * * env vars * ****************************************/ char * ub_env_get(const char *name) { char *value; if (!syscall(API_ENV_GET, NULL, name, &value)) return (NULL); return (value); } void ub_env_set(const char *name, char *value) { syscall(API_ENV_SET, NULL, name, value); } static char env_name[256]; const char * ub_env_enum(const char *last) { const char *env, *str; int i; /* * It's OK to pass only the name piece as last (and not the whole * 'name=val' string), since the API_ENUM_ENV call uses envmatch() * internally, which handles such case */ env = NULL; if (!syscall(API_ENV_ENUM, NULL, last, &env)) return (NULL); if (env == NULL || last == env) /* no more env. variables to enumerate */ return (NULL); /* next enumerated env var */ memset(env_name, 0, 256); for (i = 0, str = env; *str != '=' && *str != '\0';) env_name[i++] = *str++; env_name[i] = '\0'; return (env_name); } diff --git a/stand/uboot/main.c b/stand/uboot/main.c index 0106243a0e1e..6147757ced71 100644 --- a/stand/uboot/main.c +++ b/stand/uboot/main.c @@ -1,732 +1,731 @@ /*- * Copyright (c) 2000 Benno Rice * Copyright (c) 2000 Stephane Potvin * Copyright (c) 2007-2008 Semihalf, Rafal Jaworowski * 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 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 #include #include "api_public.h" #include "bootstrap.h" #include "glue.h" #include "libuboot.h" #ifndef nitems #define nitems(x) (sizeof((x)) / sizeof((x)[0])) #endif #ifndef HEAP_SIZE #define HEAP_SIZE (2 * 1024 * 1024) #endif struct uboot_devdesc currdev; struct arch_switch archsw; /* MI/MD interface boundary */ int devs_no; uintptr_t uboot_heap_start; uintptr_t uboot_heap_end; struct device_type { const char *name; int type; } device_types[] = { { "disk", DEV_TYP_STOR }, { "ide", DEV_TYP_STOR | DT_STOR_IDE }, { "mmc", DEV_TYP_STOR | DT_STOR_MMC }, { "sata", DEV_TYP_STOR | DT_STOR_SATA }, { "scsi", DEV_TYP_STOR | DT_STOR_SCSI }, { "usb", DEV_TYP_STOR | DT_STOR_USB }, { "net", DEV_TYP_NET } }; extern char end[]; extern unsigned char _etext[]; extern unsigned char _edata[]; extern unsigned char __bss_start[]; extern unsigned char __sbss_start[]; extern unsigned char __sbss_end[]; extern unsigned char _end[]; #ifdef LOADER_FDT_SUPPORT extern int command_fdt_internal(int argc, char *argv[]); #endif static void dump_sig(struct api_signature *sig) { #ifdef DEBUG printf("signature:\n"); printf(" version\t= %d\n", sig->version); printf(" checksum\t= 0x%08x\n", sig->checksum); printf(" sc entry\t= 0x%08x\n", sig->syscall); #endif } static void dump_addr_info(void) { #ifdef DEBUG printf("\naddresses info:\n"); printf(" _etext (sdata) = 0x%08x\n", (uint32_t)_etext); printf(" _edata = 0x%08x\n", (uint32_t)_edata); printf(" __sbss_start = 0x%08x\n", (uint32_t)__sbss_start); printf(" __sbss_end = 0x%08x\n", (uint32_t)__sbss_end); printf(" __sbss_start = 0x%08x\n", (uint32_t)__bss_start); printf(" _end = 0x%08x\n", (uint32_t)_end); printf(" syscall entry = 0x%08x\n", (uint32_t)syscall_ptr); #endif } static uint64_t memsize(struct sys_info *si, int flags) { uint64_t size; int i; size = 0; for (i = 0; i < si->mr_no; i++) if (si->mr[i].flags == flags && si->mr[i].size) size += (si->mr[i].size); return (size); } static void meminfo(void) { uint64_t size; struct sys_info *si; int t[3] = { MR_ATTR_DRAM, MR_ATTR_FLASH, MR_ATTR_SRAM }; int i; if ((si = ub_get_sys_info()) == NULL) panic("could not retrieve system info"); for (i = 0; i < 3; i++) { size = memsize(si, t[i]); if (size > 0) printf("%s: %juMB\n", ub_mem_type(t[i]), (uintmax_t)(size / 1024 / 1024)); } } static const char * get_device_type(const char *devstr, int *devtype) { int i; int namelen; struct device_type *dt; if (devstr) { for (i = 0; i < nitems(device_types); i++) { dt = &device_types[i]; namelen = strlen(dt->name); if (strncmp(dt->name, devstr, namelen) == 0) { *devtype = dt->type; return (devstr + namelen); } } printf("Unknown device type '%s'\n", devstr); } *devtype = DEV_TYP_NONE; return (NULL); } static const char * device_typename(int type) { int i; for (i = 0; i < nitems(device_types); i++) if (device_types[i].type == type) return (device_types[i].name); return (""); } /* * Parse a device string into type, unit, slice and partition numbers. A * returned value of -1 for type indicates a search should be done for the * first loadable device, otherwise a returned value of -1 for unit * indicates a search should be done for the first loadable device of the * given type. * * The returned values for slice and partition are interpreted by * disk_open(). * * The device string can be a standard loader(8) disk specifier: * * disks disk0s1 * disks disk1s2a * diskp disk0p4 * * or one of the following formats: * * Valid device strings: For device types: * * DEV_TYP_STOR, DEV_TYP_NET * DEV_TYP_STOR, DEV_TYP_NET * : DEV_TYP_STOR, DEV_TYP_NET * : DEV_TYP_STOR * :. DEV_TYP_STOR * :. DEV_TYP_STOR * * For valid type names, see the device_types array, above. * * Slice numbers are 1-based. 0 is a wildcard. */ static void get_load_device(int *type, int *unit, int *slice, int *partition) { struct disk_devdesc *dev; char *devstr; const char *p; char *endp; *type = DEV_TYP_NONE; *unit = -1; *slice = D_SLICEWILD; *partition = D_PARTWILD; devstr = ub_env_get("loaderdev"); if (devstr == NULL) { printf("U-Boot env: loaderdev not set, will probe all devices.\n"); return; } printf("U-Boot env: loaderdev='%s'\n", devstr); p = get_device_type(devstr, type); /* * If type is DEV_TYP_STOR we have a disk-like device. If the remainder * of the string contains spaces, dots, or a colon in any location other * than the last char, it's legacy format. Otherwise it might be * standard loader(8) format (e.g., disk0s2a or mmc1p12), so try to * parse the remainder of the string as such, and if it works, return * those results. Otherwise we'll fall through to the code that parses * the legacy format. * * disk_parsedev now assumes that it points to the start of the device * name, but since it doesn't know about uboot's usage, just subtract 4 * since it always adds 4. This is the least-bad solution since it makes * all the other loader code easier (might be better to create a fake * 'disk...' string, but that's more work than uboot is worth). */ if (*type & DEV_TYP_STOR) { size_t len = strlen(p); if (strcspn(p, " .") == len && strcspn(p, ":") >= len - 1 && disk_parsedev((struct devdesc **)&dev, p - 4, NULL) == 0) { /* Hack */ *unit = dev->dd.d_unit; *slice = dev->d_slice; *partition = dev->d_partition; free(dev); return; } } /* Ignore optional spaces after the device name. */ while (*p == ' ') p++; /* Unknown device name, or a known name without unit number. */ if ((*type == DEV_TYP_NONE) || (*p == '\0')) { return; } /* Malformed unit number. */ if (!isdigit(*p)) { *type = DEV_TYP_NONE; return; } /* Guaranteed to extract a number from the string, as *p is a digit. */ *unit = strtol(p, &endp, 10); p = endp; /* Known device name with unit number and nothing else. */ if (*p == '\0') { return; } /* Device string is malformed beyond unit number. */ if (*p != ':') { *type = DEV_TYP_NONE; *unit = -1; return; } p++; /* No slice and partition specification. */ if ('\0' == *p ) return; /* Only DEV_TYP_STOR devices can have a slice specification. */ if (!(*type & DEV_TYP_STOR)) { *type = DEV_TYP_NONE; *unit = -1; return; } *slice = strtoul(p, &endp, 10); /* Malformed slice number. */ if (p == endp) { *type = DEV_TYP_NONE; *unit = -1; *slice = D_SLICEWILD; return; } p = endp; /* No partition specification. */ if (*p == '\0') return; /* Device string is malformed beyond slice number. */ if (*p != '.') { *type = DEV_TYP_NONE; *unit = -1; *slice = D_SLICEWILD; return; } p++; /* No partition specification. */ if (*p == '\0') return; *partition = strtol(p, &endp, 10); p = endp; /* Full, valid device string. */ if (*endp == '\0') return; /* Junk beyond partition number. */ *type = DEV_TYP_NONE; *unit = -1; *slice = D_SLICEWILD; *partition = D_PARTWILD; } static void print_disk_probe_info() { char slice[32]; char partition[32]; if (currdev.d_disk.d_slice == D_SLICENONE) strlcpy(slice, "", sizeof(slice)); else if (currdev.d_disk.d_slice == D_SLICEWILD) strlcpy(slice, "", sizeof(slice)); else snprintf(slice, sizeof(slice), "%d", currdev.d_disk.d_slice); if (currdev.d_disk.d_partition == D_PARTNONE) strlcpy(partition, "", sizeof(partition)); else if (currdev.d_disk.d_partition == D_PARTWILD) strlcpy(partition, "", sizeof(partition)); else snprintf(partition, sizeof(partition), "%d", currdev.d_disk.d_partition); printf(" Checking unit=%d slice=%s partition=%s...", currdev.dd.d_unit, slice, partition); } static int probe_disks(int devidx, int load_type, int load_unit, int load_slice, int load_partition) { int open_result, unit; struct open_file f; currdev.d_disk.d_slice = load_slice; currdev.d_disk.d_partition = load_partition; f.f_devdata = &currdev; open_result = -1; if (load_type == -1) { printf(" Probing all disk devices...\n"); /* Try each disk in succession until one works. */ for (currdev.dd.d_unit = 0; currdev.dd.d_unit < UB_MAX_DEV; currdev.dd.d_unit++) { print_disk_probe_info(); open_result = devsw[devidx]->dv_open(&f, &currdev); if (open_result == 0) { printf(" good.\n"); return (0); } printf("\n"); } return (-1); } if (load_unit == -1) { printf(" Probing all %s devices...\n", device_typename(load_type)); /* Try each disk of given type in succession until one works. */ for (unit = 0; unit < UB_MAX_DEV; unit++) { currdev.dd.d_unit = uboot_diskgetunit(load_type, unit); if (currdev.dd.d_unit == -1) break; print_disk_probe_info(); open_result = devsw[devidx]->dv_open(&f, &currdev); if (open_result == 0) { printf(" good.\n"); return (0); } printf("\n"); } return (-1); } if ((currdev.dd.d_unit = uboot_diskgetunit(load_type, load_unit)) != -1) { print_disk_probe_info(); open_result = devsw[devidx]->dv_open(&f,&currdev); if (open_result == 0) { printf(" good.\n"); return (0); } printf("\n"); } printf(" Requested disk type/unit/slice/partition not found\n"); return (-1); } int main(int argc, char **argv) { struct api_signature *sig = NULL; int load_type, load_unit, load_slice, load_partition; int i; const char *ldev; /* * We first check if a command line argument was passed to us containing * API's signature address. If it wasn't then we try to search for the * API signature via the usual hinted address. * If we can't find the magic signature and related info, exit with a * unique error code that U-Boot reports as "## Application terminated, * rc = 0xnnbadab1". Hopefully 'badab1' looks enough like "bad api" to * provide a clue. It's better than 0xffffffff anyway. */ if (!api_parse_cmdline_sig(argc, argv, &sig) && !api_search_sig(&sig)) return (0x01badab1); syscall_ptr = sig->syscall; if (syscall_ptr == NULL) return (0x02badab1); if (sig->version > API_SIG_VERSION) return (0x03badab1); /* Clear BSS sections */ bzero(__sbss_start, __sbss_end - __sbss_start); bzero(__bss_start, _end - __bss_start); /* * Initialise the heap as early as possible. Once this is done, * alloc() is usable. We are using the stack u-boot set up near the top * of physical ram; hopefully there is sufficient space between the end * of our bss and the bottom of the u-boot stack to avoid overlap. */ uboot_heap_start = round_page((uintptr_t)end); uboot_heap_end = uboot_heap_start + HEAP_SIZE; setheap((void *)uboot_heap_start, (void *)uboot_heap_end); /* * Set up console. */ cons_probe(); printf("Compatible U-Boot API signature found @%p\n", sig); printf("\n%s", bootprog_info); printf("\n"); dump_sig(sig); dump_addr_info(); meminfo(); archsw.arch_loadaddr = uboot_loadaddr; archsw.arch_getdev = uboot_getdev; archsw.arch_copyin = uboot_copyin; archsw.arch_copyout = uboot_copyout; archsw.arch_readin = uboot_readin; archsw.arch_autoload = uboot_autoload; /* Set up currdev variable to have hooks in place. */ env_setenv("currdev", EV_VOLATILE, "", uboot_setcurrdev, env_nounset); /* * Enumerate U-Boot devices */ if ((devs_no = ub_dev_enum()) == 0) { printf("no U-Boot devices found"); goto do_interact; } printf("Number of U-Boot devices: %d\n", devs_no); get_load_device(&load_type, &load_unit, &load_slice, &load_partition); /* * March through the device switch probing for things. */ for (i = 0; devsw[i] != NULL; i++) { if (devsw[i]->dv_init == NULL) continue; if ((devsw[i]->dv_init)() != 0) continue; printf("Found U-Boot device: %s\n", devsw[i]->dv_name); currdev.dd.d_dev = devsw[i]; currdev.dd.d_unit = 0; if ((load_type == DEV_TYP_NONE || (load_type & DEV_TYP_STOR)) && strcmp(devsw[i]->dv_name, "disk") == 0) { if (probe_disks(i, load_type, load_unit, load_slice, load_partition) == 0) break; } if ((load_type == DEV_TYP_NONE || (load_type & DEV_TYP_NET)) && strcmp(devsw[i]->dv_name, "net") == 0) break; } /* * If we couldn't find a boot device, return an error to u-boot. * U-boot may be running a boot script that can try something different * so returning an error is better than forcing a reboot. */ if (devsw[i] == NULL) { printf("No boot device found!\n"); return (0xbadef1ce); } ldev = devformat(&currdev.dd); env_setenv("currdev", EV_VOLATILE, ldev, uboot_setcurrdev, env_nounset); env_setenv("loaddev", EV_VOLATILE, ldev, env_noset, env_nounset); printf("Booting from %s\n", ldev); do_interact: setenv("LINES", "24", 1); /* optional */ setenv("prompt", "loader>", 1); #ifdef __powerpc__ setenv("usefdt", "1", 1); #endif interact(); /* doesn't return */ return (0); } COMMAND_SET(heap, "heap", "show heap usage", command_heap); static int command_heap(int argc, char *argv[]) { printf("heap base at %p, top at %p, used %td\n", end, sbrk(0), sbrk(0) - end); return (CMD_OK); } COMMAND_SET(reboot, "reboot", "reboot the system", command_reboot); static int command_reboot(int argc, char *argv[]) { printf("Resetting...\n"); ub_reset(); printf("Reset failed!\n"); while (1); __unreachable(); } COMMAND_SET(devinfo, "devinfo", "show U-Boot devices", command_devinfo); static int command_devinfo(int argc, char *argv[]) { int i; if ((devs_no = ub_dev_enum()) == 0) { command_errmsg = "no U-Boot devices found!?"; return (CMD_ERROR); } printf("U-Boot devices:\n"); for (i = 0; i < devs_no; i++) { ub_dump_di(i); printf("\n"); } return (CMD_OK); } COMMAND_SET(sysinfo, "sysinfo", "show U-Boot system info", command_sysinfo); static int command_sysinfo(int argc, char *argv[]) { struct sys_info *si; if ((si = ub_get_sys_info()) == NULL) { command_errmsg = "could not retrieve U-Boot sys info!?"; return (CMD_ERROR); } printf("U-Boot system info:\n"); ub_dump_si(si); return (CMD_OK); } enum ubenv_action { UBENV_UNKNOWN, UBENV_SHOW, UBENV_IMPORT }; static void handle_uboot_env_var(enum ubenv_action action, const char * var) { char ldvar[128]; const char *val; char *wrk; int len; /* * On an import with the variable name formatted as ldname=ubname, * import the uboot variable ubname into the loader variable ldname, * otherwise the historical behavior is to import to uboot.ubname. */ if (action == UBENV_IMPORT) { len = strcspn(var, "="); if (len == 0) { printf("name cannot start with '=': '%s'\n", var); return; } if (var[len] == 0) { strcpy(ldvar, "uboot."); strncat(ldvar, var, sizeof(ldvar) - 7); } else { len = MIN(len, sizeof(ldvar) - 1); strncpy(ldvar, var, len); ldvar[len] = 0; var = &var[len + 1]; } } /* * If the user prepended "uboot." (which is how they usually see these * names) strip it off as a convenience. */ if (strncmp(var, "uboot.", 6) == 0) { var = &var[6]; } /* If there is no variable name left, punt. */ if (var[0] == 0) { printf("empty variable name\n"); return; } val = ub_env_get(var); if (action == UBENV_SHOW) { if (val == NULL) printf("uboot.%s is not set\n", var); else printf("uboot.%s=%s\n", var, val); } else if (action == UBENV_IMPORT) { if (val != NULL) { setenv(ldvar, val, 1); } } } static int command_ubenv(int argc, char *argv[]) { enum ubenv_action action; const char *var; int i; action = UBENV_UNKNOWN; if (argc > 1) { if (strcasecmp(argv[1], "import") == 0) action = UBENV_IMPORT; else if (strcasecmp(argv[1], "show") == 0) action = UBENV_SHOW; } if (action == UBENV_UNKNOWN) { command_errmsg = "usage: 'ubenv [var ...]"; return (CMD_ERROR); } if (argc > 2) { for (i = 2; i < argc; i++) handle_uboot_env_var(action, argv[i]); } else { var = NULL; for (;;) { if ((var = ub_env_enum(var)) == NULL) break; handle_uboot_env_var(action, var); } } return (CMD_OK); } COMMAND_SET(ubenv, "ubenv", "show or import U-Boot env vars", command_ubenv); #ifdef LOADER_FDT_SUPPORT /* * Since proper fdt command handling function is defined in fdt_loader_cmd.c, * and declaring it as extern is in contradiction with COMMAND_SET() macro * (which uses static pointer), we're defining wrapper function, which * calls the proper fdt handling routine. */ static int command_fdt(int argc, char *argv[]) { return (command_fdt_internal(argc, argv)); } COMMAND_SET(fdt, "fdt", "flattened device tree handling", command_fdt); #endif diff --git a/stand/uboot/net.c b/stand/uboot/net.c index e703afcadca4..969724ea02df 100644 --- a/stand/uboot/net.c +++ b/stand/uboot/net.c @@ -1,362 +1,361 @@ /*- * Copyright (c) 2000-2001 Benno Rice * Copyright (c) 2007 Semihalf, Rafal Jaworowski * 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 #include #include #include #include #include #include #include #include #include #include #include "api_public.h" #include "glue.h" #include "libuboot.h" #include "dev_net.h" static int net_probe(struct netif *, void *); static int net_match(struct netif *, void *); static void net_init(struct iodesc *, void *); static ssize_t net_get(struct iodesc *, void **, time_t); static ssize_t net_put(struct iodesc *, void *, size_t); static void net_end(struct netif *); extern struct netif_stats net_stats[]; struct netif_dif net_ifs[] = { /* dif_unit dif_nsel dif_stats dif_private */ { 0, 1, &net_stats[0], 0, }, }; struct netif_stats net_stats[nitems(net_ifs)]; struct netif_driver uboot_net = { "uboot_eth", /* netif_bname */ net_match, /* netif_match */ net_probe, /* netif_probe */ net_init, /* netif_init */ net_get, /* netif_get */ net_put, /* netif_put */ net_end, /* netif_end */ net_ifs, /* netif_ifs */ nitems(net_ifs) /* netif_nifs */ }; struct uboot_softc { uint32_t sc_pad; uint8_t sc_rxbuf[ETHER_MAX_LEN]; uint8_t sc_txbuf[ETHER_MAX_LEN + PKTALIGN]; uint8_t *sc_txbufp; int sc_handle; /* device handle for ub_dev_xxx */ }; static struct uboot_softc uboot_softc; /* * get_env_net_params() * * Attempt to obtain all the parms we need for netbooting from the U-Boot * environment. If we fail to obtain the values it may still be possible to * netboot; the net_dev code will attempt to get the values from bootp, rarp, * and other such sources. * * If rootip.s_addr is non-zero net_dev assumes the required global variables * are set and skips the bootp inquiry. For that reason, we don't set rootip * until we've verified that we have at least the minimum required info. * * This is called from netif_init() which can result in it getting called * multiple times, by design. The network code at higher layers zeroes out * rootip when it closes a network interface, so if it gets opened again we have * to obtain all this info again. */ static void get_env_net_params() { char *envstr; in_addr_t rootaddr, serveraddr; /* * Silently get out right away if we don't have rootpath, because none * of the other info we obtain below is sufficient to boot without it. * * If we do have rootpath, copy it into the global var and also set * dhcp.root-path in the env. If we don't get all the other info from * the u-boot env below, we will still try dhcp/bootp, but the server- * provided path will not replace the user-provided value we set here. */ if ((envstr = ub_env_get("rootpath")) == NULL) return; strlcpy(rootpath, envstr, sizeof(rootpath)); setenv("dhcp.root-path", rootpath, 0); /* * Our own IP address must be valid. Silently get out if it's not set, * but whine if it's there and we can't parse it. */ if ((envstr = ub_env_get("ipaddr")) == NULL) return; if ((myip.s_addr = inet_addr(envstr)) == INADDR_NONE) { printf("Could not parse ipaddr '%s'\n", envstr); return; } /* * Netmask is optional, default to the "natural" netmask for our IP, but * whine if it was provided and we couldn't parse it. */ if ((envstr = ub_env_get("netmask")) != NULL && (netmask = inet_addr(envstr)) == INADDR_NONE) { printf("Could not parse netmask '%s'\n", envstr); } if (netmask == INADDR_NONE) { if (IN_CLASSA(myip.s_addr)) netmask = IN_CLASSA_NET; else if (IN_CLASSB(myip.s_addr)) netmask = IN_CLASSB_NET; else netmask = IN_CLASSC_NET; } /* * Get optional serverip before rootpath; the latter can override it. * Whine only if it's present but can't be parsed. */ serveraddr = INADDR_NONE; if ((envstr = ub_env_get("serverip")) != NULL) { if ((serveraddr = inet_addr(envstr)) == INADDR_NONE) printf("Could not parse serverip '%s'\n", envstr); } /* * There must be a rootpath. It may be ip:/path or it may be just the * path in which case the ip needs to be in serverip. */ rootaddr = net_parse_rootpath(); if (rootaddr == INADDR_NONE) rootaddr = serveraddr; if (rootaddr == INADDR_NONE) { printf("No server address for rootpath '%s'\n", envstr); return; } rootip.s_addr = rootaddr; /* * Gateway IP is optional unless rootip is on a different net in which * case whine if it's missing or we can't parse it, and set rootip addr * to zero, which signals to other network code that network params * aren't set (so it will try dhcp, bootp, etc). */ envstr = ub_env_get("gatewayip"); if (!SAMENET(myip, rootip, netmask)) { if (envstr == NULL) { printf("Need gatewayip for a root server on a " "different network.\n"); rootip.s_addr = 0; return; } if ((gateip.s_addr = inet_addr(envstr)) == INADDR_NONE) { printf("Could not parse gatewayip '%s'\n", envstr); rootip.s_addr = 0; return; } } } static int net_match(struct netif *nif, void *machdep_hint) { char **a = (char **)machdep_hint; if (memcmp("net", *a, 3) == 0) return (1); printf("net_match: could not match network device\n"); return (0); } static int net_probe(struct netif *nif, void *machdep_hint) { struct device_info *di; int i; for (i = 0; i < devs_no; i++) if ((di = ub_dev_get(i)) != NULL) if (di->type == DEV_TYP_NET) break; if (i == devs_no) { printf("net_probe: no network devices found, maybe not" " enumerated yet..?\n"); return (-1); } #if defined(NETIF_DEBUG) printf("net_probe: network device found: %d\n", i); #endif uboot_softc.sc_handle = i; return (0); } static ssize_t net_put(struct iodesc *desc, void *pkt, size_t len) { struct netif *nif = desc->io_netif; struct uboot_softc *sc = nif->nif_devdata; size_t sendlen; ssize_t rv; #if defined(NETIF_DEBUG) struct ether_header *eh; printf("net_put: desc %p, pkt %p, len %d\n", desc, pkt, len); eh = pkt; printf("dst: %s ", ether_sprintf(eh->ether_dhost)); printf("src: %s ", ether_sprintf(eh->ether_shost)); printf("type: 0x%x\n", eh->ether_type & 0xffff); #endif if (len < ETHER_MIN_LEN - ETHER_CRC_LEN) { sendlen = ETHER_MIN_LEN - ETHER_CRC_LEN; bzero(sc->sc_txbufp, sendlen); } else sendlen = len; memcpy(sc->sc_txbufp, pkt, len); rv = ub_dev_send(sc->sc_handle, sc->sc_txbufp, sendlen); #if defined(NETIF_DEBUG) printf("net_put: ub_send returned %d\n", rv); #endif if (rv == 0) rv = len; else rv = -1; return (rv); } static ssize_t net_get(struct iodesc *desc, void **pkt, time_t timeout) { struct netif *nif = desc->io_netif; struct uboot_softc *sc = nif->nif_devdata; time_t t; int err, rlen; size_t len; char *buf; #if defined(NETIF_DEBUG) printf("net_get: pkt %p, timeout %d\n", pkt, timeout); #endif t = getsecs(); len = sizeof(sc->sc_rxbuf); do { err = ub_dev_recv(sc->sc_handle, sc->sc_rxbuf, len, &rlen); if (err != 0) { printf("net_get: ub_dev_recv() failed, error=%d\n", err); rlen = 0; break; } } while ((rlen == -1 || rlen == 0) && (getsecs() - t < timeout)); #if defined(NETIF_DEBUG) printf("net_get: received len %d (%x)\n", rlen, rlen); #endif if (rlen > 0) { buf = malloc(rlen + ETHER_ALIGN); if (buf == NULL) return (-1); memcpy(buf + ETHER_ALIGN, sc->sc_rxbuf, rlen); *pkt = buf; return ((ssize_t)rlen); } return (-1); } static void net_init(struct iodesc *desc, void *machdep_hint) { struct netif *nif = desc->io_netif; struct uboot_softc *sc; struct device_info *di; int err; sc = nif->nif_devdata = &uboot_softc; if ((err = ub_dev_open(sc->sc_handle)) != 0) panic("%s%d: initialisation failed with error %d", nif->nif_driver->netif_bname, nif->nif_unit, err); /* Get MAC address */ di = ub_dev_get(sc->sc_handle); memcpy(desc->myea, di->di_net.hwaddr, 6); if (memcmp (desc->myea, "\0\0\0\0\0\0", 6) == 0) { panic("%s%d: empty ethernet address!", nif->nif_driver->netif_bname, nif->nif_unit); } /* Attempt to get netboot params from the u-boot env. */ get_env_net_params(); if (myip.s_addr != 0) desc->myip = myip; #if defined(NETIF_DEBUG) printf("network: %s%d attached to %s\n", nif->nif_driver->netif_bname, nif->nif_unit, ether_sprintf(desc->myea)); #endif /* Set correct alignment for TX packets */ sc->sc_txbufp = sc->sc_txbuf; if ((unsigned long)sc->sc_txbufp % PKTALIGN) sc->sc_txbufp += PKTALIGN - (unsigned long)sc->sc_txbufp % PKTALIGN; } static void net_end(struct netif *nif) { struct uboot_softc *sc = nif->nif_devdata; int err; if ((err = ub_dev_close(sc->sc_handle)) != 0) panic("%s%d: net_end failed with error %d", nif->nif_driver->netif_bname, nif->nif_unit, err); } diff --git a/stand/uboot/uboot_disk.c b/stand/uboot/uboot_disk.c index 407de94017fd..4f7b6ba06942 100644 --- a/stand/uboot/uboot_disk.c +++ b/stand/uboot/uboot_disk.c @@ -1,320 +1,319 @@ /*- * Copyright (c) 2008 Semihalf, Rafal Jaworowski * Copyright (c) 2009 Semihalf, Piotr Ziecik * 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. * */ /* * Block storage I/O routines for U-Boot */ -#include #include #include #include #include #include "api_public.h" #include "bootstrap.h" #include "disk.h" #include "glue.h" #include "libuboot.h" #define stor_printf(fmt, args...) do { \ printf("%s%d: ", dev->dd.d_dev->dv_name, dev->dd.d_unit); \ printf(fmt, ##args); \ } while (0) #ifdef DEBUG #define debugf(fmt, args...) do { printf("%s(): ", __func__); \ printf(fmt,##args); } while (0) #else #define debugf(fmt, args...) #endif static struct { int opened; /* device is opened */ int handle; /* storage device handle */ int type; /* storage type */ off_t blocks; /* block count */ u_int bsize; /* block size */ } stor_info[UB_MAX_DEV]; #define SI(dev) (stor_info[(dev)->dd.d_unit]) static int stor_info_no = 0; static int stor_opendev(struct disk_devdesc *); static int stor_readdev(struct disk_devdesc *, daddr_t, size_t, char *); /* devsw I/F */ static int stor_init(void); static int stor_strategy(void *, int, daddr_t, size_t, char *, size_t *); static int stor_open(struct open_file *, ...); static int stor_close(struct open_file *); static int stor_ioctl(struct open_file *f, u_long cmd, void *data); static int stor_print(int); static void stor_cleanup(void); struct devsw uboot_storage = { .dv_name = "disk", .dv_type = DEVT_DISK, .dv_init = stor_init, .dv_strategy = stor_strategy, .dv_open = stor_open, .dv_close = stor_close, .dv_ioctl = stor_ioctl, .dv_print = stor_print, .dv_cleanup = stor_cleanup, .dv_fmtdev = disk_fmtdev, .dv_parsedev = disk_parsedev, }; static int stor_init(void) { struct device_info *di; int i; if (devs_no == 0) { printf("No U-Boot devices! Really enumerated?\n"); return (-1); } for (i = 0; i < devs_no; i++) { di = ub_dev_get(i); if ((di != NULL) && (di->type & DEV_TYP_STOR)) { if (stor_info_no >= UB_MAX_DEV) { printf("Too many storage devices: %d\n", stor_info_no); return (-1); } stor_info[stor_info_no].handle = i; stor_info[stor_info_no].opened = 0; stor_info[stor_info_no].type = di->type; stor_info[stor_info_no].blocks = di->di_stor.block_count; stor_info[stor_info_no].bsize = di->di_stor.block_size; stor_info_no++; } } if (!stor_info_no) { debugf("No storage devices\n"); return (-1); } debugf("storage devices found: %d\n", stor_info_no); return (0); } static void stor_cleanup(void) { int i; for (i = 0; i < stor_info_no; i++) if (stor_info[i].opened > 0) ub_dev_close(stor_info[i].handle); } static int stor_strategy(void *devdata, int rw, daddr_t blk, size_t size, char *buf, size_t *rsize) { struct disk_devdesc *dev = (struct disk_devdesc *)devdata; daddr_t bcount; int err; rw &= F_MASK; if (rw != F_READ) { stor_printf("write attempt, operation not supported!\n"); return (EROFS); } if (size % SI(dev).bsize) { stor_printf("size=%zu not multiple of device " "block size=%d\n", size, SI(dev).bsize); return (EIO); } bcount = size / SI(dev).bsize; if (rsize) *rsize = 0; err = stor_readdev(dev, blk + dev->d_offset, bcount, buf); if (!err && rsize) *rsize = size; return (err); } static int stor_open(struct open_file *f, ...) { va_list ap; struct disk_devdesc *dev; va_start(ap, f); dev = va_arg(ap, struct disk_devdesc *); va_end(ap); return (stor_opendev(dev)); } static int stor_opendev(struct disk_devdesc *dev) { int err; if (dev->dd.d_unit < 0 || dev->dd.d_unit >= stor_info_no) return (EIO); if (SI(dev).opened == 0) { err = ub_dev_open(SI(dev).handle); if (err != 0) { stor_printf("device open failed with error=%d, " "handle=%d\n", err, SI(dev).handle); return (ENXIO); } SI(dev).opened++; } return (disk_open(dev, SI(dev).blocks * SI(dev).bsize, SI(dev).bsize)); } static int stor_close(struct open_file *f) { struct disk_devdesc *dev; dev = (struct disk_devdesc *)(f->f_devdata); return (disk_close(dev)); } static int stor_readdev(struct disk_devdesc *dev, daddr_t blk, size_t size, char *buf) { lbasize_t real_size; int err; debugf("reading blk=%d size=%d @ 0x%08x\n", (int)blk, size, (uint32_t)buf); err = ub_dev_read(SI(dev).handle, buf, size, blk, &real_size); if (err != 0) { stor_printf("read failed, error=%d\n", err); return (EIO); } if (real_size != size) { stor_printf("real size != size\n"); err = EIO; } return (err); } static int stor_print(int verbose) { struct disk_devdesc dev; static char line[80]; int i, ret = 0; if (stor_info_no == 0) return (ret); printf("%s devices:", uboot_storage.dv_name); if ((ret = pager_output("\n")) != 0) return (ret); for (i = 0; i < stor_info_no; i++) { dev.dd.d_dev = &uboot_storage; dev.dd.d_unit = i; dev.d_slice = D_SLICENONE; dev.d_partition = D_PARTNONE; snprintf(line, sizeof(line), "\tdisk%d (%s)\n", i, ub_stor_type(SI(&dev).type)); if ((ret = pager_output(line)) != 0) break; if (stor_opendev(&dev) == 0) { sprintf(line, "\tdisk%d", i); ret = disk_print(&dev, line, verbose); disk_close(&dev); if (ret != 0) break; } } return (ret); } static int stor_ioctl(struct open_file *f, u_long cmd, void *data) { struct disk_devdesc *dev; int rc; dev = (struct disk_devdesc *)f->f_devdata; rc = disk_ioctl(dev, cmd, data); if (rc != ENOTTY) return (rc); switch (cmd) { case DIOCGSECTORSIZE: *(u_int *)data = SI(dev).bsize; break; case DIOCGMEDIASIZE: *(uint64_t *)data = SI(dev).bsize * SI(dev).blocks; break; default: return (ENOTTY); } return (0); } /* * Return the device unit number for the given type and type-relative unit * number. */ int uboot_diskgetunit(int type, int type_unit) { int local_type_unit; int i; local_type_unit = 0; for (i = 0; i < stor_info_no; i++) { if ((stor_info[i].type & type) == type) { if (local_type_unit == type_unit) { return (i); } local_type_unit++; } } return (-1); } diff --git a/stand/uboot/uboot_fdt.c b/stand/uboot/uboot_fdt.c index 4d592db1aeda..d6f99f8b29cb 100644 --- a/stand/uboot/uboot_fdt.c +++ b/stand/uboot/uboot_fdt.c @@ -1,210 +1,209 @@ /*- * Copyright (c) 2009-2010 The FreeBSD Foundation * * This software was developed by Semihalf under sponsorship from * the FreeBSD Foundation. * * 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 #include #include #include "glue.h" #define STR(number) #number #define STRINGIFY(number) STR(number) static int fdt_platform_load_from_ubenv(const char *var) { struct fdt_header *hdr; const char *s; char *p; s = ub_env_get(var); if (s == NULL || *s == '\0') return (1); hdr = (struct fdt_header *)strtoul(s, &p, 16); if (*p != '\0') return (1); if (fdt_load_dtb_addr(hdr) == 0) { printf("Using DTB provided by U-Boot at " "address %p.\n", hdr); return (0); } return (1); } #define FDT_DTB_PADSZ 1024 int fdt_platform_load_dtb(void) { struct fdt_header *hdr; const char *s; char *p; int rv; /* * If the U-boot environment contains a variable giving the address of a * valid blob in memory, use it. The U-boot README says the right * variable for fdt data loaded into ram is fdt_addr_r, so try that * first. Board vendors also use both fdtaddr and fdt_addr names. */ if ((rv = fdt_platform_load_from_ubenv("fdt_addr_r")) == 0) goto exit; if ((rv = fdt_platform_load_from_ubenv("fdt_addr")) == 0) goto exit; if ((rv = fdt_platform_load_from_ubenv("fdtaddr")) == 0) goto exit; rv = 1; /* * Try to get FDT filename first from loader env and then from u-boot env */ s = getenv("fdt_file"); if (s == NULL) s = ub_env_get("fdtfile"); if (s == NULL) s = ub_env_get("fdt_file"); if (s != NULL && *s != '\0') { if (fdt_load_dtb_file(s) == 0) { printf("Loaded DTB from file '%s'.\n", s); rv = 0; goto exit; } } exit: return (rv); } void fdt_platform_load_overlays(void) { fdt_load_dtb_overlays(ub_env_get("fdt_overlays")); } void fdt_platform_fixups(void) { static struct fdt_mem_region regions[UB_MAX_MR]; const char *env, *str; char *end, *ethstr; int eth_no, i, len, n; struct sys_info *si; env = NULL; eth_no = 0; ethstr = NULL; /* Apply overlays before anything else */ if (fdt_apply_overlays() > 0) fdt_pad_dtb(FDT_DTB_PADSZ); /* Acquire sys_info */ si = ub_get_sys_info(); while ((env = ub_env_enum(env)) != NULL) { if (strncmp(env, "eth", 3) == 0 && strncmp(env + (strlen(env) - 4), "addr", 4) == 0) { /* * Handle Ethernet addrs: parse uboot env eth%daddr */ if (!eth_no) { /* * Check how many chars we will need to store * maximal eth iface number. */ len = strlen(STRINGIFY(TMP_MAX_ETH)) + strlen("ethernet") + 1; /* * Reserve mem for string "ethernet" and len * chars for iface no. */ ethstr = (char *)malloc(len * sizeof(char)); bzero(ethstr, len * sizeof(char)); strcpy(ethstr, "ethernet0"); } /* Extract interface number */ i = strtol(env + 3, &end, 10); if (end == (env + 3)) /* 'ethaddr' means interface 0 address */ n = 0; else n = i; if (n > TMP_MAX_ETH) continue; str = ub_env_get(env); if (n != 0) { /* * Find the length of the interface id by * taking in to account the first 3 and * last 4 characters. */ i = strlen(env) - 7; strncpy(ethstr + 8, env + 3, i); } /* Modify blob */ fdt_fixup_ethernet(str, ethstr, len); /* Clear ethernet..XXXX.. string */ bzero(ethstr + 8, len - 8); if (n + 1 > eth_no) eth_no = n + 1; } else if (strcmp(env, "consoledev") == 0) { str = ub_env_get(env); fdt_fixup_stdout(str); } } /* Modify cpu(s) and bus clock frequenties in /cpus node [Hz] */ fdt_fixup_cpubusfreqs(si->clk_cpu, si->clk_bus); /* Extract the DRAM regions into fdt_mem_region format. */ for (i = 0, n = 0; i < si->mr_no && n < nitems(regions); i++) { if (si->mr[i].flags == MR_ATTR_DRAM) { regions[n].start = si->mr[i].start; regions[n].size = si->mr[i].size; n++; } } /* Fixup memory regions */ fdt_fixup_memory(regions, n); } diff --git a/stand/userboot/userboot/elf32_freebsd.c b/stand/userboot/userboot/elf32_freebsd.c index edbee9ec090e..c2492dce7397 100644 --- a/stand/userboot/userboot/elf32_freebsd.c +++ b/stand/userboot/userboot/elf32_freebsd.c @@ -1,112 +1,111 @@ /*- * 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 #include #include #include #include #define _MACHINE_ELF_WANT_32BIT #include #include #include #include "bootstrap.h" #include "libuserboot.h" static int elf32_exec(struct preloaded_file *amp); static int elf32_obj_exec(struct preloaded_file *amp); struct file_format i386_elf = { elf32_loadfile, elf32_exec }; struct file_format i386_elf_obj = { elf32_obj_loadfile, elf32_obj_exec }; #define GUEST_STACK 0x1000 /* Initial stack base */ #define GUEST_GDT 0x3000 /* Address of initial GDT */ /* * There is an ELF kernel and one or more ELF modules loaded. * We wish to start executing the kernel image, so make such * preparations as are required, and do so. */ static int elf32_exec(struct preloaded_file *fp) { struct file_metadata *md; Elf_Ehdr *ehdr; vm_offset_t entry, bootinfop, modulep, kernend; int boothowto, err, bootdev; uint32_t stack[1024], *sp; if ((md = file_findmetadata(fp, MODINFOMD_ELFHDR)) == NULL) return(EFTYPE); ehdr = (Elf_Ehdr *)&(md->md_data); err = bi_load32(fp->f_args, &boothowto, &bootdev, &bootinfop, &modulep, &kernend); if (err != 0) return(err); entry = ehdr->e_entry & 0xffffff; #ifdef DEBUG printf("Start @ 0x%lx ...\n", entry); #endif dev_cleanup(); /* * Build a scratch stack at physical 0x1000 */ memset(stack, 0, sizeof(stack)); sp = (uint32_t *)((char *)stack + sizeof(stack)); *--sp = kernend; *--sp = modulep; *--sp = bootinfop; *--sp = 0; *--sp = 0; *--sp = 0; *--sp = bootdev; *--sp = boothowto; /* * Fake return address to mimic "new" boot blocks. For more * details see recover_bootinfo in locore.S. */ *--sp = 0xbeefface; CALLBACK(copyin, stack, GUEST_STACK, sizeof(stack)); CALLBACK(setreg, 4, (char *)sp - (char *)stack + GUEST_STACK); CALLBACK(setgdt, GUEST_GDT, 8 * 4 - 1); CALLBACK(exec, entry); panic("exec returned"); } static int elf32_obj_exec(struct preloaded_file *fp) { return (EFTYPE); }