Index: stable/11/stand/common/bcache.c =================================================================== --- stable/11/stand/common/bcache.c (revision 346482) +++ stable/11/stand/common/bcache.c (revision 346483) @@ -1,488 +1,488 @@ /*- * Copyright (c) 1998 Michael Smith * Copyright 2015 Toomas Soome * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ #include #include __FBSDID("$FreeBSD$"); /* * Simple hashed block cache */ #include #include #include #include #include "bootstrap.h" /* #define BCACHE_DEBUG */ #ifdef BCACHE_DEBUG -# define DEBUG(fmt, args...) printf("%s: " fmt "\n" , __func__ , ## args) +# define DPRINTF(fmt, args...) printf("%s: " fmt "\n" , __func__ , ## args) #else -# define DEBUG(fmt, args...) +# define DPRINTF(fmt, args...) #endif struct bcachectl { daddr_t bc_blkno; int bc_count; }; /* * bcache per device node. cache is allocated on device first open and freed * on last close, to save memory. The issue there is the size; biosdisk * supports up to 31 (0x1f) devices. Classic setup would use single disk * to boot from, but this has changed with zfs. */ struct bcache { struct bcachectl *bcache_ctl; caddr_t bcache_data; size_t bcache_nblks; size_t ra; }; static u_int bcache_total_nblks; /* set by bcache_init */ static u_int bcache_blksize; /* set by bcache_init */ static u_int bcache_numdev; /* set by bcache_add_dev */ /* statistics */ static u_int bcache_units; /* number of devices with cache */ static u_int bcache_unit_nblks; /* nblocks per unit */ static u_int bcache_hits; static u_int bcache_misses; static u_int bcache_ops; static u_int bcache_bypasses; static u_int bcache_bcount; static u_int bcache_rablks; #define BHASH(bc, blkno) ((blkno) & ((bc)->bcache_nblks - 1)) #define BCACHE_LOOKUP(bc, blkno) \ ((bc)->bcache_ctl[BHASH((bc), (blkno))].bc_blkno != (blkno)) #define BCACHE_READAHEAD 256 #define BCACHE_MINREADAHEAD 32 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 */ 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; if (nblk - i > BCACHE_MINREADAHEAD && bc->ra > BCACHE_MINREADAHEAD) bc->ra >>= 1; /* reduce read ahead */ break; } else { bcache_hits++; } } if (complete) { /* whole set was in cache, return it */ if (bc->ra < BCACHE_READAHEAD) bc->ra <<= 1; /* increase read ahead */ bcopy(bc->bcache_data + (bcache_blksize * BHASH(bc, blk)), buf, size); goto done; } /* * Fill in any misses. From check we have i pointing to first missing * block, read in all remaining blocks + readahead. * We have space at least for nblk - i before bcache wraps. */ p_blk = blk + i; p_buf = bc->bcache_data + (bcache_blksize * BHASH(bc, p_blk)); r_size = bc->bcache_nblks - BHASH(bc, p_blk); /* remaining blocks */ p_size = MIN(r_size, nblk - i); /* read at least those blocks */ /* * The read ahead size setup. * While the read ahead can save us IO, it also can complicate things: * 1. We do not want to read ahead by wrapping around the * bcache end - this would complicate the cache management. * 2. We are using bc->ra as dynamic hint for read ahead size, * detected cache hits will increase the read-ahead block count, and * misses will decrease, see the code above. * 3. The bcache is sized by 512B blocks, however, the underlying device * may have a larger sector size, and we should perform the IO by * taking into account these larger sector sizes. We could solve this by * passing the sector size to bcache_allocate(), or by using ioctl(), but * in this version we are using the constant, 16 blocks, and are rounding * read ahead block count down to multiple of 16. * Using the constant has two reasons, we are not entirely sure if the * BIOS disk interface is providing the correct value for sector size. * And secondly, this way we get the most conservative setup for the ra. * * The selection of multiple of 16 blocks (8KB) is quite arbitrary, however, * we want to cover CDs (2K) and 4K disks. * bcache_allocate() will always fall back to a minimum of 32 blocks. * Our choice of 16 read ahead blocks will always fit inside the bcache. */ if ((rw & F_NORA) == F_NORA) ra = 0; else ra = bc->bcache_nblks - BHASH(bc, p_blk + p_size); if (ra != 0 && ra != bc->bcache_nblks) { /* do we have RA space? */ ra = MIN(bc->ra, ra - 1); ra = rounddown(ra, 16); /* multiple of 16 blocks */ p_size += ra; } /* invalidate bcache */ for (i = 0; i < p_size; i++) { bcache_invalidate(bc, p_blk + i); } r_size = 0; /* * with read-ahead, it may happen we are attempting to read past * disk end, as bcache has no information about disk size. * in such case we should get partial read if some blocks can be * read or error, if no blocks can be read. * in either case we should return the data in bcache and only * return error if there is no data. */ rw &= F_MASK; result = dd->dv_strategy(dd->dv_devdata, rw, p_blk, p_size * bcache_blksize, p_buf, &r_size); r_size /= bcache_blksize; for (i = 0; i < r_size; i++) bcache_insert(bc, p_blk + i); /* update ra statistics */ if (r_size != 0) { if (r_size < p_size) bcache_rablks += (p_size - r_size); else bcache_rablks += ra; } /* check how much data can we copy */ for (i = 0; i < nblk; i++) { if (BCACHE_LOOKUP(bc, (daddr_t)(blk + i))) break; } if (size > i * bcache_blksize) size = i * bcache_blksize; if (size != 0) { bcopy(bc->bcache_data + (bcache_blksize * BHASH(bc, blk)), buf, size); result = 0; } done: if ((result == 0) && (rsize != NULL)) *rsize = size; return(result); } /* * Requests larger than 1/2 cache size will be bypassed and go * directly to the disk. XXX tune this. */ int bcache_strategy(void *devdata, int rw, daddr_t blk, size_t size, char *buf, size_t *rsize) { struct bcache_devdata *dd = (struct bcache_devdata *)devdata; struct bcache *bc = dd->dv_cache; u_int bcache_nblks = 0; int nblk, cblk, ret; size_t csize, isize, total; bcache_ops++; if (bc != NULL) bcache_nblks = bc->bcache_nblks; /* bypass large requests, or when the cache is inactive */ if (bc == NULL || ((size * 2 / bcache_blksize) > bcache_nblks)) { - DEBUG("bypass %zu from %qu", size / bcache_blksize, blk); + DPRINTF("bypass %zu from %qu", size / bcache_blksize, blk); bcache_bypasses++; rw &= F_MASK; return (dd->dv_strategy(dd->dv_devdata, rw, blk, size, buf, rsize)); } switch (rw & F_MASK) { case F_READ: nblk = size / bcache_blksize; if (size != 0 && nblk == 0) nblk++; /* read at least one block */ ret = 0; total = 0; while(size) { cblk = bcache_nblks - BHASH(bc, blk); /* # of blocks left */ cblk = MIN(cblk, nblk); if (size <= bcache_blksize) csize = size; else csize = cblk * bcache_blksize; ret = read_strategy(devdata, rw, blk, csize, buf+total, &isize); /* * we may have error from read ahead, if we have read some data * return partial read. */ if (ret != 0 || isize == 0) { if (total != 0) ret = 0; break; } blk += isize / bcache_blksize; total += isize; size -= isize; nblk = size / bcache_blksize; } if (rsize) *rsize = total; return (ret); case F_WRITE: return write_strategy(devdata, F_WRITE, blk, size, buf, rsize); } return -1; } /* * Free allocated bcache instance */ static void bcache_free_instance(struct bcache *bc) { if (bc != NULL) { if (bc->bcache_ctl) free(bc->bcache_ctl); if (bc->bcache_data) free(bc->bcache_data); free(bc); } } /* * Insert a block into the cache. */ static void bcache_insert(struct bcache *bc, daddr_t blkno) { u_int cand; cand = BHASH(bc, blkno); - DEBUG("insert blk %llu -> %u # %d", blkno, cand, bcache_bcount); + DPRINTF("insert blk %llu -> %u # %d", blkno, cand, bcache_bcount); bc->bcache_ctl[cand].bc_blkno = blkno; bc->bcache_ctl[cand].bc_count = bcache_bcount++; } /* * Invalidate a block from the cache. */ static void bcache_invalidate(struct bcache *bc, daddr_t blkno) { u_int i; i = BHASH(bc, blkno); if (bc->bcache_ctl[i].bc_blkno == blkno) { bc->bcache_ctl[i].bc_count = -1; bc->bcache_ctl[i].bc_blkno = -1; - DEBUG("invalidate blk %llu", blkno); + DPRINTF("invalidate blk %llu", blkno); } } #ifndef BOOT2 COMMAND_SET(bcachestat, "bcachestat", "get disk block cache stats", command_bcache); static int command_bcache(int argc, char *argv[]) { if (argc != 1) { command_errmsg = "wrong number of arguments"; return(CMD_ERROR); } printf("\ncache blocks: %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 Index: stable/11/stand/common/disk.c =================================================================== --- stable/11/stand/common/disk.c (revision 346482) +++ stable/11/stand/common/disk.c (revision 346483) @@ -1,440 +1,443 @@ /*- * Copyright (c) 1998 Michael Smith * Copyright (c) 2012 Andrey V. Elsukov * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ #include __FBSDID("$FreeBSD$"); #include #include #include #include #include #include #include "disk.h" #ifdef DISK_DEBUG -# define DEBUG(fmt, args...) printf("%s: " fmt "\n" , __func__ , ## args) +# define DPRINTF(fmt, args...) printf("%s: " fmt "\n" , __func__ , ## args) #else -# define DEBUG(fmt, args...) +# define DPRINTF(fmt, args...) #endif struct open_disk { struct ptable *table; uint64_t mediasize; uint64_t entrysize; u_int sectorsize; }; struct print_args { struct disk_devdesc *dev; const char *prefix; int verbose; }; /* Convert size to a human-readable number. */ static char * display_size(uint64_t size, u_int sectorsize) { static char buf[80]; char unit; size = size * sectorsize / 1024; unit = 'K'; if (size >= 10485760000LL) { size /= 1073741824; unit = 'T'; } else if (size >= 10240000) { size /= 1048576; unit = 'G'; } else if (size >= 10000) { size /= 1024; unit = 'M'; } - sprintf(buf, "%ld%cB", (long)size, unit); + sprintf(buf, "%4ld%cB", (long)size, unit); return (buf); } int ptblread(void *d, void *buf, size_t blocks, uint64_t offset) { struct disk_devdesc *dev; struct open_disk *od; dev = (struct disk_devdesc *)d; od = (struct open_disk *)dev->dd.d_opendata; /* * The strategy function assumes the offset is in units of 512 byte * sectors. For larger sector sizes, we need to adjust the offset to * match the actual sector size. */ offset *= (od->sectorsize / 512); /* * As the GPT backup partition is located at the end of the disk, * to avoid reading past disk end, flag bcache not to use RA. */ return (dev->dd.d_dev->dv_strategy(dev, F_READ | F_NORA, offset, blocks * od->sectorsize, (char *)buf, NULL)); } -#define PWIDTH 35 static int ptable_print(void *arg, const char *pname, const struct ptable_entry *part) { struct disk_devdesc dev; struct print_args *pa, bsd; struct open_disk *od; struct ptable *table; char line[80]; int res; + u_int sectsize; + uint64_t partsize; pa = (struct print_args *)arg; od = (struct open_disk *)pa->dev->dd.d_opendata; - sprintf(line, " %s%s: %s", pa->prefix, pname, - parttype2str(part->type)); - if (pa->verbose) - sprintf(line, "%-*s%s", PWIDTH, line, - display_size(part->end - part->start + 1, - od->sectorsize)); - strcat(line, "\n"); + sectsize = od->sectorsize; + partsize = part->end - part->start + 1; + sprintf(line, " %s%s: %s\t%s\n", pa->prefix, pname, + parttype2str(part->type), + pa->verbose ? display_size(partsize, sectsize) : ""); if (pager_output(line)) return 1; res = 0; if (part->type == PART_FREEBSD) { /* Open slice with BSD label */ dev.dd.d_dev = pa->dev->dd.d_dev; dev.dd.d_unit = pa->dev->dd.d_unit; dev.d_slice = part->index; dev.d_partition = -1; - if (disk_open(&dev, part->end - part->start + 1, - od->sectorsize) == 0) { - table = ptable_open(&dev, part->end - part->start + 1, - od->sectorsize, ptblread); + if (disk_open(&dev, partsize, sectsize) == 0) { + /* + * disk_open() for partition -1 on a bsd slice assumes + * you want the first bsd partition. Reset things so + * that we're looking at the start of the raw slice. + */ + dev.d_partition = -1; + dev.d_offset = part->start; + table = ptable_open(&dev, partsize, sectsize, ptblread); if (table != NULL) { sprintf(line, " %s%s", pa->prefix, pname); bsd.dev = pa->dev; bsd.prefix = line; bsd.verbose = pa->verbose; res = ptable_iterate(table, &bsd, ptable_print); ptable_close(table); } disk_close(&dev); } } return (res); } -#undef PWIDTH int disk_print(struct disk_devdesc *dev, char *prefix, int verbose) { struct open_disk *od; struct print_args pa; /* Disk should be opened */ od = (struct open_disk *)dev->dd.d_opendata; pa.dev = dev; pa.prefix = prefix; pa.verbose = verbose; return (ptable_iterate(od->table, &pa, ptable_print)); } int disk_read(struct disk_devdesc *dev, void *buf, uint64_t offset, u_int blocks) { struct open_disk *od; int ret; od = (struct open_disk *)dev->dd.d_opendata; ret = dev->dd.d_dev->dv_strategy(dev, F_READ, dev->d_offset + offset, blocks * od->sectorsize, buf, NULL); return (ret); } int disk_write(struct disk_devdesc *dev, void *buf, uint64_t offset, u_int blocks) { struct open_disk *od; int ret; od = (struct open_disk *)dev->dd.d_opendata; ret = dev->dd.d_dev->dv_strategy(dev, F_WRITE, dev->d_offset + offset, blocks * od->sectorsize, buf, NULL); return (ret); } int disk_ioctl(struct disk_devdesc *dev, u_long cmd, void *data) { struct open_disk *od = dev->dd.d_opendata; if (od == NULL) return (ENOTTY); switch (cmd) { case DIOCGSECTORSIZE: *(u_int *)data = od->sectorsize; break; case DIOCGMEDIASIZE: if (dev->d_offset == 0) *(uint64_t *)data = od->mediasize; else *(uint64_t *)data = od->entrysize * od->sectorsize; break; default: return (ENOTTY); } return (0); } int disk_open(struct disk_devdesc *dev, uint64_t mediasize, u_int sectorsize) { struct disk_devdesc partdev; struct open_disk *od; struct ptable *table; struct ptable_entry part; int rc, slice, partition; rc = 0; od = (struct open_disk *)malloc(sizeof(struct open_disk)); if (od == NULL) { - DEBUG("no memory"); + DPRINTF("no memory"); return (ENOMEM); } dev->dd.d_opendata = od; od->entrysize = 0; od->mediasize = mediasize; od->sectorsize = sectorsize; /* * While we are reading disk metadata, make sure we do it relative * to the start of the disk */ memcpy(&partdev, dev, sizeof(partdev)); partdev.d_offset = 0; partdev.d_slice = -1; partdev.d_partition = -1; dev->d_offset = 0; table = NULL; slice = dev->d_slice; partition = dev->d_partition; - DEBUG("%s unit %d, slice %d, partition %d => %p", + DPRINTF("%s unit %d, slice %d, partition %d => %p", disk_fmtdev(dev), dev->dd.d_unit, dev->d_slice, dev->d_partition, od); /* Determine disk layout. */ od->table = ptable_open(&partdev, mediasize / sectorsize, sectorsize, ptblread); if (od->table == NULL) { - DEBUG("Can't read partition table"); + DPRINTF("Can't read partition table"); rc = ENXIO; goto out; } if (ptable_getsize(od->table, &mediasize) != 0) { rc = ENXIO; goto out; } od->mediasize = mediasize; if (ptable_gettype(od->table) == PTABLE_BSD && partition >= 0) { /* It doesn't matter what value has d_slice */ rc = ptable_getpart(od->table, &part, partition); if (rc == 0) { dev->d_offset = part.start; od->entrysize = part.end - part.start + 1; } } else if (ptable_gettype(od->table) == PTABLE_ISO9660) { dev->d_offset = 0; od->entrysize = mediasize; } else if (slice >= 0) { /* Try to get information about partition */ if (slice == 0) rc = ptable_getbestpart(od->table, &part); else rc = ptable_getpart(od->table, &part, slice); if (rc != 0) /* Partition doesn't exist */ goto out; dev->d_offset = part.start; od->entrysize = part.end - part.start + 1; slice = part.index; if (ptable_gettype(od->table) == PTABLE_GPT) { partition = 255; goto out; /* Nothing more to do */ } else if (partition == 255) { /* * When we try to open GPT partition, but partition * table isn't GPT, reset d_partition value to -1 * and try to autodetect appropriate value. */ partition = -1; } /* * If d_partition < 0 and we are looking at a BSD slice, * then try to read BSD label, otherwise return the * whole MBR slice. */ if (partition == -1 && part.type != PART_FREEBSD) goto out; /* Try to read BSD label */ table = ptable_open(dev, part.end - part.start + 1, od->sectorsize, ptblread); if (table == NULL) { - DEBUG("Can't read BSD label"); + DPRINTF("Can't read BSD label"); rc = ENXIO; goto out; } /* * If slice contains BSD label and d_partition < 0, then * assume the 'a' partition. Otherwise just return the * whole MBR slice, because it can contain ZFS. */ if (partition < 0) { if (ptable_gettype(table) != PTABLE_BSD) goto out; partition = 0; } rc = ptable_getpart(table, &part, partition); if (rc != 0) goto out; dev->d_offset += part.start; od->entrysize = part.end - part.start + 1; } out: if (table != NULL) ptable_close(table); if (rc != 0) { if (od->table != NULL) ptable_close(od->table); free(od); - DEBUG("%s could not open", disk_fmtdev(dev)); + DPRINTF("%s could not open", disk_fmtdev(dev)); } else { /* Save the slice and partition number to the dev */ dev->d_slice = slice; dev->d_partition = partition; - DEBUG("%s offset %lld => %p", disk_fmtdev(dev), + DPRINTF("%s offset %lld => %p", disk_fmtdev(dev), (long long)dev->d_offset, od); } return (rc); } int disk_close(struct disk_devdesc *dev) { struct open_disk *od; od = (struct open_disk *)dev->dd.d_opendata; - DEBUG("%s closed => %p", disk_fmtdev(dev), od); + DPRINTF("%s closed => %p", disk_fmtdev(dev), od); ptable_close(od->table); free(od); return (0); } char* disk_fmtdev(struct disk_devdesc *dev) { static char buf[128]; char *cp; cp = buf + sprintf(buf, "%s%d", dev->dd.d_dev->dv_name, dev->dd.d_unit); if (dev->d_slice >= 0) { #ifdef LOADER_GPT_SUPPORT if (dev->d_partition == 255) { sprintf(cp, "p%d:", dev->d_slice); return (buf); } else #endif #ifdef LOADER_MBR_SUPPORT cp += sprintf(cp, "s%d", dev->d_slice); #endif } if (dev->d_partition >= 0) cp += sprintf(cp, "%c", dev->d_partition + 'a'); strcat(cp, ":"); return (buf); } int disk_parsedev(struct disk_devdesc *dev, const char *devspec, const char **path) { int unit, slice, partition; const char *np; char *cp; np = devspec; unit = slice = partition = -1; if (*np != '\0' && *np != ':') { unit = strtol(np, &cp, 10); if (cp == np) return (EUNIT); #ifdef LOADER_GPT_SUPPORT if (*cp == 'p') { np = cp + 1; slice = strtol(np, &cp, 10); if (np == cp) return (ESLICE); /* we don't support nested partitions on GPT */ if (*cp != '\0' && *cp != ':') return (EINVAL); partition = 255; } else #endif #ifdef LOADER_MBR_SUPPORT if (*cp == 's') { np = cp + 1; slice = strtol(np, &cp, 10); if (np == cp) return (ESLICE); } #endif if (*cp != '\0' && *cp != ':') { partition = *cp - 'a'; if (partition < 0) return (EPART); cp++; } } else return (EINVAL); if (*cp != '\0' && *cp != ':') return (EINVAL); dev->dd.d_unit = unit; dev->d_slice = slice; dev->d_partition = partition; if (path != NULL) *path = (*cp == '\0') ? cp: cp + 1; return (0); } Index: stable/11/stand/common/interp_forth.c =================================================================== --- stable/11/stand/common/interp_forth.c (revision 346482) +++ stable/11/stand/common/interp_forth.c (revision 346483) @@ -1,445 +1,445 @@ /*- * Copyright (c) 1998 Michael Smith * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ #include __FBSDID("$FreeBSD$"); #include /* to pick up __FreeBSD_version */ #include #include #include "bootstrap.h" #include "ficl.h" extern unsigned bootprog_rev; INTERP_DEFINE("4th"); /* #define BFORTH_DEBUG */ #ifdef BFORTH_DEBUG -#define DEBUG(fmt, args...) printf("%s: " fmt "\n" , __func__ , ## args) +#define DPRINTF(fmt, args...) printf("%s: " fmt "\n" , __func__ , ## args) #else -#define DEBUG(fmt, args...) +#define DPRINTF(fmt, args...) #endif /* * Eventually, all builtin commands throw codes must be defined * elsewhere, possibly bootstrap.h. For now, just this code, used * just in this file, it is getting defined. */ #define BF_PARSE 100 /* * FreeBSD loader default dictionary cells */ #ifndef BF_DICTSIZE #define BF_DICTSIZE 10000 #endif /* * BootForth Interface to Ficl Forth interpreter. */ FICL_SYSTEM *bf_sys; FICL_VM *bf_vm; /* * Shim for taking commands from BF and passing them out to 'standard' * argv/argc command functions. */ static void bf_command(FICL_VM *vm) { char *name, *line, *tail, *cp; size_t len; struct bootblk_command **cmdp; bootblk_cmd_t *cmd; int nstrings, i; int argc, result; char **argv; /* Get the name of the current word */ name = vm->runningWord->name; /* Find our command structure */ cmd = NULL; SET_FOREACH(cmdp, Xcommand_set) { if (((*cmdp)->c_name != NULL) && !strcmp(name, (*cmdp)->c_name)) cmd = (*cmdp)->c_fn; } if (cmd == NULL) panic("callout for unknown command '%s'", name); /* Check whether we have been compiled or are being interpreted */ if (stackPopINT(vm->pStack)) { /* * Get parameters from stack, in the format: * an un ... a2 u2 a1 u1 n -- * Where n is the number of strings, a/u are pairs of * address/size for strings, and they will be concatenated * in LIFO order. */ nstrings = stackPopINT(vm->pStack); for (i = 0, len = 0; i < nstrings; i++) len += stackFetch(vm->pStack, i * 2).i + 1; line = malloc(strlen(name) + len + 1); strcpy(line, name); if (nstrings) for (i = 0; i < nstrings; i++) { len = stackPopINT(vm->pStack); cp = stackPopPtr(vm->pStack); strcat(line, " "); strncat(line, cp, len); } } else { /* Get remainder of invocation */ tail = vmGetInBuf(vm); for (cp = tail, len = 0; cp != vm->tib.end && *cp != 0 && *cp != '\n'; cp++, len++) ; line = malloc(strlen(name) + len + 2); strcpy(line, name); if (len > 0) { strcat(line, " "); strncat(line, tail, len); vmUpdateTib(vm, tail + len); } } - DEBUG("cmd '%s'", line); + 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) { (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)); - DEBUG("ficlExec '%s' = %d", line, result); + 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); } /* * 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); } Index: stable/11/stand/common/load_elf.c =================================================================== --- stable/11/stand/common/load_elf.c (revision 346482) +++ stable/11/stand/common/load_elf.c (revision 346483) @@ -1,1216 +1,1218 @@ /*- * Copyright (c) 1998 Michael Smith * Copyright (c) 1998 Peter Wemm * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ #include __FBSDID("$FreeBSD$"); #include #include #include #include #include #include #include #include #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; } *elf_file_t; static int __elfN(loadimage)(struct preloaded_file *mp, elf_file_t ef, uint64_t loadaddr); static int __elfN(lookup_symbol)(struct preloaded_file *mp, elf_file_t ef, const char* name, Elf_Sym* sym); static int __elfN(reloc_ptr)(struct preloaded_file *mp, elf_file_t ef, Elf_Addr p, void *val, size_t len); static int __elfN(parse_modmetadata)(struct preloaded_file *mp, elf_file_t ef, Elf_Addr p_start, Elf_Addr p_end); static symaddr_fn __elfN(symaddr); static char *fake_modname(const char *name); const char *__elfN(kerneltype) = "elf kernel"; const char *__elfN(moduletype) = "elf module"; uint64_t __elfN(relocation_offset) = 0; 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 static int __elfN(load_elf_header)(char *filename, elf_file_t ef) { ssize_t bytes_read; Elf_Ehdr *ehdr; int err; /* * Open the image, read and validate the ELF header */ if (filename == NULL) /* can't handle nameless */ return (EFTYPE); if ((ef->fd = open(filename, O_RDONLY)) == -1) return (errno); ef->firstpage = malloc(PAGE_SIZE); if (ef->firstpage == NULL) { close(ef->fd); return (ENOMEM); } bytes_read = read(ef->fd, ef->firstpage, PAGE_SIZE); ef->firstlen = (size_t)bytes_read; if (bytes_read < 0 || ef->firstlen <= sizeof(Elf_Ehdr)) { err = EFTYPE; /* could be EIO, but may be small file */ goto error; } ehdr = ef->ehdr = (Elf_Ehdr *)ef->firstpage; /* Is it ELF? */ if (!IS_ELF(*ehdr)) { err = EFTYPE; goto error; } if (ehdr->e_ident[EI_CLASS] != ELF_TARG_CLASS || /* Layout ? */ ehdr->e_ident[EI_DATA] != ELF_TARG_DATA || ehdr->e_ident[EI_VERSION] != EV_CURRENT) /* Version ? */ { 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; } return (0); error: if (ef->firstpage != NULL) { free(ef->firstpage); ef->firstpage = NULL; } if (ef->fd != -1) { close(ef->fd); ef->fd = -1; } return (err); } /* * Attempt to load the file (file) as an ELF module. It will be stored at * (dest), and a pointer to a module structure describing the loaded object * will be saved in (result). */ int __elfN(loadfile)(char *filename, 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"); #ifdef ELF_VERBOSE if (ef.kernel) printf("%s entry at 0x%jx\n", filename, (uintmax_t)ehdr->e_entry); #else printf("%s ", filename); #endif fp->f_size = __elfN(loadimage)(fp, &ef, dest); if (fp->f_size == 0 || fp->f_addr == 0) goto ioerr; /* save exec header as metadata */ file_addmetadata(fp, MODINFOMD_ELFHDR, sizeof(*ehdr), ehdr); /* Load OK, return module pointer */ *result = (struct preloaded_file *)fp; err = 0; goto out; ioerr: err = EIO; oerr: file_discard(fp); out: if (ef.firstpage) free(ef.firstpage); if (ef.fd != -1) close(ef.fd); return (err); } /* * With the file (fd) open on the image, and (ehdr) containing * the Elf header, load the image at (off) */ static int __elfN(loadimage)(struct preloaded_file *fp, elf_file_t ef, 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; if (ehdr->e_type == ET_EXEC) { #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; #ifdef ELF_VERBOSE printf("Converted entry 0x%08x\n", ehdr->e_entry); #endif } else off = 0; #elif defined(__arm__) && !defined(EFI) /* * The elf headers in arm kernels specify virtual addresses in * all header fields, even the ones that should be physical * addresses. We assume the entry point is in the first page, * and masking the page offset will leave us with the virtual * address the kernel was linked at. We subtract that from the * load offset, making 'off' into the value which, when added * to a virtual address in an elf header, translates it to a * physical address. We do the va->pa conversion on the entry * point address in the header now, so that later we can launch * the kernel by just jumping to that address. * * When booting from UEFI the copyin and copyout functions * handle adjusting the location relative to the first virtual * address. Because of this there is no need to adjust the * offset or entry point address as these will both be handled * by the efi code. */ off -= ehdr->e_entry & ~PAGE_MASK; ehdr->e_entry += off; #ifdef ELF_VERBOSE printf("ehdr->e_entry 0x%08x, va<->pa off %llx\n", ehdr->e_entry, off); #endif #else off = 0; /* other archs use direct mapped kernels */ #endif } ef->off = off; if (ef->kernel) __elfN(relocation_offset) = off; if ((ehdr->e_phoff + ehdr->e_phnum * sizeof(*phdr)) > ef->firstlen) { printf("elf" __XSTRING(__ELF_WORD_SIZE) "_loadimage: program header not within first page\n"); goto out; } phdr = (Elf_Phdr *)(ef->firstpage + ehdr->e_phoff); for (i = 0; i < ehdr->e_phnum; i++) { if (elf_program_header_convert(ehdr, phdr)) continue; /* We want to load PT_LOAD segments only.. */ if (phdr[i].p_type != PT_LOAD) continue; #ifdef ELF_VERBOSE printf("Segment: 0x%lx@0x%lx -> 0x%lx-0x%lx", (long)phdr[i].p_filesz, (long)phdr[i].p_offset, (long)(phdr[i].p_vaddr + off), (long)(phdr[i].p_vaddr + off + phdr[i].p_memsz - 1)); #else if ((phdr[i].p_flags & PF_W) == 0) { printf("text=0x%lx ", (long)phdr[i].p_filesz); } else { printf("data=0x%lx", (long)phdr[i].p_filesz); if (phdr[i].p_filesz < phdr[i].p_memsz) printf("+0x%lx", (long)(phdr[i].p_memsz - phdr[i].p_filesz)); printf(" "); } #endif fpcopy = 0; if (ef->firstlen > phdr[i].p_offset) { fpcopy = ef->firstlen - phdr[i].p_offset; archsw.arch_copyin(ef->firstpage + phdr[i].p_offset, phdr[i].p_vaddr + off, fpcopy); } if (phdr[i].p_filesz > fpcopy) { if (kern_pread(ef->fd, phdr[i].p_vaddr + off + fpcopy, phdr[i].p_filesz - fpcopy, phdr[i].p_offset + fpcopy) != 0) { printf("\nelf" __XSTRING(__ELF_WORD_SIZE) "_loadimage: read failed\n"); goto out; } } /* clear space from oversized segments; eg: bss */ if (phdr[i].p_filesz < phdr[i].p_memsz) { #ifdef ELF_VERBOSE printf(" (bss: 0x%lx-0x%lx)", (long)(phdr[i].p_vaddr + off + phdr[i].p_filesz), (long)(phdr[i].p_vaddr + off + phdr[i].p_memsz -1)); #endif kern_bzero(phdr[i].p_vaddr + off + phdr[i].p_filesz, phdr[i].p_memsz - phdr[i].p_filesz); } #ifdef ELF_VERBOSE printf("\n"); #endif if (archsw.arch_loadseg != NULL) archsw.arch_loadseg(ehdr, phdr + i, off); if (firstaddr == 0 || firstaddr > (phdr[i].p_vaddr + off)) firstaddr = phdr[i].p_vaddr + off; if (lastaddr == 0 || lastaddr < (phdr[i].p_vaddr + off + phdr[i].p_memsz)) lastaddr = phdr[i].p_vaddr + off + phdr[i].p_memsz; } lastaddr = roundup(lastaddr, sizeof(long)); /* * Get the section headers. We need this for finding the .ctors * section as well as for loading any symbols. Both may be hard * to do if reading from a .gz file as it involves seeking. I * think the rule is going to have to be that you must strip a * file to remove symbols before gzipping it. */ chunk = (size_t)ehdr->e_shnum * (size_t)ehdr->e_shentsize; if (chunk == 0 || ehdr->e_shoff == 0) goto nosyms; shdr = alloc_pread(ef->fd, ehdr->e_shoff, chunk); if (shdr == NULL) { printf("\nelf" __XSTRING(__ELF_WORD_SIZE) "_loadimage: failed to read section headers"); goto nosyms; } 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(ef->fd, shdr[ehdr->e_shstrndx].sh_offset, chunk); if (shstr) { for (i = 0; i < ehdr->e_shnum; i++) { if (strcmp(shstr + shdr[i].sh_name, ".ctors") != 0) continue; ctors = shdr[i].sh_addr; file_addmetadata(fp, MODINFOMD_CTORS_ADDR, sizeof(ctors), &ctors); size = shdr[i].sh_size; file_addmetadata(fp, MODINFOMD_CTORS_SIZE, sizeof(size), &size); break; } free(shstr); } } /* * Now load any symbols. */ symtabindex = -1; symstrindex = -1; for (i = 0; i < ehdr->e_shnum; i++) { if (shdr[i].sh_type != SHT_SYMTAB) continue; for (j = 0; j < ehdr->e_phnum; j++) { if (phdr[j].p_type != PT_LOAD) continue; if (shdr[i].sh_offset >= phdr[j].p_offset && (shdr[i].sh_offset + shdr[i].sh_size <= phdr[j].p_offset + phdr[j].p_filesz)) { shdr[i].sh_offset = 0; shdr[i].sh_size = 0; break; } } if (shdr[i].sh_offset == 0 || shdr[i].sh_size == 0) continue; /* alread loaded in a PT_LOAD above */ /* Save it for loading below */ symtabindex = i; symstrindex = shdr[i].sh_link; } if (symtabindex < 0 || symstrindex < 0) goto nosyms; /* Ok, committed to a load. */ #ifndef ELF_VERBOSE printf("syms=["); #endif ssym = lastaddr; for (i = symtabindex; i >= 0; i = symstrindex) { #ifdef ELF_VERBOSE char *secname; switch(shdr[i].sh_type) { case SHT_SYMTAB: /* Symbol table */ secname = "symtab"; break; case SHT_STRTAB: /* String table */ secname = "strtab"; break; default: secname = "WHOA!!"; break; } #endif size = shdr[i].sh_size; #if defined(__powerpc__) #if __ELF_WORD_SIZE == 64 size = htobe64(size); #else size = htobe32(size); #endif #endif archsw.arch_copyin(&size, lastaddr, sizeof(size)); lastaddr += sizeof(size); #ifdef ELF_VERBOSE printf("\n%s: 0x%jx@0x%jx -> 0x%jx-0x%jx", secname, (uintmax_t)shdr[i].sh_size, (uintmax_t)shdr[i].sh_offset, (uintmax_t)lastaddr, (uintmax_t)(lastaddr + shdr[i].sh_size)); #else if (i == symstrindex) printf("+"); printf("0x%lx+0x%lx", (long)sizeof(size), (long)size); #endif if (lseek(ef->fd, (off_t)shdr[i].sh_offset, SEEK_SET) == -1) { printf("\nelf" __XSTRING(__ELF_WORD_SIZE) "_loadimage: could not seek for symbols - skipped!"); lastaddr = ssym; ssym = 0; goto nosyms; } result = archsw.arch_readin(ef->fd, lastaddr, shdr[i].sh_size); if (result < 0 || (size_t)result != shdr[i].sh_size) { printf("\nelf" __XSTRING(__ELF_WORD_SIZE) "_loadimage: could not read symbols - skipped! " "(%ju != %ju)", (uintmax_t)result, (uintmax_t)shdr[i].sh_size); lastaddr = ssym; ssym = 0; goto nosyms; } /* Reset offsets relative to ssym */ lastaddr += shdr[i].sh_size; lastaddr = roundup(lastaddr, sizeof(size)); if (i == symtabindex) symtabindex = -1; else if (i == symstrindex) symstrindex = -1; } esym = lastaddr; #ifndef ELF_VERBOSE printf("]"); #endif #if defined(__powerpc__) /* On PowerPC we always need to provide BE data to the kernel */ #if __ELF_WORD_SIZE == 64 ssym = htobe64((uint64_t)ssym); esym = htobe64((uint64_t)esym); #else ssym = htobe32((uint32_t)ssym); esym = htobe32((uint32_t)esym); #endif #endif file_addmetadata(fp, MODINFOMD_SSYM, sizeof(ssym), &ssym); file_addmetadata(fp, MODINFOMD_ESYM, sizeof(esym), &esym); nosyms: printf("\n"); ret = lastaddr - firstaddr; fp->f_addr = firstaddr; php = NULL; for (i = 0; i < ehdr->e_phnum; i++) { if (phdr[i].p_type == PT_DYNAMIC) { php = phdr + i; adp = php->p_vaddr; file_addmetadata(fp, MODINFOMD_DYNAMIC, sizeof(adp), &adp); break; } } if (php == NULL) /* this is bad, we cannot get to symbols or _DYNAMIC */ goto out; ndp = php->p_filesz / sizeof(Elf_Dyn); if (ndp == 0) goto out; dp = malloc(php->p_filesz); if (dp == NULL) goto out; archsw.arch_copyout(php->p_vaddr + off, dp, php->p_filesz); ef->strsz = 0; for (i = 0; i < ndp; i++) { if (dp[i].d_tag == 0) break; switch (dp[i].d_tag) { case DT_HASH: ef->hashtab = (Elf_Hashelt*)(uintptr_t)(dp[i].d_un.d_ptr + off); break; case DT_STRTAB: ef->strtab = (char *)(uintptr_t)(dp[i].d_un.d_ptr + off); break; case DT_STRSZ: ef->strsz = dp[i].d_un.d_val; break; case DT_SYMTAB: ef->symtab = (Elf_Sym *)(uintptr_t)(dp[i].d_un.d_ptr + off); break; case DT_REL: ef->rel = (Elf_Rel *)(uintptr_t)(dp[i].d_un.d_ptr + off); break; case DT_RELSZ: ef->relsz = dp[i].d_un.d_val; break; case DT_RELA: ef->rela = (Elf_Rela *)(uintptr_t)(dp[i].d_un.d_ptr + off); break; case DT_RELASZ: ef->relasz = dp[i].d_un.d_val; break; default: break; } } if (ef->hashtab == NULL || ef->symtab == NULL || ef->strtab == NULL || ef->strsz == 0) goto out; COPYOUT(ef->hashtab, &ef->nbuckets, sizeof(ef->nbuckets)); COPYOUT(ef->hashtab + 1, &ef->nchains, sizeof(ef->nchains)); ef->buckets = ef->hashtab + 2; ef->chains = ef->buckets + ef->nbuckets; if (__elfN(lookup_symbol)(fp, ef, "__start_set_modmetadata_set", &sym) != 0) return 0; p_start = sym.st_value + ef->off; if (__elfN(lookup_symbol)(fp, ef, "__stop_set_modmetadata_set", &sym) != 0) return ENOENT; p_end = sym.st_value + ef->off; if (__elfN(parse_modmetadata)(fp, ef, p_start, p_end) == 0) goto out; if (ef->kernel) /* kernel must not depend on anything */ goto out; out: if (dp) free(dp); if (shdr) free(shdr); return ret; } static char invalid_name[] = "bad"; char * fake_modname(const char *name) { const char *sp, *ep; char *fp; size_t len; sp = strrchr(name, '/'); if (sp) sp++; else sp = name; - ep = strrchr(name, '.'); - if (ep) { - if (ep == name) { - sp = invalid_name; - ep = invalid_name + sizeof(invalid_name) - 1; - } - } else - ep = name + strlen(name); + + 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(ef.fd, ef.ehdr->e_shoff, size); if (shdr == NULL) { err = ENOMEM; goto out; } /* Load shstrtab. */ shstrtab = alloc_pread(ef.fd, shdr[ef.ehdr->e_shstrndx].sh_offset, shdr[ef.ehdr->e_shstrndx].sh_size); if (shstrtab == NULL) { printf("\nelf" __XSTRING(__ELF_WORD_SIZE) "load_modmetadata: unable to load shstrtab\n"); err = EFTYPE; goto out; } /* Find set_modmetadata_set and data sections. */ sh_data[0] = sh_data[1] = sh_meta = NULL; for (i = 0, j = 0; i < ef.ehdr->e_shnum; i++) { if (strcmp(&shstrtab[shdr[i].sh_name], "set_modmetadata_set") == 0) { sh_meta = &shdr[i]; } if ((strcmp(&shstrtab[shdr[i].sh_name], ".data") == 0) || (strcmp(&shstrtab[shdr[i].sh_name], ".rodata") == 0)) { sh_data[j++] = &shdr[i]; } } if (sh_meta == NULL || sh_data[0] == NULL || sh_data[1] == NULL) { printf("\nelf" __XSTRING(__ELF_WORD_SIZE) "load_modmetadata: unable to find set_modmetadata_set or data sections\n"); err = EFTYPE; goto out; } /* Load set_modmetadata_set into memory */ err = kern_pread(ef.fd, dest, sh_meta->sh_size, sh_meta->sh_offset); if (err != 0) { printf("\nelf" __XSTRING(__ELF_WORD_SIZE) "load_modmetadata: unable to load set_modmetadata_set: %d\n", err); goto out; } p_start = dest; p_end = dest + sh_meta->sh_size; dest += sh_meta->sh_size; /* Load data sections into memory. */ err = kern_pread(ef.fd, dest, sh_data[0]->sh_size, sh_data[0]->sh_offset); if (err != 0) { printf("\nelf" __XSTRING(__ELF_WORD_SIZE) "load_modmetadata: unable to load data: %d\n", err); goto out; } /* * We have to increment the dest, so that the offset is the same into * both the .rodata and .data sections. */ ef.off = -(sh_data[0]->sh_addr - dest); dest += (sh_data[1]->sh_addr - sh_data[0]->sh_addr); err = kern_pread(ef.fd, dest, sh_data[1]->sh_size, sh_data[1]->sh_offset); if (err != 0) { printf("\nelf" __XSTRING(__ELF_WORD_SIZE) "load_modmetadata: unable to load data: %d\n", err); goto out; } err = __elfN(parse_modmetadata)(fp, &ef, p_start, p_end); if (err != 0) { printf("\nelf" __XSTRING(__ELF_WORD_SIZE) "load_modmetadata: unable to parse metadata: %d\n", err); goto out; } out: if (shstrtab != NULL) free(shstrtab); if (shdr != NULL) free(shdr); if (ef.firstpage != NULL) free(ef.firstpage); if (ef.fd != -1) close(ef.fd); return (err); } int __elfN(parse_modmetadata)(struct preloaded_file *fp, elf_file_t ef, Elf_Addr p_start, Elf_Addr p_end) { struct mod_metadata md; #if (defined(__i386__) || defined(__powerpc__)) && __ELF_WORD_SIZE == 64 struct mod_metadata64 md64; #elif defined(__amd64__) && __ELF_WORD_SIZE == 32 struct mod_metadata32 md32; #endif struct mod_depend *mdepend; struct mod_version mver; char *s; int error, modcnt, minfolen; Elf_Addr v, p; modcnt = 0; p = p_start; while (p < p_end) { COPYOUT(p, &v, sizeof(v)); error = __elfN(reloc_ptr)(fp, ef, p, &v, sizeof(v)); if (error == EOPNOTSUPP) v += ef->off; else if (error != 0) return (error); #if (defined(__i386__) || defined(__powerpc__)) && __ELF_WORD_SIZE == 64 COPYOUT(v, &md64, sizeof(md64)); error = __elfN(reloc_ptr)(fp, ef, v, &md64, sizeof(md64)); if (error == EOPNOTSUPP) { md64.md_cval += ef->off; md64.md_data += ef->off; } else if (error != 0) return (error); md.md_version = md64.md_version; md.md_type = md64.md_type; md.md_cval = (const char *)(uintptr_t)md64.md_cval; md.md_data = (void *)(uintptr_t)md64.md_data; #elif defined(__amd64__) && __ELF_WORD_SIZE == 32 COPYOUT(v, &md32, sizeof(md32)); error = __elfN(reloc_ptr)(fp, ef, v, &md32, sizeof(md32)); if (error == EOPNOTSUPP) { md32.md_cval += ef->off; md32.md_data += ef->off; } else if (error != 0) return (error); md.md_version = md32.md_version; md.md_type = md32.md_type; md.md_cval = (const char *)(uintptr_t)md32.md_cval; md.md_data = (void *)(uintptr_t)md32.md_data; #else COPYOUT(v, &md, sizeof(md)); error = __elfN(reloc_ptr)(fp, ef, v, &md, sizeof(md)); if (error == EOPNOTSUPP) { md.md_cval += ef->off; md.md_data = (void *)((uintptr_t)md.md_data + (uintptr_t)ef->off); } else if (error != 0) return (error); #endif p += sizeof(Elf_Addr); switch(md.md_type) { case MDT_DEPEND: if (ef->kernel) /* kernel must not depend on anything */ break; s = strdupout((vm_offset_t)md.md_cval); minfolen = sizeof(*mdepend) + strlen(s) + 1; mdepend = malloc(minfolen); if (mdepend == NULL) return ENOMEM; COPYOUT((vm_offset_t)md.md_data, mdepend, sizeof(*mdepend)); strcpy((char*)(mdepend + 1), s); free(s); file_addmetadata(fp, MODINFOMD_DEPLIST, minfolen, mdepend); free(mdepend); break; case MDT_VERSION: s = strdupout((vm_offset_t)md.md_cval); COPYOUT((vm_offset_t)md.md_data, &mver, sizeof(mver)); file_addmodule(fp, s, mver.mv_version, NULL); free(s); modcnt++; break; } } if (modcnt == 0) { s = fake_modname(fp->f_name); file_addmodule(fp, s, 1, NULL); free(s); } return 0; } static unsigned long elf_hash(const char *name) { const unsigned char *p = (const unsigned char *) name; unsigned long h = 0; unsigned long g; while (*p != '\0') { h = (h << 4) + *p++; if ((g = h & 0xf0000000) != 0) h ^= g >> 24; h &= ~g; } return h; } static const char __elfN(bad_symtable)[] = "elf" __XSTRING(__ELF_WORD_SIZE) "_lookup_symbol: corrupt symbol table\n"; int __elfN(lookup_symbol)(struct preloaded_file *fp, elf_file_t ef, const char* name, Elf_Sym *symp) { Elf_Hashelt symnum; Elf_Sym sym; char *strp; unsigned long hash; hash = elf_hash(name); COPYOUT(&ef->buckets[hash % ef->nbuckets], &symnum, sizeof(symnum)); while (symnum != STN_UNDEF) { if (symnum >= ef->nchains) { printf(__elfN(bad_symtable)); return ENOENT; } COPYOUT(ef->symtab + symnum, &sym, sizeof(sym)); if (sym.st_name == 0) { printf(__elfN(bad_symtable)); return ENOENT; } strp = strdupout((vm_offset_t)(ef->strtab + sym.st_name)); if (strcmp(name, strp) == 0) { free(strp); if (sym.st_shndx != SHN_UNDEF || (sym.st_value != 0 && ELF_ST_TYPE(sym.st_info) == STT_FUNC)) { *symp = sym; return 0; } return ENOENT; } free(strp); COPYOUT(&ef->chains[symnum], &symnum, sizeof(symnum)); } return ENOENT; } /* * Apply any intra-module relocations to the value. p is the load address * of the value and val/len is the value to be modified. This does NOT modify * the image in-place, because this is done by kern_linker later on. * * Returns EOPNOTSUPP if no relocation method is supplied. */ static int __elfN(reloc_ptr)(struct preloaded_file *mp, elf_file_t ef, Elf_Addr p, void *val, size_t len) { size_t n; Elf_Rela a; Elf_Rel r; int error; /* * The kernel is already relocated, but we still want to apply * offset adjustments. */ if (ef->kernel) return (EOPNOTSUPP); for (n = 0; n < ef->relsz / sizeof(r); n++) { COPYOUT(ef->rel + n, &r, sizeof(r)); error = __elfN(reloc)(ef, __elfN(symaddr), &r, ELF_RELOC_REL, ef->off, p, val, len); if (error != 0) return (error); } for (n = 0; n < ef->relasz / sizeof(a); n++) { COPYOUT(ef->rela + n, &a, sizeof(a)); error = __elfN(reloc)(ef, __elfN(symaddr), &a, ELF_RELOC_RELA, ef->off, p, val, len); if (error != 0) return (error); } return (0); } static Elf_Addr __elfN(symaddr)(struct elf_file *ef, Elf_Size symidx) { /* Symbol lookup by index not required here. */ return (0); } Index: stable/11/stand/common/part.c =================================================================== --- stable/11/stand/common/part.c (revision 346482) +++ stable/11/stand/common/part.c (revision 346483) @@ -1,1137 +1,1140 @@ /*- * Copyright (c) 2012 Andrey V. Elsukov * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHORS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ #include __FBSDID("$FreeBSD$"); #include #include #include #include #include #include #include #include #include #include #include #include #include #ifdef PART_DEBUG -#define DEBUG(fmt, args...) printf("%s: " fmt "\n", __func__, ## args) +#define DPRINTF(fmt, args...) printf("%s: " fmt "\n", __func__, ## args) #else -#define DEBUG(fmt, args...) +#define DPRINTF(fmt, args...) #endif #ifdef LOADER_GPT_SUPPORT #define MAXTBLSZ 64 static const uuid_t gpt_uuid_unused = GPT_ENT_TYPE_UNUSED; static const uuid_t gpt_uuid_ms_basic_data = GPT_ENT_TYPE_MS_BASIC_DATA; static const uuid_t gpt_uuid_freebsd_ufs = GPT_ENT_TYPE_FREEBSD_UFS; static const uuid_t gpt_uuid_efi = GPT_ENT_TYPE_EFI; static const uuid_t gpt_uuid_freebsd = GPT_ENT_TYPE_FREEBSD; static const uuid_t gpt_uuid_freebsd_boot = GPT_ENT_TYPE_FREEBSD_BOOT; static const uuid_t gpt_uuid_freebsd_nandfs = GPT_ENT_TYPE_FREEBSD_NANDFS; static const uuid_t gpt_uuid_freebsd_swap = GPT_ENT_TYPE_FREEBSD_SWAP; static const uuid_t gpt_uuid_freebsd_zfs = GPT_ENT_TYPE_FREEBSD_ZFS; static const uuid_t gpt_uuid_freebsd_vinum = GPT_ENT_TYPE_FREEBSD_VINUM; #endif struct pentry { struct ptable_entry part; uint64_t flags; union { uint8_t bsd; uint8_t mbr; uuid_t gpt; uint16_t vtoc8; } type; STAILQ_ENTRY(pentry) entry; }; struct ptable { enum ptable_type type; uint16_t sectorsize; uint64_t sectors; STAILQ_HEAD(, pentry) entries; }; static struct parttypes { enum partition_type type; const char *desc; } ptypes[] = { { PART_UNKNOWN, "Unknown" }, { PART_EFI, "EFI" }, { PART_FREEBSD, "FreeBSD" }, { PART_FREEBSD_BOOT, "FreeBSD boot" }, { PART_FREEBSD_NANDFS, "FreeBSD nandfs" }, { PART_FREEBSD_UFS, "FreeBSD UFS" }, { PART_FREEBSD_ZFS, "FreeBSD ZFS" }, { PART_FREEBSD_SWAP, "FreeBSD swap" }, { PART_FREEBSD_VINUM, "FreeBSD vinum" }, { PART_LINUX, "Linux" }, { PART_LINUX_SWAP, "Linux swap" }, { PART_DOS, "DOS/Windows" }, { PART_ISO9660, "ISO9660" }, }; const char * parttype2str(enum partition_type type) { size_t i; for (i = 0; i < nitems(ptypes); i++) if (ptypes[i].type == type) return (ptypes[i].desc); return (ptypes[0].desc); } #ifdef LOADER_GPT_SUPPORT static void uuid_letoh(uuid_t *uuid) { uuid->time_low = le32toh(uuid->time_low); uuid->time_mid = le16toh(uuid->time_mid); uuid->time_hi_and_version = le16toh(uuid->time_hi_and_version); } static enum partition_type gpt_parttype(uuid_t type) { if (uuid_equal(&type, &gpt_uuid_efi, NULL)) return (PART_EFI); else if (uuid_equal(&type, &gpt_uuid_ms_basic_data, NULL)) return (PART_DOS); else if (uuid_equal(&type, &gpt_uuid_freebsd_boot, NULL)) return (PART_FREEBSD_BOOT); else if (uuid_equal(&type, &gpt_uuid_freebsd_ufs, NULL)) return (PART_FREEBSD_UFS); else if (uuid_equal(&type, &gpt_uuid_freebsd_zfs, NULL)) return (PART_FREEBSD_ZFS); else if (uuid_equal(&type, &gpt_uuid_freebsd_swap, NULL)) return (PART_FREEBSD_SWAP); else if (uuid_equal(&type, &gpt_uuid_freebsd_vinum, NULL)) return (PART_FREEBSD_VINUM); else if (uuid_equal(&type, &gpt_uuid_freebsd_nandfs, NULL)) return (PART_FREEBSD_NANDFS); else if (uuid_equal(&type, &gpt_uuid_freebsd, NULL)) return (PART_FREEBSD); return (PART_UNKNOWN); } static struct gpt_hdr * gpt_checkhdr(struct gpt_hdr *hdr, uint64_t lba_self, uint64_t lba_last, uint16_t sectorsize) { uint32_t sz, crc; if (memcmp(hdr->hdr_sig, GPT_HDR_SIG, sizeof(hdr->hdr_sig)) != 0) { - DEBUG("no GPT signature"); + DPRINTF("no GPT signature"); return (NULL); } sz = le32toh(hdr->hdr_size); if (sz < 92 || sz > sectorsize) { - DEBUG("invalid GPT header size: %d", sz); + DPRINTF("invalid GPT header size: %d", sz); return (NULL); } crc = le32toh(hdr->hdr_crc_self); hdr->hdr_crc_self = 0; if (crc32(hdr, sz) != crc) { - DEBUG("GPT header's CRC doesn't match"); + DPRINTF("GPT header's CRC doesn't match"); return (NULL); } hdr->hdr_crc_self = crc; hdr->hdr_revision = le32toh(hdr->hdr_revision); if (hdr->hdr_revision < GPT_HDR_REVISION) { - DEBUG("unsupported GPT revision %d", hdr->hdr_revision); + DPRINTF("unsupported GPT revision %d", hdr->hdr_revision); return (NULL); } hdr->hdr_lba_self = le64toh(hdr->hdr_lba_self); if (hdr->hdr_lba_self != lba_self) { - DEBUG("self LBA doesn't match"); + DPRINTF("self LBA doesn't match"); return (NULL); } hdr->hdr_lba_alt = le64toh(hdr->hdr_lba_alt); if (hdr->hdr_lba_alt == hdr->hdr_lba_self) { - DEBUG("invalid alternate LBA"); + DPRINTF("invalid alternate LBA"); return (NULL); } hdr->hdr_entries = le32toh(hdr->hdr_entries); hdr->hdr_entsz = le32toh(hdr->hdr_entsz); if (hdr->hdr_entries == 0 || hdr->hdr_entsz < sizeof(struct gpt_ent) || sectorsize % hdr->hdr_entsz != 0) { - DEBUG("invalid entry size or number of entries"); + DPRINTF("invalid entry size or number of entries"); return (NULL); } hdr->hdr_lba_start = le64toh(hdr->hdr_lba_start); hdr->hdr_lba_end = le64toh(hdr->hdr_lba_end); hdr->hdr_lba_table = le64toh(hdr->hdr_lba_table); hdr->hdr_crc_table = le32toh(hdr->hdr_crc_table); uuid_letoh(&hdr->hdr_uuid); return (hdr); } static int gpt_checktbl(const struct gpt_hdr *hdr, uint8_t *tbl, size_t size, uint64_t lba_last) { struct gpt_ent *ent; uint32_t i, cnt; cnt = size / hdr->hdr_entsz; if (hdr->hdr_entries <= cnt) { cnt = hdr->hdr_entries; /* Check CRC only when buffer size is enough for table. */ if (hdr->hdr_crc_table != crc32(tbl, hdr->hdr_entries * hdr->hdr_entsz)) { - DEBUG("GPT table's CRC doesn't match"); + DPRINTF("GPT table's CRC doesn't match"); return (-1); } } for (i = 0; i < cnt; i++) { ent = (struct gpt_ent *)(tbl + i * hdr->hdr_entsz); uuid_letoh(&ent->ent_type); if (uuid_equal(&ent->ent_type, &gpt_uuid_unused, NULL)) continue; ent->ent_lba_start = le64toh(ent->ent_lba_start); ent->ent_lba_end = le64toh(ent->ent_lba_end); } return (0); } static struct ptable * ptable_gptread(struct ptable *table, void *dev, diskread_t dread) { struct pentry *entry; struct gpt_hdr *phdr, hdr; struct gpt_ent *ent; uint8_t *buf, *tbl; uint64_t offset; int pri, sec; size_t size, i; buf = malloc(table->sectorsize); if (buf == NULL) return (NULL); tbl = malloc(table->sectorsize * MAXTBLSZ); if (tbl == NULL) { free(buf); return (NULL); } /* Read the primary GPT header. */ if (dread(dev, buf, 1, 1) != 0) { ptable_close(table); table = NULL; goto out; } pri = sec = 0; /* Check the primary GPT header. */ phdr = gpt_checkhdr((struct gpt_hdr *)buf, 1, table->sectors - 1, table->sectorsize); if (phdr != NULL) { /* Read the primary GPT table. */ size = MIN(MAXTBLSZ, howmany(phdr->hdr_entries * phdr->hdr_entsz, table->sectorsize)); if (dread(dev, tbl, size, phdr->hdr_lba_table) == 0 && gpt_checktbl(phdr, tbl, size * table->sectorsize, table->sectors - 1) == 0) { memcpy(&hdr, phdr, sizeof(hdr)); pri = 1; } } offset = pri ? hdr.hdr_lba_alt: table->sectors - 1; /* Read the backup GPT header. */ if (dread(dev, buf, 1, offset) != 0) phdr = NULL; else phdr = gpt_checkhdr((struct gpt_hdr *)buf, offset, table->sectors - 1, table->sectorsize); if (phdr != NULL) { /* * Compare primary and backup headers. * If they are equal, then we do not need to read backup * table. If they are different, then prefer backup header * and try to read backup table. */ if (pri == 0 || uuid_equal(&hdr.hdr_uuid, &phdr->hdr_uuid, NULL) == 0 || hdr.hdr_revision != phdr->hdr_revision || hdr.hdr_size != phdr->hdr_size || hdr.hdr_lba_start != phdr->hdr_lba_start || hdr.hdr_lba_end != phdr->hdr_lba_end || hdr.hdr_entries != phdr->hdr_entries || hdr.hdr_entsz != phdr->hdr_entsz || hdr.hdr_crc_table != phdr->hdr_crc_table) { /* Read the backup GPT table. */ size = MIN(MAXTBLSZ, howmany(phdr->hdr_entries * phdr->hdr_entsz, table->sectorsize)); if (dread(dev, tbl, size, phdr->hdr_lba_table) == 0 && gpt_checktbl(phdr, tbl, size * table->sectorsize, table->sectors - 1) == 0) { memcpy(&hdr, phdr, sizeof(hdr)); sec = 1; } } } if (pri == 0 && sec == 0) { /* Both primary and backup tables are invalid. */ table->type = PTABLE_NONE; goto out; } - DEBUG("GPT detected"); + DPRINTF("GPT detected"); size = MIN(hdr.hdr_entries * hdr.hdr_entsz, MAXTBLSZ * table->sectorsize); /* * If the disk's sector count is smaller than the sector count recorded * in the disk's GPT table header, set the table->sectors to the value * recorded in GPT tables. This is done to work around buggy firmware * that returns truncated disk sizes. * * Note, this is still not a foolproof way to get disk's size. For * example, an image file can be truncated when copied to smaller media. */ table->sectors = hdr.hdr_lba_alt + 1; for (i = 0; i < size / hdr.hdr_entsz; i++) { ent = (struct gpt_ent *)(tbl + i * hdr.hdr_entsz); if (uuid_equal(&ent->ent_type, &gpt_uuid_unused, NULL)) continue; /* Simple sanity checks. */ if (ent->ent_lba_start < hdr.hdr_lba_start || ent->ent_lba_end > hdr.hdr_lba_end || ent->ent_lba_start > ent->ent_lba_end) continue; entry = malloc(sizeof(*entry)); if (entry == NULL) break; entry->part.start = ent->ent_lba_start; entry->part.end = ent->ent_lba_end; entry->part.index = i + 1; entry->part.type = gpt_parttype(ent->ent_type); entry->flags = le64toh(ent->ent_attr); memcpy(&entry->type.gpt, &ent->ent_type, sizeof(uuid_t)); STAILQ_INSERT_TAIL(&table->entries, entry, entry); - DEBUG("new GPT partition added"); + DPRINTF("new GPT partition added"); } out: free(buf); free(tbl); return (table); } #endif /* LOADER_GPT_SUPPORT */ #ifdef LOADER_MBR_SUPPORT /* We do not need to support too many EBR partitions in the loader */ #define MAXEBRENTRIES 8 static enum partition_type mbr_parttype(uint8_t type) { switch (type) { case DOSPTYP_386BSD: return (PART_FREEBSD); case DOSPTYP_LINSWP: return (PART_LINUX_SWAP); case DOSPTYP_LINUX: return (PART_LINUX); case 0x01: case 0x04: case 0x06: case 0x07: case 0x0b: case 0x0c: case 0x0e: return (PART_DOS); } return (PART_UNKNOWN); } static struct ptable * ptable_ebrread(struct ptable *table, void *dev, diskread_t dread) { struct dos_partition *dp; struct pentry *e1, *entry; uint32_t start, end, offset; u_char *buf; int i, index; STAILQ_FOREACH(e1, &table->entries, entry) { if (e1->type.mbr == DOSPTYP_EXT || e1->type.mbr == DOSPTYP_EXTLBA) break; } if (e1 == NULL) return (table); index = 5; offset = e1->part.start; buf = malloc(table->sectorsize); if (buf == NULL) return (table); - DEBUG("EBR detected"); + DPRINTF("EBR detected"); for (i = 0; i < MAXEBRENTRIES; i++) { #if 0 /* Some BIOSes return an incorrect number of sectors */ if (offset >= table->sectors) break; #endif if (dread(dev, buf, 1, offset) != 0) break; dp = (struct dos_partition *)(buf + DOSPARTOFF); if (dp[0].dp_typ == 0) break; start = le32toh(dp[0].dp_start); if (dp[0].dp_typ == DOSPTYP_EXT && dp[1].dp_typ == 0) { offset = e1->part.start + start; continue; } end = le32toh(dp[0].dp_size); entry = malloc(sizeof(*entry)); if (entry == NULL) break; entry->part.start = offset + start; entry->part.end = entry->part.start + end - 1; entry->part.index = index++; entry->part.type = mbr_parttype(dp[0].dp_typ); entry->flags = dp[0].dp_flag; entry->type.mbr = dp[0].dp_typ; STAILQ_INSERT_TAIL(&table->entries, entry, entry); - DEBUG("new EBR partition added"); + DPRINTF("new EBR partition added"); if (dp[1].dp_typ == 0) break; offset = e1->part.start + le32toh(dp[1].dp_start); } free(buf); return (table); } #endif /* LOADER_MBR_SUPPORT */ static enum partition_type bsd_parttype(uint8_t type) { switch (type) { case FS_NANDFS: return (PART_FREEBSD_NANDFS); case FS_SWAP: return (PART_FREEBSD_SWAP); case FS_BSDFFS: return (PART_FREEBSD_UFS); case FS_VINUM: return (PART_FREEBSD_VINUM); case FS_ZFS: return (PART_FREEBSD_ZFS); } return (PART_UNKNOWN); } static struct ptable * ptable_bsdread(struct ptable *table, void *dev, diskread_t dread) { struct disklabel *dl; struct partition *part; struct pentry *entry; uint8_t *buf; uint32_t raw_offset; int i; if (table->sectorsize < sizeof(struct disklabel)) { - DEBUG("Too small sectorsize"); + DPRINTF("Too small sectorsize"); return (table); } buf = malloc(table->sectorsize); if (buf == NULL) return (table); if (dread(dev, buf, 1, 1) != 0) { - DEBUG("read failed"); + DPRINTF("read failed"); ptable_close(table); table = NULL; goto out; } dl = (struct disklabel *)buf; if (le32toh(dl->d_magic) != DISKMAGIC && le32toh(dl->d_magic2) != DISKMAGIC) goto out; if (le32toh(dl->d_secsize) != table->sectorsize) { - DEBUG("unsupported sector size"); + DPRINTF("unsupported sector size"); goto out; } dl->d_npartitions = le16toh(dl->d_npartitions); if (dl->d_npartitions > 20 || dl->d_npartitions < 8) { - DEBUG("invalid number of partitions"); + DPRINTF("invalid number of partitions"); goto out; } - DEBUG("BSD detected"); + DPRINTF("BSD detected"); part = &dl->d_partitions[0]; raw_offset = le32toh(part[RAW_PART].p_offset); for (i = 0; i < dl->d_npartitions; i++, part++) { if (i == RAW_PART) continue; if (part->p_size == 0) continue; entry = malloc(sizeof(*entry)); if (entry == NULL) break; entry->part.start = le32toh(part->p_offset) - raw_offset; entry->part.end = entry->part.start + le32toh(part->p_size) - 1; entry->part.type = bsd_parttype(part->p_fstype); entry->part.index = i; /* starts from zero */ entry->type.bsd = part->p_fstype; STAILQ_INSERT_TAIL(&table->entries, entry, entry); - DEBUG("new BSD partition added"); + DPRINTF("new BSD partition added"); } table->type = PTABLE_BSD; out: free(buf); return (table); } #ifdef LOADER_VTOC8_SUPPORT static enum partition_type vtoc8_parttype(uint16_t type) { switch (type) { case VTOC_TAG_FREEBSD_NANDFS: return (PART_FREEBSD_NANDFS); case VTOC_TAG_FREEBSD_SWAP: return (PART_FREEBSD_SWAP); case VTOC_TAG_FREEBSD_UFS: return (PART_FREEBSD_UFS); case VTOC_TAG_FREEBSD_VINUM: return (PART_FREEBSD_VINUM); case VTOC_TAG_FREEBSD_ZFS: return (PART_FREEBSD_ZFS); } return (PART_UNKNOWN); } static struct ptable * ptable_vtoc8read(struct ptable *table, void *dev, diskread_t dread) { struct pentry *entry; struct vtoc8 *dl; uint8_t *buf; uint16_t sum, heads, sectors; int i; if (table->sectorsize != sizeof(struct vtoc8)) return (table); buf = malloc(table->sectorsize); if (buf == NULL) return (table); if (dread(dev, buf, 1, 0) != 0) { - DEBUG("read failed"); + DPRINTF("read failed"); ptable_close(table); table = NULL; goto out; } dl = (struct vtoc8 *)buf; /* Check the sum */ for (i = sum = 0; i < sizeof(struct vtoc8); i += sizeof(sum)) sum ^= be16dec(buf + i); if (sum != 0) { - DEBUG("incorrect checksum"); + DPRINTF("incorrect checksum"); goto out; } if (be16toh(dl->nparts) != VTOC8_NPARTS) { - DEBUG("invalid number of entries"); + DPRINTF("invalid number of entries"); goto out; } sectors = be16toh(dl->nsecs); heads = be16toh(dl->nheads); if (sectors * heads == 0) { - DEBUG("invalid geometry"); + DPRINTF("invalid geometry"); goto out; } - DEBUG("VTOC8 detected"); + DPRINTF("VTOC8 detected"); for (i = 0; i < VTOC8_NPARTS; i++) { dl->part[i].tag = be16toh(dl->part[i].tag); if (i == VTOC_RAW_PART || dl->part[i].tag == VTOC_TAG_UNASSIGNED) continue; entry = malloc(sizeof(*entry)); if (entry == NULL) break; entry->part.start = be32toh(dl->map[i].cyl) * heads * sectors; entry->part.end = be32toh(dl->map[i].nblks) + entry->part.start - 1; entry->part.type = vtoc8_parttype(dl->part[i].tag); entry->part.index = i; /* starts from zero */ entry->type.vtoc8 = dl->part[i].tag; STAILQ_INSERT_TAIL(&table->entries, entry, entry); - DEBUG("new VTOC8 partition added"); + DPRINTF("new VTOC8 partition added"); } table->type = PTABLE_VTOC8; out: free(buf); return (table); } #endif /* LOADER_VTOC8_SUPPORT */ #define cdb2devb(bno) ((bno) * ISO_DEFAULT_BLOCK_SIZE / table->sectorsize) static struct ptable * ptable_iso9660read(struct ptable *table, void *dev, diskread_t dread) { uint8_t *buf; struct iso_primary_descriptor *vd; struct pentry *entry; buf = malloc(table->sectorsize); if (buf == NULL) return (table); if (dread(dev, buf, 1, cdb2devb(16)) != 0) { - DEBUG("read failed"); + DPRINTF("read failed"); ptable_close(table); table = NULL; goto out; } vd = (struct iso_primary_descriptor *)buf; if (bcmp(vd->id, ISO_STANDARD_ID, sizeof vd->id) != 0) goto out; entry = malloc(sizeof(*entry)); if (entry == NULL) goto out; entry->part.start = 0; entry->part.end = table->sectors; entry->part.type = PART_ISO9660; entry->part.index = 0; STAILQ_INSERT_TAIL(&table->entries, entry, entry); table->type = PTABLE_ISO9660; out: free(buf); return (table); } struct ptable * ptable_open(void *dev, uint64_t sectors, uint16_t sectorsize, diskread_t *dread) { struct dos_partition *dp; struct ptable *table; uint8_t *buf; int i, count; #ifdef LOADER_MBR_SUPPORT struct pentry *entry; uint32_t start, end; int has_ext; #endif table = NULL; buf = malloc(sectorsize); if (buf == NULL) return (NULL); /* First, read the MBR. */ if (dread(dev, buf, 1, DOSBBSECTOR) != 0) { - DEBUG("read failed"); + DPRINTF("read failed"); goto out; } table = malloc(sizeof(*table)); if (table == NULL) goto out; table->sectors = sectors; table->sectorsize = sectorsize; table->type = PTABLE_NONE; STAILQ_INIT(&table->entries); if (ptable_iso9660read(table, dev, dread) == NULL) { /* Read error. */ table = NULL; goto out; } else if (table->type == PTABLE_ISO9660) goto out; #ifdef LOADER_VTOC8_SUPPORT if (be16dec(buf + offsetof(struct vtoc8, magic)) == VTOC_MAGIC) { if (ptable_vtoc8read(table, dev, dread) == NULL) { /* Read error. */ table = NULL; goto out; } else if (table->type == PTABLE_VTOC8) goto out; } #endif /* Check the BSD label. */ if (ptable_bsdread(table, dev, dread) == NULL) { /* Read error. */ table = NULL; goto out; } else if (table->type == PTABLE_BSD) goto out; #if defined(LOADER_GPT_SUPPORT) || defined(LOADER_MBR_SUPPORT) /* Check the MBR magic. */ if (buf[DOSMAGICOFFSET] != 0x55 || buf[DOSMAGICOFFSET + 1] != 0xaa) { - DEBUG("magic sequence not found"); + DPRINTF("magic sequence not found"); #if defined(LOADER_GPT_SUPPORT) /* There is no PMBR, check that we have backup GPT */ table->type = PTABLE_GPT; table = ptable_gptread(table, dev, dread); #endif goto out; } /* Check that we have PMBR. Also do some validation. */ dp = (struct dos_partition *)(buf + DOSPARTOFF); for (i = 0, count = 0; i < NDOSPART; i++) { if (dp[i].dp_flag != 0 && dp[i].dp_flag != 0x80) { - DEBUG("invalid partition flag %x", dp[i].dp_flag); + DPRINTF("invalid partition flag %x", dp[i].dp_flag); goto out; } #ifdef LOADER_GPT_SUPPORT if (dp[i].dp_typ == DOSPTYP_PMBR) { table->type = PTABLE_GPT; - DEBUG("PMBR detected"); + DPRINTF("PMBR detected"); } #endif if (dp[i].dp_typ != 0) count++; } /* Do we have some invalid values? */ if (table->type == PTABLE_GPT && count > 1) { if (dp[1].dp_typ != DOSPTYP_HFS) { table->type = PTABLE_NONE; - DEBUG("Incorrect PMBR, ignore it"); + DPRINTF("Incorrect PMBR, ignore it"); } else { - DEBUG("Bootcamp detected"); + DPRINTF("Bootcamp detected"); } } #ifdef LOADER_GPT_SUPPORT if (table->type == PTABLE_GPT) { table = ptable_gptread(table, dev, dread); goto out; } #endif #ifdef LOADER_MBR_SUPPORT /* Read MBR. */ - DEBUG("MBR detected"); + DPRINTF("MBR detected"); table->type = PTABLE_MBR; for (i = has_ext = 0; i < NDOSPART; i++) { if (dp[i].dp_typ == 0) continue; start = le32dec(&(dp[i].dp_start)); end = le32dec(&(dp[i].dp_size)); if (start == 0 || end == 0) continue; #if 0 /* Some BIOSes return an incorrect number of sectors */ if (start + end - 1 >= sectors) continue; /* XXX: ignore */ #endif if (dp[i].dp_typ == DOSPTYP_EXT || dp[i].dp_typ == DOSPTYP_EXTLBA) has_ext = 1; entry = malloc(sizeof(*entry)); if (entry == NULL) break; entry->part.start = start; entry->part.end = start + end - 1; entry->part.index = i + 1; entry->part.type = mbr_parttype(dp[i].dp_typ); entry->flags = dp[i].dp_flag; entry->type.mbr = dp[i].dp_typ; STAILQ_INSERT_TAIL(&table->entries, entry, entry); - DEBUG("new MBR partition added"); + DPRINTF("new MBR partition added"); } if (has_ext) { table = ptable_ebrread(table, dev, dread); /* FALLTHROUGH */ } #endif /* LOADER_MBR_SUPPORT */ #endif /* LOADER_MBR_SUPPORT || LOADER_GPT_SUPPORT */ out: free(buf); return (table); } void ptable_close(struct ptable *table) { struct pentry *entry; + + if (table == NULL) + return; while (!STAILQ_EMPTY(&table->entries)) { entry = STAILQ_FIRST(&table->entries); STAILQ_REMOVE_HEAD(&table->entries, entry); free(entry); } free(table); } enum ptable_type ptable_gettype(const struct ptable *table) { return (table->type); } int ptable_getsize(const struct ptable *table, uint64_t *sizep) { uint64_t tmp = table->sectors * table->sectorsize; if (tmp < table->sectors) return (EOVERFLOW); if (sizep != NULL) *sizep = tmp; return (0); } int ptable_getpart(const struct ptable *table, struct ptable_entry *part, int index) { struct pentry *entry; if (part == NULL || table == NULL) return (EINVAL); STAILQ_FOREACH(entry, &table->entries, entry) { if (entry->part.index != index) continue; memcpy(part, &entry->part, sizeof(*part)); return (0); } return (ENOENT); } /* * Search for a slice with the following preferences: * * 1: Active FreeBSD slice * 2: Non-active FreeBSD slice * 3: Active Linux slice * 4: non-active Linux slice * 5: Active FAT/FAT32 slice * 6: non-active FAT/FAT32 slice */ #define PREF_RAWDISK 0 #define PREF_FBSD_ACT 1 #define PREF_FBSD 2 #define PREF_LINUX_ACT 3 #define PREF_LINUX 4 #define PREF_DOS_ACT 5 #define PREF_DOS 6 #define PREF_NONE 7 int ptable_getbestpart(const struct ptable *table, struct ptable_entry *part) { struct pentry *entry, *best; int pref, preflevel; if (part == NULL || table == NULL) return (EINVAL); best = NULL; preflevel = pref = PREF_NONE; STAILQ_FOREACH(entry, &table->entries, entry) { #ifdef LOADER_MBR_SUPPORT if (table->type == PTABLE_MBR) { switch (entry->type.mbr) { case DOSPTYP_386BSD: pref = entry->flags & 0x80 ? PREF_FBSD_ACT: PREF_FBSD; break; case DOSPTYP_LINUX: pref = entry->flags & 0x80 ? PREF_LINUX_ACT: PREF_LINUX; break; case 0x01: /* DOS/Windows */ case 0x04: case 0x06: case 0x0c: case 0x0e: case DOSPTYP_FAT32: pref = entry->flags & 0x80 ? PREF_DOS_ACT: PREF_DOS; break; default: pref = PREF_NONE; } } #endif /* LOADER_MBR_SUPPORT */ #ifdef LOADER_GPT_SUPPORT if (table->type == PTABLE_GPT) { if (entry->part.type == PART_DOS) pref = PREF_DOS; else if (entry->part.type == PART_FREEBSD_UFS || entry->part.type == PART_FREEBSD_ZFS) pref = PREF_FBSD; else pref = PREF_NONE; } #endif /* LOADER_GPT_SUPPORT */ #ifdef LOADER_PC98_SUPPORT if (table->type == PTABLE_PC98) { switch(entry->part.type & PC98_MID_MASK) { case PC98_MID_386BSD: /* FreeBSD */ if ((entry->part.type & PC98_MID_BOOTABLE) && (preflevel > PREF_FBSD_ACT)) { pref = i; preflevel = PREF_FBSD_ACT; } else if (preflevel > PREF_FBSD) { pref = i; preflevel = PREF_FBSD; } break; case 0x11: /* DOS/Windows */ case 0x20: case 0x21: case 0x22: case 0x23: case 0x63: if ((entry->part.type & PC98_MID_BOOTABLE) && (preflevel > PREF_DOS_ACT)) { pref = i; preflevel = PREF_DOS_ACT; } else if (preflevel > PREF_DOS) { pref = i; preflevel = PREF_DOS; } break; } } #endif /* LOADER_PC98_SUPPORT */ if (pref < preflevel) { preflevel = pref; best = entry; } } if (best != NULL) { memcpy(part, &best->part, sizeof(*part)); return (0); } return (ENOENT); } int ptable_iterate(const struct ptable *table, void *arg, ptable_iterate_t *iter) { struct pentry *entry; char name[32]; int ret = 0; name[0] = '\0'; STAILQ_FOREACH(entry, &table->entries, entry) { #ifdef LOADER_MBR_SUPPORT if (table->type == PTABLE_MBR) sprintf(name, "s%d", entry->part.index); else #endif #ifdef LOADER_GPT_SUPPORT if (table->type == PTABLE_GPT) sprintf(name, "p%d", entry->part.index); else #endif #ifdef LOADER_VTOC8_SUPPORT if (table->type == PTABLE_VTOC8) sprintf(name, "%c", (uint8_t) 'a' + entry->part.index); else #endif if (table->type == PTABLE_BSD) sprintf(name, "%c", (uint8_t) 'a' + entry->part.index); if ((ret = iter(arg, name, &entry->part)) != 0) return (ret); } return (ret); } #ifdef LOADER_PC98_SUPPORT static int bd_open_pc98(struct open_disk *od, struct i386_devdesc *dev) { struct pc98_partition *dptr; struct disklabel *lp; int sector, slice, i; char buf[BUFSIZE]; /* * Following calculations attempt to determine the correct value * for d->od_boff by looking for the slice and partition specified, * or searching for reasonable defaults. */ /* * Find the slice in the DOS slice table. */ od->od_nslices = 0; if (od->od_flags & BD_FLOPPY) { sector = 0; goto unsliced; } if (bd_read(od, 0, 1, buf)) { DEBUG("error reading MBR"); return (EIO); } /* * Check the slice table magic. */ if (((u_char)buf[0x1fe] != 0x55) || ((u_char)buf[0x1ff] != 0xaa)) { /* If a slice number was explicitly supplied, this is an error */ if (dev->d_kind.biosdisk.slice > 0) { DEBUG("no slice table/MBR (no magic)"); return (ENOENT); } sector = 0; goto unsliced; /* may be a floppy */ } if (bd_read(od, 1, 1, buf)) { DEBUG("error reading MBR"); return (EIO); } /* * copy the partition table, then pick up any extended partitions. */ bcopy(buf + PC98_PARTOFF, &od->od_slicetab, sizeof(struct pc98_partition) * PC98_NPARTS); od->od_nslices = PC98_NPARTS; /* extended slices start here */ od->od_flags |= BD_PARTTABOK; dptr = &od->od_slicetab[0]; /* Is this a request for the whole disk? */ if (dev->d_kind.biosdisk.slice == -1) { sector = 0; goto unsliced; } /* * if a slice number was supplied but not found, this is an error. */ if (dev->d_kind.biosdisk.slice > 0) { slice = dev->d_kind.biosdisk.slice - 1; if (slice >= od->od_nslices) { DEBUG("slice %d not found", slice); return (ENOENT); } } /* Try to auto-detect the best slice; this should always give a slice number */ if (dev->d_kind.biosdisk.slice == 0) { slice = bd_bestslice(od); if (slice == -1) { return (ENOENT); } dev->d_kind.biosdisk.slice = slice; } dptr = &od->od_slicetab[0]; /* * Accept the supplied slice number unequivocally (we may be looking * at a DOS partition). */ dptr += (dev->d_kind.biosdisk.slice - 1); /* we number 1-4, offsets are 0-3 */ sector = dptr->dp_scyl * od->od_hds * od->od_sec + dptr->dp_shd * od->od_sec + dptr->dp_ssect; { int end = dptr->dp_ecyl * od->od_hds * od->od_sec + dptr->dp_ehd * od->od_sec + dptr->dp_esect; DEBUG("slice entry %d at %d, %d sectors", dev->d_kind.biosdisk.slice - 1, sector, end-sector); } /* * If we are looking at a BSD slice, and the partition is < 0, assume the 'a' partition */ if ((dptr->dp_mid == DOSMID_386BSD) && (dev->d_kind.biosdisk.partition < 0)) dev->d_kind.biosdisk.partition = 0; unsliced: /* * Now we have the slice offset, look for the partition in the disklabel if we have * a partition to start with. * * XXX we might want to check the label checksum. */ if (dev->d_kind.biosdisk.partition < 0) { od->od_boff = sector; /* no partition, must be after the slice */ DEBUG("opening raw slice"); } else { if (bd_read(od, sector + LABELSECTOR, 1, buf)) { DEBUG("error reading disklabel"); return (EIO); } DEBUG("copy %d bytes of label from %p to %p", sizeof(struct disklabel), buf + LABELOFFSET, &od->od_disklabel); bcopy(buf + LABELOFFSET, &od->od_disklabel, sizeof(struct disklabel)); lp = &od->od_disklabel; od->od_flags |= BD_LABELOK; if (lp->d_magic != DISKMAGIC) { DEBUG("no disklabel"); return (ENOENT); } if (dev->d_kind.biosdisk.partition >= lp->d_npartitions) { DEBUG("partition '%c' exceeds partitions in table (a-'%c')", 'a' + dev->d_kind.biosdisk.partition, 'a' + lp->d_npartitions); return (EPART); } #ifdef DISK_DEBUG /* Complain if the partition is unused unless this is a floppy. */ if ((lp->d_partitions[dev->d_kind.biosdisk.partition].p_fstype == FS_UNUSED) && !(od->od_flags & BD_FLOPPY)) DEBUG("warning, partition marked as unused"); #endif od->od_boff = lp->d_partitions[dev->d_kind.biosdisk.partition].p_offset - lp->d_partitions[RAW_PART].p_offset + sector; } return (0); } static void bd_closedisk(struct open_disk *od) { DEBUG("open_disk %p", od); #if 0 /* XXX is this required? (especially if disk already open...) */ if (od->od_flags & BD_FLOPPY) delay(3000000); #endif free(od); } #endif /* LOADER_PC98_SUPPORT */ Index: stable/11/stand/efi/libefi/efienv.c =================================================================== --- stable/11/stand/efi/libefi/efienv.c (revision 346482) +++ stable/11/stand/efi/libefi/efienv.c (revision 346483) @@ -1,87 +1,87 @@ /*- * Copyright (c) 2018 Netflix, Inc. * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ #include __FBSDID("$FreeBSD$"); #include #include #include #include static EFI_GUID FreeBSDBootVarGUID = FREEBSD_BOOT_VAR_GUID; static EFI_GUID GlobalBootVarGUID = EFI_GLOBAL_VARIABLE; EFI_STATUS efi_getenv(EFI_GUID *g, const char *v, void *data, size_t *len) { size_t ul; CHAR16 *uv; UINT32 attr; UINTN dl; EFI_STATUS rv; uv = NULL; if (utf8_to_ucs2(v, &uv, &ul) != 0) return (EFI_OUT_OF_RESOURCES); dl = *len; rv = RS->GetVariable(uv, g, &attr, &dl, data); - if (rv == EFI_SUCCESS) + if (rv == EFI_SUCCESS || rv == EFI_BUFFER_TOO_SMALL) *len = dl; free(uv); return (rv); } EFI_STATUS efi_global_getenv(const char *v, void *data, size_t *len) { return (efi_getenv(&GlobalBootVarGUID, v, data, len)); } EFI_STATUS efi_freebsd_getenv(const char *v, void *data, size_t *len) { return (efi_getenv(&FreeBSDBootVarGUID, v, data, len)); } EFI_STATUS efi_setenv_freebsd_wcs(const char *varname, CHAR16 *valstr) { CHAR16 *var = NULL; size_t len; EFI_STATUS rv; if (utf8_to_ucs2(varname, &var, &len) != 0) return (EFI_OUT_OF_RESOURCES); rv = RS->SetVariable(var, &FreeBSDBootVarGUID, EFI_VARIABLE_BOOTSERVICE_ACCESS | EFI_VARIABLE_RUNTIME_ACCESS, (ucs2len(valstr) + 1) * sizeof(efi_char), valstr); free(var); return (rv); } Index: stable/11/stand/efi/loader/bootinfo.c =================================================================== --- stable/11/stand/efi/loader/bootinfo.c (revision 346482) +++ stable/11/stand/efi/loader/bootinfo.c (revision 346483) @@ -1,534 +1,552 @@ /*- * Copyright (c) 1998 Michael Smith * Copyright (c) 2004, 2006 Marcel Moolenaar * Copyright (c) 2014 The FreeBSD Foundation * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ #include __FBSDID("$FreeBSD$"); #include #include #include #include #include #include #include #include #include #include #include #include #include "bootstrap.h" #include "loader_efi.h" #if defined(__amd64__) #include #endif #include "framebuffer.h" #if defined(LOADER_FDT_SUPPORT) #include #endif #ifdef LOADER_GELI_SUPPORT #include "geliboot.h" #endif int bi_load(char *args, vm_offset_t *modulep, vm_offset_t *kernendp); extern EFI_SYSTEM_TABLE *ST; static int bi_getboothowto(char *kargs) { const char *sw, *tmp; char *opts; char *console; int howto, speed, port; char buf[50]; howto = boot_parse_cmdline(kargs); howto |= boot_env_to_howto(); console = getenv("console"); if (console != NULL) { if (strcmp(console, "comconsole") == 0) howto |= RB_SERIAL; if (strcmp(console, "nullconsole") == 0) howto |= RB_MUTE; #if defined(__i386__) || defined(__amd64__) if (strcmp(console, "efi") == 0 && getenv("efi_8250_uid") != NULL && getenv("hw.uart.console") == NULL) { /* * If we found a 8250 com port and com speed, we need to * tell the kernel where the serial port is, and how * fast. Ideally, we'd get the port from ACPI, but that * isn't running in the loader. Do the next best thing * by allowing it to be set by a loader.conf variable, * either a EFI specific one, or the compatible * comconsole_port if not. PCI support is needed, but * for that we'd ideally refactor the * libi386/comconsole.c code to have identical behavior. * We only try to set the port for cases where we saw * the Serial(x) node when parsing, otherwise * specialized hardware that has Uart nodes will have a * bogus address set. * But if someone specifically setup hw.uart.console, * don't override that. */ speed = -1; port = -1; tmp = getenv("efi_com_speed"); if (tmp != NULL) speed = strtol(tmp, NULL, 0); tmp = getenv("efi_com_port"); if (tmp == NULL) tmp = getenv("comconsole_port"); if (tmp != NULL) port = strtol(tmp, NULL, 0); if (speed != -1 && port != -1) { snprintf(buf, sizeof(buf), "io:%d,br:%d", port, speed); env_setenv("hw.uart.console", EV_VOLATILE, buf, NULL, NULL); } } #endif } return (howto); } /* * Copy the environment into the load area starting at (addr). * Each variable is formatted as =, with a single nul * separating each variable, and a double nul terminating the environment. */ static vm_offset_t bi_copyenv(vm_offset_t start) { struct env_var *ep; vm_offset_t addr, last; size_t len; addr = last = start; /* Traverse the environment. */ for (ep = environ; ep != NULL; ep = ep->ev_next) { len = strlen(ep->ev_name); if ((size_t)archsw.arch_copyin(ep->ev_name, addr, len) != len) break; addr += len; if (archsw.arch_copyin("=", addr, 1) != 1) break; addr++; if (ep->ev_value != NULL) { len = strlen(ep->ev_value); if ((size_t)archsw.arch_copyin(ep->ev_value, addr, len) != len) break; addr += len; } if (archsw.arch_copyin("", addr, 1) != 1) break; last = ++addr; } if (archsw.arch_copyin("", last++, 1) != 1) last = start; return(last); } /* * Copy module-related data into the load area, where it can be * used as a directory for loaded modules. * * Module data is presented in a self-describing format. Each datum * is preceded by a 32-bit identifier and a 32-bit size field. * * Currently, the following data are saved: * * MOD_NAME (variable) module name (string) * MOD_TYPE (variable) module type (string) * MOD_ARGS (variable) module parameters (string) * MOD_ADDR sizeof(vm_offset_t) module load address * MOD_SIZE sizeof(size_t) module size * MOD_METADATA (variable) type-specific metadata */ #define COPY32(v, a, c) { \ uint32_t x = (v); \ if (c) \ archsw.arch_copyin(&x, a, sizeof(x)); \ a += sizeof(x); \ } #define MOD_STR(t, a, s, c) { \ COPY32(t, a, c); \ COPY32(strlen(s) + 1, a, c); \ if (c) \ archsw.arch_copyin(s, a, strlen(s) + 1); \ a += roundup(strlen(s) + 1, sizeof(u_long)); \ } #define MOD_NAME(a, s, c) MOD_STR(MODINFO_NAME, a, s, c) #define MOD_TYPE(a, s, c) MOD_STR(MODINFO_TYPE, a, s, c) #define MOD_ARGS(a, s, c) MOD_STR(MODINFO_ARGS, a, s, c) #define MOD_VAR(t, a, s, c) { \ COPY32(t, a, c); \ COPY32(sizeof(s), a, c); \ if (c) \ archsw.arch_copyin(&s, a, sizeof(s)); \ a += roundup(sizeof(s), sizeof(u_long)); \ } #define MOD_ADDR(a, s, c) MOD_VAR(MODINFO_ADDR, a, s, c) #define MOD_SIZE(a, s, c) MOD_VAR(MODINFO_SIZE, a, s, c) #define MOD_METADATA(a, mm, c) { \ COPY32(MODINFO_METADATA | mm->md_type, a, c); \ COPY32(mm->md_size, a, c); \ if (c) \ archsw.arch_copyin(mm->md_data, a, mm->md_size); \ a += roundup(mm->md_size, sizeof(u_long)); \ } #define MOD_END(a, c) { \ COPY32(MODINFO_END, a, c); \ COPY32(0, a, c); \ } static vm_offset_t bi_copymodules(vm_offset_t addr) { struct preloaded_file *fp; struct file_metadata *md; int c; uint64_t v; c = addr != 0; /* Start with the first module on the list, should be the kernel. */ for (fp = file_findfile(NULL, NULL); fp != NULL; fp = fp->f_next) { MOD_NAME(addr, fp->f_name, c); /* This must come first. */ MOD_TYPE(addr, fp->f_type, c); if (fp->f_args) MOD_ARGS(addr, fp->f_args, c); v = fp->f_addr; #if defined(__arm__) v -= __elfN(relocation_offset); #endif MOD_ADDR(addr, v, c); v = fp->f_size; MOD_SIZE(addr, v, c); for (md = fp->f_metadata; md != NULL; md = md->md_next) if (!(md->md_type & MODINFOMD_NOCOPY)) MOD_METADATA(addr, md, c); } MOD_END(addr, c); return(addr); } static EFI_STATUS efi_do_vmap(EFI_MEMORY_DESCRIPTOR *mm, UINTN sz, UINTN mmsz, UINT32 mmver) { EFI_MEMORY_DESCRIPTOR *desc, *viter, *vmap; EFI_STATUS ret; int curr, ndesc, nset; nset = 0; desc = mm; ndesc = sz / mmsz; vmap = malloc(sz); if (vmap == NULL) /* This isn't really an EFI error case, but pretend it is */ return (EFI_OUT_OF_RESOURCES); viter = vmap; for (curr = 0; curr < ndesc; curr++, desc = NextMemoryDescriptor(desc, mmsz)) { if ((desc->Attribute & EFI_MEMORY_RUNTIME) != 0) { ++nset; desc->VirtualStart = desc->PhysicalStart; *viter = *desc; viter = NextMemoryDescriptor(viter, mmsz); } } ret = RS->SetVirtualAddressMap(nset * mmsz, mmsz, mmver, vmap); free(vmap); return (ret); } static int bi_load_efi_data(struct preloaded_file *kfp) { EFI_MEMORY_DESCRIPTOR *mm; - EFI_PHYSICAL_ADDRESS addr; + EFI_PHYSICAL_ADDRESS addr = 0; EFI_STATUS status; const char *efi_novmap; size_t efisz; UINTN efi_mapkey; - UINTN mmsz, pages, retry, sz; + UINTN dsz, pages, retry, sz; UINT32 mmver; struct efi_map_header *efihdr; bool do_vmap; #if defined(__amd64__) || defined(__aarch64__) struct efi_fb efifb; if (efi_find_framebuffer(&efifb) == 0) { printf("EFI framebuffer information:\n"); printf("addr, size 0x%jx, 0x%jx\n", efifb.fb_addr, efifb.fb_size); printf("dimensions %d x %d\n", efifb.fb_width, efifb.fb_height); printf("stride %d\n", efifb.fb_stride); printf("masks 0x%08x, 0x%08x, 0x%08x, 0x%08x\n", efifb.fb_mask_red, efifb.fb_mask_green, efifb.fb_mask_blue, efifb.fb_mask_reserved); file_addmetadata(kfp, MODINFOMD_EFI_FB, sizeof(efifb), &efifb); } #endif do_vmap = true; efi_novmap = getenv("efi_disable_vmap"); if (efi_novmap != NULL) do_vmap = strcasecmp(efi_novmap, "YES") != 0; efisz = (sizeof(struct efi_map_header) + 0xf) & ~0xf; /* - * Assgin size of EFI_MEMORY_DESCRIPTOR to keep compatible with + * Assign size of EFI_MEMORY_DESCRIPTOR to keep compatible with * u-boot which doesn't fill this value when buffer for memory * descriptors is too small (eg. 0 to obtain memory map size) */ - mmsz = sizeof(EFI_MEMORY_DESCRIPTOR); + dsz = sizeof(EFI_MEMORY_DESCRIPTOR); /* - * It is possible that the first call to ExitBootServices may change - * the map key. Fetch a new map key and retry ExitBootServices in that - * case. + * Allocate enough pages to hold the bootinfo block and the + * memory map EFI will return to us. The memory map has an + * unknown size, so we have to determine that first. Note that + * the AllocatePages call can itself modify the memory map, so + * we have to take that into account as well. The changes to + * the memory map are caused by splitting a range of free + * memory into two, so that one is marked as being loader + * data. */ + + sz = 0; + + /* + * Matthew Garrett has observed at least one system changing the + * memory map when calling ExitBootServices, causing it to return an + * error, probably because callbacks are allocating memory. + * So we need to retry calling it at least once. + */ for (retry = 2; retry > 0; retry--) { - /* - * Allocate enough pages to hold the bootinfo block and the - * memory map EFI will return to us. The memory map has an - * unknown size, so we have to determine that first. Note that - * the AllocatePages call can itself modify the memory map, so - * we have to take that into account as well. The changes to - * the memory map are caused by splitting a range of free - * memory into two (AFAICT), so that one is marked as being - * loader data. - */ - sz = 0; - BS->GetMemoryMap(&sz, NULL, &efi_mapkey, &mmsz, &mmver); - sz += mmsz; - sz = (sz + 0xf) & ~0xf; - pages = EFI_SIZE_TO_PAGES(sz + efisz); - status = BS->AllocatePages(AllocateAnyPages, EfiLoaderData, - pages, &addr); - if (EFI_ERROR(status)) { - printf("%s: AllocatePages error %lu\n", __func__, - EFI_ERROR_CODE(status)); - return (ENOMEM); - } + for (;;) { + status = BS->GetMemoryMap(&sz, mm, &efi_mapkey, &dsz, &mmver); + if (!EFI_ERROR(status)) + break; - /* - * Read the memory map and stash it after bootinfo. Align the - * memory map on a 16-byte boundary (the bootinfo block is page - * aligned). - */ - efihdr = (struct efi_map_header *)(uintptr_t)addr; - mm = (void *)((uint8_t *)efihdr + efisz); - sz = (EFI_PAGE_SIZE * pages) - efisz; + if (status != EFI_BUFFER_TOO_SMALL) { + printf("%s: GetMemoryMap error %lu\n", __func__, + EFI_ERROR_CODE(status)); + return (EINVAL); + } - status = BS->GetMemoryMap(&sz, mm, &efi_mapkey, &mmsz, &mmver); - if (EFI_ERROR(status)) { - printf("%s: GetMemoryMap error %lu\n", __func__, - EFI_ERROR_CODE(status)); - return (EINVAL); - } - status = BS->ExitBootServices(IH, efi_mapkey); - if (EFI_ERROR(status) == 0) { + if (addr != 0) + BS->FreePages(addr, pages); + + /* Add 10 descriptors to the size to allow for + * fragmentation caused by calling AllocatePages */ + sz += (10 * dsz); + pages = EFI_SIZE_TO_PAGES(sz + efisz); + status = BS->AllocatePages(AllocateAnyPages, EfiLoaderData, + pages, &addr); + if (EFI_ERROR(status)) { + printf("%s: AllocatePages error %lu\n", __func__, + EFI_ERROR_CODE(status)); + return (ENOMEM); + } + /* - * This may be disabled by setting efi_disable_vmap in - * loader.conf(5). By default we will setup the virtual - * map entries. + * Read the memory map and stash it after bootinfo. Align the + * memory map on a 16-byte boundary (the bootinfo block is page + * aligned). */ - if (do_vmap) - efi_do_vmap(mm, sz, mmsz, mmver); - efihdr->memory_size = sz; - efihdr->descriptor_size = mmsz; - efihdr->descriptor_version = mmver; - file_addmetadata(kfp, MODINFOMD_EFI_MAP, efisz + sz, - efihdr); - return (0); + efihdr = (struct efi_map_header *)(uintptr_t)addr; + mm = (void *)((uint8_t *)efihdr + efisz); + sz = (EFI_PAGE_SIZE * pages) - efisz; } + + status = BS->ExitBootServices(IH, efi_mapkey); + if (!EFI_ERROR(status)) + break; + } + + if (retry == 0) { BS->FreePages(addr, pages); + printf("ExitBootServices error %lu\n", EFI_ERROR_CODE(status)); + return (EINVAL); } - printf("ExitBootServices error %lu\n", EFI_ERROR_CODE(status)); - return (EINVAL); + + /* + * This may be disabled by setting efi_disable_vmap in + * loader.conf(5). By default we will setup the virtual + * map entries. + */ + + if (do_vmap) + efi_do_vmap(mm, sz, dsz, mmver); + efihdr->memory_size = sz; + efihdr->descriptor_size = dsz; + efihdr->descriptor_version = mmver; + file_addmetadata(kfp, MODINFOMD_EFI_MAP, efisz + sz, + efihdr); + + return (0); } /* * Load the information expected by an amd64 kernel. * * - The 'boothowto' argument is constructed. * - The 'bootdev' argument is constructed. * - The 'bootinfo' struct is constructed, and copied into the kernel space. * - The kernel environment is copied into kernel space. * - Module metadata are formatted and placed in kernel space. */ int bi_load(char *args, vm_offset_t *modulep, vm_offset_t *kernendp) { struct preloaded_file *xp, *kfp; struct devdesc *rootdev; struct file_metadata *md; vm_offset_t addr; uint64_t kernend; uint64_t envp; vm_offset_t size; char *rootdevname; int howto; #if defined(LOADER_FDT_SUPPORT) vm_offset_t dtbp; int dtb_size; #endif #if defined(__arm__) vm_offset_t vaddr; size_t i; /* * These metadata addreses must be converted for kernel after * relocation. */ uint32_t mdt[] = { MODINFOMD_SSYM, MODINFOMD_ESYM, MODINFOMD_KERNEND, MODINFOMD_ENVP, #if defined(LOADER_FDT_SUPPORT) MODINFOMD_DTBP #endif }; #endif howto = bi_getboothowto(args); /* * Allow the environment variable 'rootdev' to override the supplied * device. This should perhaps go to MI code and/or have $rootdev * tested/set by MI code before launching the kernel. */ rootdevname = getenv("rootdev"); archsw.arch_getdev((void**)(&rootdev), rootdevname, NULL); if (rootdev == NULL) { printf("Can't determine root device.\n"); return(EINVAL); } /* Try reading the /etc/fstab file to select the root device */ getrootmount(efi_fmtdev((void *)rootdev)); addr = 0; for (xp = file_findfile(NULL, NULL); xp != NULL; xp = xp->f_next) { if (addr < (xp->f_addr + xp->f_size)) addr = xp->f_addr + xp->f_size; } /* Pad to a page boundary. */ addr = roundup(addr, PAGE_SIZE); /* Copy our environment. */ envp = addr; addr = bi_copyenv(addr); /* Pad to a page boundary. */ addr = roundup(addr, PAGE_SIZE); #if defined(LOADER_FDT_SUPPORT) /* Handle device tree blob */ dtbp = addr; dtb_size = fdt_copy(addr); /* Pad to a page boundary */ if (dtb_size) addr += roundup(dtb_size, PAGE_SIZE); #endif kfp = file_findfile(NULL, "elf kernel"); if (kfp == NULL) kfp = file_findfile(NULL, "elf64 kernel"); if (kfp == NULL) panic("can't find kernel file"); kernend = 0; /* fill it in later */ file_addmetadata(kfp, MODINFOMD_HOWTO, sizeof howto, &howto); file_addmetadata(kfp, MODINFOMD_ENVP, sizeof envp, &envp); #if defined(LOADER_FDT_SUPPORT) if (dtb_size) file_addmetadata(kfp, MODINFOMD_DTBP, sizeof dtbp, &dtbp); else printf("WARNING! Trying to fire up the kernel, but no " "device tree blob found!\n"); #endif file_addmetadata(kfp, MODINFOMD_KERNEND, sizeof kernend, &kernend); file_addmetadata(kfp, MODINFOMD_FW_HANDLE, sizeof ST, &ST); #ifdef LOADER_GELI_SUPPORT geli_export_key_metadata(kfp); #endif bi_load_efi_data(kfp); /* Figure out the size and location of the metadata. */ *modulep = addr; size = bi_copymodules(0); kernend = roundup(addr + size, PAGE_SIZE); *kernendp = kernend; /* patch MODINFOMD_KERNEND */ md = file_findmetadata(kfp, MODINFOMD_KERNEND); bcopy(&kernend, md->md_data, sizeof kernend); #if defined(__arm__) *modulep -= __elfN(relocation_offset); /* Do relocation fixup on metadata of each module. */ for (xp = file_findfile(NULL, NULL); xp != NULL; xp = xp->f_next) { for (i = 0; i < nitems(mdt); i++) { md = file_findmetadata(xp, mdt[i]); if (md) { bcopy(md->md_data, &vaddr, sizeof vaddr); vaddr -= __elfN(relocation_offset); bcopy(&vaddr, md->md_data, sizeof vaddr); } } } #endif /* Copy module list and metadata. */ (void)bi_copymodules(addr); return (0); } Index: stable/11/stand/efi/loader/copy.c =================================================================== --- stable/11/stand/efi/loader/copy.c (revision 346482) +++ stable/11/stand/efi/loader/copy.c (revision 346483) @@ -1,287 +1,298 @@ /*- * Copyright (c) 2013 The FreeBSD Foundation * All rights reserved. * * 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 __FBSDID("$FreeBSD$"); #include #include #include #include #include #include "loader_efi.h" #if defined(__i386__) || defined(__amd64__) #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); } #define KERNEL_PHYSICAL_BASE (2*1024*1024) static void efi_verify_staging_size(unsigned long *nr_pages) { UINTN sz; - EFI_MEMORY_DESCRIPTOR *map, *p; + 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; - status = BS->GetMemoryMap(&sz, 0, &key, &dsz, &dver); - if (status != EFI_BUFFER_TOO_SMALL) { - printf("Can't determine memory map size\n"); - return; - } - map = malloc(sz); - status = BS->GetMemoryMap(&sz, map, &key, &dsz, &dver); - if (EFI_ERROR(status)) { - printf("Can't read memory map\n"); - goto out; + 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 (KERNEL_PHYSICAL_BASE < start || KERNEL_PHYSICAL_BASE >= end) continue; available_pages = p->NumberOfPages - ((KERNEL_PHYSICAL_BASE - 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 /* __i386__ || __amd64__ */ #ifndef EFI_STAGING_SIZE #define EFI_STAGING_SIZE 64 #endif EFI_PHYSICAL_ADDRESS staging, staging_end; int stage_offset_set = 0; ssize_t stage_offset; int efi_copy_init(void) { EFI_STATUS status; unsigned long nr_pages; nr_pages = EFI_SIZE_TO_PAGES((EFI_STAGING_SIZE) * 1024 * 1024); #if defined(__i386__) || 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); /* * The staging area must reside in the the first 1GB physical * memory: see elf64_exec() in * boot/efi/loader/arch/amd64/elf64_freebsd.c. */ staging = 1024*1024*1024; status = BS->AllocatePages(AllocateMaxAddress, EfiLoaderData, nr_pages, &staging); #else status = BS->AllocatePages(AllocateAnyPages, EfiLoaderData, nr_pages, &staging); #endif if (EFI_ERROR(status)) { printf("failed to allocate staging area: %lu\n", EFI_ERROR_CODE(status)); return (status); } staging_end = staging + nr_pages * EFI_PAGE_SIZE; #if defined(__aarch64__) || defined(__arm__) /* * 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, 2 * 1024 * 1024); #endif return (0); } 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 = 1; } /* XXX: Callers do not check for failure. */ if (dest + stage_offset + len > staging_end) { 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(const int fd, vm_offset_t dest, const size_t len) { if (dest + stage_offset + len > staging_end) { errno = ENOMEM; return (-1); } return (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++; } Index: stable/11/stand/uboot/common/main.c =================================================================== --- stable/11/stand/uboot/common/main.c (revision 346482) +++ stable/11/stand/uboot/common/main.c (revision 346483) @@ -1,686 +1,715 @@ /*- * 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 __FBSDID("$FreeBSD$"); #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 = -1; + *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 = -1; + *type = DEV_TYP_NONE; *unit = -1; *slice = 0; *partition = -1; 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. + */ + if (*type & DEV_TYP_STOR) { + size_t len = strlen(p); + if (strcspn(p, " .") == len && strcspn(p, ":") >= len - 1 && + disk_parsedev(&dev, p, NULL) == 0) { + *unit = dev.dd.d_unit; + *slice = dev.d_slice; + *partition = dev.d_partition; + return; + } + } + /* Ignore optional spaces after the device name. */ while (*p == ' ') p++; /* Unknown device name, or a known name without unit number. */ - if ((*type == -1) || (*p == '\0')) { + if ((*type == DEV_TYP_NONE) || (*p == '\0')) { return; } /* Malformed unit number. */ if (!isdigit(*p)) { - *type = -1; + *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 = -1; + *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 = -1; + *type = DEV_TYP_NONE; *unit = -1; return; } *slice = strtoul(p, &endp, 10); /* Malformed slice number. */ if (p == endp) { - *type = -1; + *type = DEV_TYP_NONE; *unit = -1; *slice = 0; return; } p = endp; /* No partition specification. */ if (*p == '\0') return; /* Device string is malformed beyond slice number. */ if (*p != '.') { - *type = -1; + *type = DEV_TYP_NONE; *unit = -1; *slice = 0; 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 = -1; + *type = DEV_TYP_NONE; *unit = -1; *slice = 0; *partition = -1; } static void print_disk_probe_info() { char slice[32]; char partition[32]; - if (currdev.d_disk.slice > 0) - sprintf(slice, "%d", currdev.d_disk.slice); + if (currdev.d_disk.d_slice > 0) + sprintf(slice, "%d", currdev.d_disk.d_slice); else strcpy(slice, ""); - if (currdev.d_disk.partition >= 0) - sprintf(partition, "%d", currdev.d_disk.partition); + if (currdev.d_disk.d_partition >= 0) + sprintf(partition, "%d", currdev.d_disk.d_partition); else strcpy(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.slice = load_slice; - currdev.d_disk.partition = load_partition; + 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(); /* * 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 == -1 || (load_type & DEV_TYP_STOR)) && + 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 == -1 || (load_type & DEV_TYP_NET)) && + 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 = uboot_fmtdev(&currdev); 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); 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; 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 Index: stable/11/stand/uboot/lib/libuboot.h =================================================================== --- stable/11/stand/uboot/lib/libuboot.h (revision 346482) +++ stable/11/stand/uboot/lib/libuboot.h (revision 346483) @@ -1,80 +1,76 @@ /*- * Copyright (C) 2000 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. * * $FreeBSD$ */ +#include + struct uboot_devdesc { - struct devdesc dd; /* Must be first. */ union { - struct { - int slice; - int partition; - off_t offset; - } disk; - } d_kind; + struct devdesc dd; + struct disk_devdesc d_disk; + }; }; - -#define d_disk d_kind.disk /* * Default network packet alignment in memory. On arm arches packets must be * aligned to cacheline boundaries. */ #if defined(__aarch64__) #define PKTALIGN 128 #elif defined(__arm__) #define PKTALIGN 64 #else #define PKTALIGN 32 #endif int uboot_getdev(void **vdev, const char *devspec, const char **path); char *uboot_fmtdev(void *vdev); int uboot_setcurrdev(struct env_var *ev, int flags, const void *value); extern int devs_no; extern struct netif_driver uboot_net; extern struct devsw uboot_storage; extern uintptr_t uboot_heap_start; extern uintptr_t uboot_heap_end; uint64_t uboot_loadaddr(u_int type, void *data, uint64_t addr); ssize_t uboot_copyin(const void *src, vm_offset_t dest, const size_t len); ssize_t uboot_copyout(const vm_offset_t src, void *dest, const size_t len); ssize_t uboot_readin(const int fd, vm_offset_t dest, const size_t len); extern int uboot_autoload(void); struct preloaded_file; struct file_format; extern struct file_format uboot_elf; void reboot(void); int uboot_diskgetunit(int type, int type_unit); Index: stable/11/stand/userboot/userboot/elf64_freebsd.c =================================================================== --- stable/11/stand/userboot/userboot/elf64_freebsd.c (revision 346482) +++ stable/11/stand/userboot/userboot/elf64_freebsd.c (revision 346483) @@ -1,172 +1,175 @@ /*- * Copyright (c) 1998 Michael Smith * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ #include __FBSDID("$FreeBSD$"); #define __ELF_WORD_SIZE 64 #include #include #include +#ifdef DEBUG +#include +#endif #include #include #include #include #include "bootstrap.h" #include "libuserboot.h" static int elf64_exec(struct preloaded_file *amp); static int elf64_obj_exec(struct preloaded_file *amp); struct file_format amd64_elf = { elf64_loadfile, elf64_exec }; struct file_format amd64_elf_obj = { elf64_obj_loadfile, elf64_obj_exec }; #define MSR_EFER 0xc0000080 #define EFER_LME 0x00000100 #define EFER_LMA 0x00000400 /* Long mode active (R) */ #define CR4_PAE 0x00000020 #define CR4_VMXE (1UL << 13) #define CR4_PSE 0x00000010 #define CR0_PG 0x80000000 #define CR0_PE 0x00000001 /* Protected mode Enable */ #define CR0_NE 0x00000020 /* Numeric Error enable (EX16 vs IRQ13) */ #define PG_V 0x001 #define PG_RW 0x002 #define PG_U 0x004 #define PG_PS 0x080 typedef uint64_t p4_entry_t; typedef uint64_t p3_entry_t; typedef uint64_t p2_entry_t; #define GUEST_NULL_SEL 0 #define GUEST_CODE_SEL 1 #define GUEST_DATA_SEL 2 #define GUEST_GDTR_LIMIT (3 * 8 - 1) static void setup_freebsd_gdt(uint64_t *gdtr) { gdtr[GUEST_NULL_SEL] = 0; gdtr[GUEST_CODE_SEL] = 0x0020980000000000; gdtr[GUEST_DATA_SEL] = 0x0000900000000000; } /* * 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 elf64_exec(struct preloaded_file *fp) { struct file_metadata *md; Elf_Ehdr *ehdr; vm_offset_t modulep, kernend; int err; int i; uint32_t stack[1024]; p4_entry_t PT4[512]; p3_entry_t PT3[512]; p2_entry_t PT2[512]; uint64_t gdtr[3]; if ((md = file_findmetadata(fp, MODINFOMD_ELFHDR)) == NULL) return(EFTYPE); ehdr = (Elf_Ehdr *)&(md->md_data); err = bi_load64(fp->f_args, &modulep, &kernend); if (err != 0) return(err); bzero(PT4, PAGE_SIZE); bzero(PT3, PAGE_SIZE); bzero(PT2, PAGE_SIZE); /* * Build a scratch stack at physical 0x1000, page tables: * PT4 at 0x2000, * PT3 at 0x3000, * PT2 at 0x4000, * gdtr at 0x5000, */ /* * This is kinda brutal, but every single 1GB VM memory segment * points to the same first 1GB of physical memory. But it is * more than adequate. */ for (i = 0; i < 512; i++) { /* Each slot of the level 4 pages points to the same level 3 page */ PT4[i] = (p4_entry_t) 0x3000; PT4[i] |= PG_V | PG_RW | PG_U; /* Each slot of the level 3 pages points to the same level 2 page */ PT3[i] = (p3_entry_t) 0x4000; PT3[i] |= PG_V | PG_RW | PG_U; /* The level 2 page slots are mapped with 2MB pages for 1GB. */ PT2[i] = i * (2 * 1024 * 1024); PT2[i] |= PG_V | PG_RW | PG_PS | PG_U; } #ifdef DEBUG - printf("Start @ %#llx ...\n", ehdr->e_entry); + printf("Start @ %#"PRIx64" ...\n", ehdr->e_entry); #endif dev_cleanup(); stack[0] = 0; /* return address */ stack[1] = modulep; stack[2] = kernend; CALLBACK(copyin, stack, 0x1000, sizeof(stack)); CALLBACK(copyin, PT4, 0x2000, sizeof(PT4)); CALLBACK(copyin, PT3, 0x3000, sizeof(PT3)); CALLBACK(copyin, PT2, 0x4000, sizeof(PT2)); CALLBACK(setreg, 4, 0x1000); CALLBACK(setmsr, MSR_EFER, EFER_LMA | EFER_LME); CALLBACK(setcr, 4, CR4_PAE | CR4_VMXE); CALLBACK(setcr, 3, 0x2000); CALLBACK(setcr, 0, CR0_PG | CR0_PE | CR0_NE); setup_freebsd_gdt(gdtr); CALLBACK(copyin, gdtr, 0x5000, sizeof(gdtr)); CALLBACK(setgdt, 0x5000, sizeof(gdtr)); CALLBACK(exec, ehdr->e_entry); panic("exec returned"); } static int elf64_obj_exec(struct preloaded_file *fp) { return (EFTYPE); } Index: stable/11 =================================================================== --- stable/11 (revision 346482) +++ stable/11 (revision 346483) Property changes on: stable/11 ___________________________________________________________________ Modified: svn:mergeinfo ## -0,0 +0,1 ## Merged /head:r343911,344238-344241,344247,344254-344255,344260,344268,344335,344839,345066,345330