Index: head/stand/common/disk.c =================================================================== --- head/stand/common/disk.c (revision 345476) +++ head/stand/common/disk.c (revision 345477) @@ -1,443 +1,438 @@ /*- * 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 DPRINTF(fmt, args...) printf("%s: " fmt "\n" , __func__ , ## args) #else # 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, "%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)); } 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; 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; + dev.d_partition = D_PARTNONE; 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); } 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) { 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; + partdev.d_slice = D_SLICENONE; + partdev.d_partition = D_PARTNONE; dev->d_offset = 0; table = NULL; slice = dev->d_slice; partition = dev->d_partition; 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) { 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) { 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); 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; 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; 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) { + if (dev->d_slice > D_SLICENONE) { #ifdef LOADER_GPT_SUPPORT - if (dev->d_partition == 255) { + if (dev->d_partition == D_PARTISGPT) { 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) + if (dev->d_partition > D_PARTNONE) 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; + unit = -1; + slice = D_SLICEWILD; + partition = D_PARTWILD; 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: head/stand/common/disk.h =================================================================== --- head/stand/common/disk.h (revision 345476) +++ head/stand/common/disk.h (revision 345477) @@ -1,113 +1,122 @@ /*- * Copyright (c) 2011 Google, Inc. * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * * $FreeBSD$ */ /* * Device descriptor for partitioned disks. To use, set the * d_slice and d_partition variables as follows: * * Whole disk access: * - * d_slice = -1 - * d_partition = -1 + * d_slice = D_SLICENONE + * d_partition = * * Whole MBR slice: * * d_slice = MBR slice number (typically 1..4) - * d_partition = -1 + * d_partition = D_PARTNONE * * BSD disklabel partition within an MBR slice: * * d_slice = MBR slice number (typically 1..4) - * d_partition = disklabel partition (typically 0..19) + * d_partition = disklabel partition (typically 0..19 or D_PARTWILD) * * BSD disklabel partition on the true dedicated disk: * - * d_slice = -1 - * d_partition = disklabel partition (typically 0..19) + * d_slice = D_SLICENONE + * d_partition = disklabel partition (typically 0..19 or D_PARTWILD) * * GPT partition: * * d_slice = GPT partition number (typically 1..N) - * d_partition = 255 + * d_partition = D_PARTISGPT * - * For both MBR and GPT, to automatically find the 'best' slice or partition, - * set d_slice to zero. This uses the partition type to decide which partition - * to use according to the following list of preferences: + * For MBR, setting d_partition to D_PARTWILD will automatically use the first + * partition within the slice. * + * For both MBR and GPT, to automatically find the 'best' slice and partition, + * set d_slice to D_SLICEWILD. This uses the partition type to decide which + * partition to use according to the following list of preferences: + * * FreeBSD (active) * FreeBSD (inactive) * Linux (active) * Linux (inactive) * DOS/Windows (active) * DOS/Windows (inactive) * * Active MBR slices (marked as bootable) are preferred over inactive. GPT * doesn't have the concept of active/inactive partitions. In both MBR and GPT, * if there are multiple slices/partitions of a given type, the first one * is chosen. * * The low-level disk device will typically call disk_open() from its open * method to interpret the disk partition tables according to the rules above. * This will initialize d_offset to the block offset of the start of the * selected partition - this offset should be added to the offset passed to * the device's strategy method. */ #ifndef _DISK_H #define _DISK_H + +#define D_SLICENONE -1 +#define D_SLICEWILD 0 +#define D_PARTNONE -1 +#define D_PARTWILD -2 +#define D_PARTISGPT 255 struct disk_devdesc { struct devdesc dd; /* Must be first. */ int d_slice; int d_partition; uint64_t d_offset; }; enum disk_ioctl { IOCTL_GET_BLOCKS, IOCTL_GET_BLOCK_SIZE }; /* * Parse disk metadata and initialise dev->d_offset. */ extern int disk_open(struct disk_devdesc *, uint64_t, u_int); extern int disk_close(struct disk_devdesc *); extern int disk_ioctl(struct disk_devdesc *, u_long, void *); extern int disk_read(struct disk_devdesc *, void *, uint64_t, u_int); extern int disk_write(struct disk_devdesc *, void *, uint64_t, u_int); extern int ptblread(void *, void *, size_t, uint64_t); /* * Print information about slices on a disk. */ extern int disk_print(struct disk_devdesc *, char *, int); extern char* disk_fmtdev(struct disk_devdesc *); extern int disk_parsedev(struct disk_devdesc *, const char *, const char **); #endif /* _DISK_H */ Index: head/stand/efi/libefi/efipart.c =================================================================== --- head/stand/efi/libefi/efipart.c (revision 345476) +++ head/stand/efi/libefi/efipart.c (revision 345477) @@ -1,1141 +1,1141 @@ /*- * Copyright (c) 2010 Marcel Moolenaar * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ #include __FBSDID("$FreeBSD$"); #include #include #include #include #include #include #include #include #include #include #include #include static EFI_GUID blkio_guid = BLOCK_IO_PROTOCOL; static int efipart_initfd(void); static int efipart_initcd(void); static int efipart_inithd(void); static int efipart_strategy(void *, int, daddr_t, size_t, char *, size_t *); static int efipart_realstrategy(void *, int, daddr_t, size_t, char *, size_t *); static int efipart_open(struct open_file *, ...); static int efipart_close(struct open_file *); static int efipart_ioctl(struct open_file *, u_long, void *); static int efipart_printfd(int); static int efipart_printcd(int); static int efipart_printhd(int); /* EISA PNP ID's for floppy controllers */ #define PNP0604 0x604 #define PNP0700 0x700 #define PNP0701 0x701 struct devsw efipart_fddev = { .dv_name = "fd", .dv_type = DEVT_FD, .dv_init = efipart_initfd, .dv_strategy = efipart_strategy, .dv_open = efipart_open, .dv_close = efipart_close, .dv_ioctl = efipart_ioctl, .dv_print = efipart_printfd, .dv_cleanup = NULL }; struct devsw efipart_cddev = { .dv_name = "cd", .dv_type = DEVT_CD, .dv_init = efipart_initcd, .dv_strategy = efipart_strategy, .dv_open = efipart_open, .dv_close = efipart_close, .dv_ioctl = efipart_ioctl, .dv_print = efipart_printcd, .dv_cleanup = NULL }; struct devsw efipart_hddev = { .dv_name = "disk", .dv_type = DEVT_DISK, .dv_init = efipart_inithd, .dv_strategy = efipart_strategy, .dv_open = efipart_open, .dv_close = efipart_close, .dv_ioctl = efipart_ioctl, .dv_print = efipart_printhd, .dv_cleanup = NULL }; static pdinfo_list_t fdinfo; static pdinfo_list_t cdinfo; static pdinfo_list_t hdinfo; static EFI_HANDLE *efipart_handles = NULL; static UINTN efipart_nhandles = 0; pdinfo_list_t * efiblk_get_pdinfo_list(struct devsw *dev) { if (dev->dv_type == DEVT_DISK) return (&hdinfo); if (dev->dv_type == DEVT_CD) return (&cdinfo); if (dev->dv_type == DEVT_FD) return (&fdinfo); return (NULL); } /* XXX this gets called way way too often, investigate */ pdinfo_t * efiblk_get_pdinfo(struct devdesc *dev) { pdinfo_list_t *pdi; pdinfo_t *pd = NULL; pdi = efiblk_get_pdinfo_list(dev->d_dev); if (pdi == NULL) return (pd); STAILQ_FOREACH(pd, pdi, pd_link) { if (pd->pd_unit == dev->d_unit) return (pd); } return (pd); } pdinfo_t * efiblk_get_pdinfo_by_device_path(EFI_DEVICE_PATH *path) { unsigned i; EFI_DEVICE_PATH *media, *devpath; EFI_HANDLE h; media = efi_devpath_to_media_path(path); if (media == NULL) return (NULL); for (i = 0; i < efipart_nhandles; i++) { h = efipart_handles[i]; devpath = efi_lookup_devpath(h); if (devpath == NULL) continue; if (!efi_devpath_match_node(media, efi_devpath_to_media_path(devpath))) continue; return (efiblk_get_pdinfo_by_handle(h)); } return (NULL); } static bool same_handle(pdinfo_t *pd, EFI_HANDLE h) { return (pd->pd_handle == h || pd->pd_alias == h); } pdinfo_t * efiblk_get_pdinfo_by_handle(EFI_HANDLE h) { pdinfo_t *dp, *pp; /* * Check hard disks, then cd, then floppy */ STAILQ_FOREACH(dp, &hdinfo, pd_link) { if (same_handle(dp, h)) return (dp); STAILQ_FOREACH(pp, &dp->pd_part, pd_link) { if (same_handle(pp, h)) return (pp); } } STAILQ_FOREACH(dp, &cdinfo, pd_link) { if (same_handle(dp, h)) return (dp); } STAILQ_FOREACH(dp, &fdinfo, pd_link) { if (same_handle(dp, h)) return (dp); } return (NULL); } static int efiblk_pdinfo_count(pdinfo_list_t *pdi) { pdinfo_t *pd; int i = 0; STAILQ_FOREACH(pd, pdi, pd_link) { i++; } return (i); } int efipart_inithandles(void) { UINTN sz; EFI_HANDLE *hin; EFI_STATUS status; if (efipart_nhandles != 0) { free(efipart_handles); efipart_handles = NULL; efipart_nhandles = 0; } sz = 0; hin = NULL; status = BS->LocateHandle(ByProtocol, &blkio_guid, 0, &sz, hin); if (status == EFI_BUFFER_TOO_SMALL) { hin = malloc(sz); status = BS->LocateHandle(ByProtocol, &blkio_guid, 0, &sz, hin); if (EFI_ERROR(status)) free(hin); } if (EFI_ERROR(status)) return (efi_status_to_errno(status)); efipart_handles = hin; efipart_nhandles = sz / sizeof(*hin); #ifdef EFIPART_DEBUG printf("%s: Got %d BLOCK IO MEDIA handle(s)\n", __func__, efipart_nhandles); #endif return (0); } static ACPI_HID_DEVICE_PATH * efipart_floppy(EFI_DEVICE_PATH *node) { ACPI_HID_DEVICE_PATH *acpi; if (DevicePathType(node) == ACPI_DEVICE_PATH && DevicePathSubType(node) == ACPI_DP) { acpi = (ACPI_HID_DEVICE_PATH *) node; if (acpi->HID == EISA_PNP_ID(PNP0604) || acpi->HID == EISA_PNP_ID(PNP0700) || acpi->HID == EISA_PNP_ID(PNP0701)) { return (acpi); } } return (NULL); } /* * Determine if the provided device path is hdd. * * There really is no simple fool proof way to classify the devices. * Since we do build three lists of devices - floppy, cd and hdd, we * will try to see if the device is floppy or cd, and list anything else * as hdd. */ static bool efipart_hdd(EFI_DEVICE_PATH *dp) { unsigned i; EFI_DEVICE_PATH *devpath, *node; EFI_BLOCK_IO *blkio; EFI_STATUS status; if (dp == NULL) return (false); if ((node = efi_devpath_last_node(dp)) == NULL) return (false); if (efipart_floppy(node) != NULL) return (false); /* * Test every EFI BLOCK IO handle to make sure dp is not device path * for CD/DVD. */ for (i = 0; i < efipart_nhandles; i++) { devpath = efi_lookup_devpath(efipart_handles[i]); if (devpath == NULL) return (false); /* Only continue testing when dp is prefix in devpath. */ if (!efi_devpath_is_prefix(dp, devpath)) continue; /* * The device path has to have last node describing the * device, or we can not test the type. */ if ((node = efi_devpath_last_node(devpath)) == NULL) return (false); if (DevicePathType(node) == MEDIA_DEVICE_PATH && DevicePathSubType(node) == MEDIA_CDROM_DP) { return (false); } /* Make sure we do have the media. */ status = BS->HandleProtocol(efipart_handles[i], &blkio_guid, (void **)&blkio); if (EFI_ERROR(status)) return (false); /* USB or SATA cd without the media. */ if (blkio->Media->RemovableMedia && !blkio->Media->MediaPresent) { return (false); } /* * We assume the block size 512 or greater power of 2. * iPXE is known to insert stub BLOCK IO device with * BlockSize 1. */ if (blkio->Media->BlockSize < 512 || !powerof2(blkio->Media->BlockSize)) { return (false); } } return (true); } /* * Add or update entries with new handle data. */ static int efipart_fdinfo_add(EFI_HANDLE handle, uint32_t uid, EFI_DEVICE_PATH *devpath) { pdinfo_t *fd; fd = calloc(1, sizeof(pdinfo_t)); if (fd == NULL) { printf("Failed to register floppy %d, out of memory\n", uid); return (ENOMEM); } STAILQ_INIT(&fd->pd_part); fd->pd_unit = uid; fd->pd_handle = handle; fd->pd_devpath = devpath; fd->pd_parent = NULL; fd->pd_devsw = &efipart_fddev; STAILQ_INSERT_TAIL(&fdinfo, fd, pd_link); return (0); } static void efipart_updatefd(void) { EFI_DEVICE_PATH *devpath, *node; ACPI_HID_DEVICE_PATH *acpi; int i; for (i = 0; i < efipart_nhandles; i++) { devpath = efi_lookup_devpath(efipart_handles[i]); if (devpath == NULL) continue; if ((node = efi_devpath_last_node(devpath)) == NULL) continue; if ((acpi = efipart_floppy(node)) != NULL) { efipart_fdinfo_add(efipart_handles[i], acpi->UID, devpath); } } } static int efipart_initfd(void) { STAILQ_INIT(&fdinfo); efipart_updatefd(); bcache_add_dev(efiblk_pdinfo_count(&fdinfo)); return (0); } /* * Add or update entries with new handle data. */ static int efipart_cdinfo_add(EFI_HANDLE handle, EFI_HANDLE alias, EFI_DEVICE_PATH *devpath) { int unit; pdinfo_t *cd; pdinfo_t *pd; unit = 0; STAILQ_FOREACH(pd, &cdinfo, pd_link) { if (efi_devpath_match(pd->pd_devpath, devpath) == true) { pd->pd_handle = handle; pd->pd_alias = alias; return (0); } unit++; } cd = calloc(1, sizeof(pdinfo_t)); if (cd == NULL) { printf("Failed to add cd %d, out of memory\n", unit); return (ENOMEM); } STAILQ_INIT(&cd->pd_part); cd->pd_handle = handle; cd->pd_unit = unit; cd->pd_alias = alias; cd->pd_devpath = devpath; cd->pd_parent = NULL; cd->pd_devsw = &efipart_cddev; STAILQ_INSERT_TAIL(&cdinfo, cd, pd_link); return (0); } static void efipart_updatecd(void) { int i; EFI_DEVICE_PATH *devpath, *devpathcpy, *tmpdevpath, *node; EFI_HANDLE handle; EFI_BLOCK_IO *blkio; EFI_STATUS status; for (i = 0; i < efipart_nhandles; i++) { devpath = efi_lookup_devpath(efipart_handles[i]); if (devpath == NULL) continue; if ((node = efi_devpath_last_node(devpath)) == NULL) continue; if (efipart_floppy(node) != NULL) continue; if (efipart_hdd(devpath)) continue; status = BS->HandleProtocol(efipart_handles[i], &blkio_guid, (void **)&blkio); if (EFI_ERROR(status)) continue; /* * If we come across a logical partition of subtype CDROM * it doesn't refer to the CD filesystem itself, but rather * to any usable El Torito boot image on it. In this case * we try to find the parent device and add that instead as * that will be the CD filesystem. */ if (DevicePathType(node) == MEDIA_DEVICE_PATH && DevicePathSubType(node) == MEDIA_CDROM_DP) { devpathcpy = efi_devpath_trim(devpath); if (devpathcpy == NULL) continue; tmpdevpath = devpathcpy; status = BS->LocateDevicePath(&blkio_guid, &tmpdevpath, &handle); free(devpathcpy); if (EFI_ERROR(status)) continue; devpath = efi_lookup_devpath(handle); efipart_cdinfo_add(handle, efipart_handles[i], devpath); continue; } if (DevicePathType(node) == MESSAGING_DEVICE_PATH && DevicePathSubType(node) == MSG_ATAPI_DP) { efipart_cdinfo_add(efipart_handles[i], NULL, devpath); continue; } /* USB or SATA cd without the media. */ if (blkio->Media->RemovableMedia && !blkio->Media->MediaPresent) { efipart_cdinfo_add(efipart_handles[i], NULL, devpath); } } } static int efipart_initcd(void) { STAILQ_INIT(&cdinfo); efipart_updatecd(); bcache_add_dev(efiblk_pdinfo_count(&cdinfo)); return (0); } static int efipart_hdinfo_add(EFI_HANDLE disk_handle, EFI_HANDLE part_handle) { EFI_DEVICE_PATH *disk_devpath, *part_devpath; HARDDRIVE_DEVICE_PATH *node; int unit; pdinfo_t *hd, *pd, *last; disk_devpath = efi_lookup_devpath(disk_handle); if (disk_devpath == NULL) return (ENOENT); if (part_handle != NULL) { part_devpath = efi_lookup_devpath(part_handle); if (part_devpath == NULL) return (ENOENT); node = (HARDDRIVE_DEVICE_PATH *) efi_devpath_last_node(part_devpath); if (node == NULL) return (ENOENT); /* This should not happen. */ } else { part_devpath = NULL; node = NULL; } pd = calloc(1, sizeof(pdinfo_t)); if (pd == NULL) { printf("Failed to add disk, out of memory\n"); return (ENOMEM); } STAILQ_INIT(&pd->pd_part); STAILQ_FOREACH(hd, &hdinfo, pd_link) { if (efi_devpath_match(hd->pd_devpath, disk_devpath) == true) { if (part_devpath == NULL) return (0); /* Add the partition. */ pd->pd_handle = part_handle; pd->pd_unit = node->PartitionNumber; pd->pd_devpath = part_devpath; pd->pd_parent = hd; pd->pd_devsw = &efipart_hddev; STAILQ_INSERT_TAIL(&hd->pd_part, pd, pd_link); return (0); } } last = STAILQ_LAST(&hdinfo, pdinfo, pd_link); if (last != NULL) unit = last->pd_unit + 1; else unit = 0; /* Add the disk. */ hd = pd; hd->pd_handle = disk_handle; hd->pd_unit = unit; hd->pd_devpath = disk_devpath; hd->pd_parent = NULL; hd->pd_devsw = &efipart_hddev; STAILQ_INSERT_TAIL(&hdinfo, hd, pd_link); if (part_devpath == NULL) return (0); pd = calloc(1, sizeof(pdinfo_t)); if (pd == NULL) { printf("Failed to add partition, out of memory\n"); return (ENOMEM); } STAILQ_INIT(&pd->pd_part); /* Add the partition. */ pd->pd_handle = part_handle; pd->pd_unit = node->PartitionNumber; pd->pd_devpath = part_devpath; pd->pd_parent = hd; pd->pd_devsw = &efipart_hddev; STAILQ_INSERT_TAIL(&hd->pd_part, pd, pd_link); return (0); } /* * The MEDIA_FILEPATH_DP has device name. * From U-Boot sources it looks like names are in the form * of typeN:M, where type is interface type, N is disk id * and M is partition id. */ static int efipart_hdinfo_add_filepath(EFI_HANDLE disk_handle) { EFI_DEVICE_PATH *devpath; FILEPATH_DEVICE_PATH *node; char *pathname, *p; int unit, len; pdinfo_t *pd, *last; /* First collect and verify all the data */ if ((devpath = efi_lookup_devpath(disk_handle)) == NULL) return (ENOENT); node = (FILEPATH_DEVICE_PATH *)efi_devpath_last_node(devpath); if (node == NULL) return (ENOENT); /* This should not happen. */ pd = calloc(1, sizeof(pdinfo_t)); if (pd == NULL) { printf("Failed to add disk, out of memory\n"); return (ENOMEM); } STAILQ_INIT(&pd->pd_part); last = STAILQ_LAST(&hdinfo, pdinfo, pd_link); if (last != NULL) unit = last->pd_unit + 1; else unit = 0; /* FILEPATH_DEVICE_PATH has 0 terminated string */ len = ucs2len(node->PathName); if ((pathname = malloc(len + 1)) == NULL) { printf("Failed to add disk, out of memory\n"); free(pd); return (ENOMEM); } cpy16to8(node->PathName, pathname, len + 1); p = strchr(pathname, ':'); /* * Assume we are receiving handles in order, first disk handle, * then partitions for this disk. If this assumption proves * false, this code would need update. */ if (p == NULL) { /* no colon, add the disk */ pd->pd_handle = disk_handle; pd->pd_unit = unit; pd->pd_devpath = devpath; pd->pd_parent = NULL; pd->pd_devsw = &efipart_hddev; STAILQ_INSERT_TAIL(&hdinfo, pd, pd_link); free(pathname); return (0); } p++; /* skip the colon */ errno = 0; unit = (int)strtol(p, NULL, 0); if (errno != 0) { printf("Bad unit number for partition \"%s\"\n", pathname); free(pathname); free(pd); return (EUNIT); } /* * We should have disk registered, if not, we are receiving * handles out of order, and this code should be reworked * to create "blank" disk for partition, and to find the * disk based on PathName compares. */ if (last == NULL) { printf("BUG: No disk for partition \"%s\"\n", pathname); free(pathname); free(pd); return (EINVAL); } /* Add the partition. */ pd->pd_handle = disk_handle; pd->pd_unit = unit; pd->pd_devpath = devpath; pd->pd_parent = last; pd->pd_devsw = &efipart_hddev; STAILQ_INSERT_TAIL(&last->pd_part, pd, pd_link); free(pathname); return (0); } static void efipart_updatehd(void) { int i; EFI_DEVICE_PATH *devpath, *devpathcpy, *tmpdevpath, *node; EFI_HANDLE handle; EFI_BLOCK_IO *blkio; EFI_STATUS status; for (i = 0; i < efipart_nhandles; i++) { devpath = efi_lookup_devpath(efipart_handles[i]); if (devpath == NULL) continue; if ((node = efi_devpath_last_node(devpath)) == NULL) continue; if (!efipart_hdd(devpath)) continue; status = BS->HandleProtocol(efipart_handles[i], &blkio_guid, (void **)&blkio); if (EFI_ERROR(status)) continue; if (DevicePathType(node) == MEDIA_DEVICE_PATH && DevicePathSubType(node) == MEDIA_FILEPATH_DP) { efipart_hdinfo_add_filepath(efipart_handles[i]); continue; } if (DevicePathType(node) == MEDIA_DEVICE_PATH && DevicePathSubType(node) == MEDIA_HARDDRIVE_DP) { devpathcpy = efi_devpath_trim(devpath); if (devpathcpy == NULL) continue; tmpdevpath = devpathcpy; status = BS->LocateDevicePath(&blkio_guid, &tmpdevpath, &handle); free(devpathcpy); if (EFI_ERROR(status)) continue; /* * We do not support nested partitions. */ devpathcpy = efi_lookup_devpath(handle); if (devpathcpy == NULL) continue; if ((node = efi_devpath_last_node(devpathcpy)) == NULL) continue; if (DevicePathType(node) == MEDIA_DEVICE_PATH && DevicePathSubType(node) == MEDIA_HARDDRIVE_DP) continue; efipart_hdinfo_add(handle, efipart_handles[i]); continue; } efipart_hdinfo_add(efipart_handles[i], NULL); } } static int efipart_inithd(void) { STAILQ_INIT(&hdinfo); efipart_updatehd(); bcache_add_dev(efiblk_pdinfo_count(&hdinfo)); return (0); } static int efipart_print_common(struct devsw *dev, pdinfo_list_t *pdlist, int verbose) { int ret = 0; EFI_BLOCK_IO *blkio; EFI_STATUS status; EFI_HANDLE h; pdinfo_t *pd; CHAR16 *text; struct disk_devdesc pd_dev; char line[80]; if (STAILQ_EMPTY(pdlist)) return (0); printf("%s devices:", dev->dv_name); if ((ret = pager_output("\n")) != 0) return (ret); STAILQ_FOREACH(pd, pdlist, pd_link) { h = pd->pd_handle; if (verbose) { /* Output the device path. */ text = efi_devpath_name(efi_lookup_devpath(h)); if (text != NULL) { printf(" %S", text); efi_free_devpath_name(text); if ((ret = pager_output("\n")) != 0) break; } } snprintf(line, sizeof(line), " %s%d", dev->dv_name, pd->pd_unit); printf("%s:", line); status = BS->HandleProtocol(h, &blkio_guid, (void **)&blkio); if (!EFI_ERROR(status)) { printf(" %llu", blkio->Media->LastBlock == 0? 0: (unsigned long long) (blkio->Media->LastBlock + 1)); if (blkio->Media->LastBlock != 0) { printf(" X %u", blkio->Media->BlockSize); } printf(" blocks"); if (blkio->Media->MediaPresent) { if (blkio->Media->RemovableMedia) printf(" (removable)"); } else { printf(" (no media)"); } if ((ret = pager_output("\n")) != 0) break; if (!blkio->Media->MediaPresent) continue; pd->pd_blkio = blkio; pd_dev.dd.d_dev = dev; pd_dev.dd.d_unit = pd->pd_unit; - pd_dev.d_slice = -1; - pd_dev.d_partition = -1; + pd_dev.d_slice = D_SLICENONE; + pd_dev.d_partition = D_PARTNONE; ret = disk_open(&pd_dev, blkio->Media->BlockSize * (blkio->Media->LastBlock + 1), blkio->Media->BlockSize); if (ret == 0) { ret = disk_print(&pd_dev, line, verbose); disk_close(&pd_dev); if (ret != 0) return (ret); } else { /* Do not fail from disk_open() */ ret = 0; } } else { if ((ret = pager_output("\n")) != 0) break; } } return (ret); } static int efipart_printfd(int verbose) { return (efipart_print_common(&efipart_fddev, &fdinfo, verbose)); } static int efipart_printcd(int verbose) { return (efipart_print_common(&efipart_cddev, &cdinfo, verbose)); } static int efipart_printhd(int verbose) { return (efipart_print_common(&efipart_hddev, &hdinfo, verbose)); } static int efipart_open(struct open_file *f, ...) { va_list args; struct disk_devdesc *dev; pdinfo_t *pd; EFI_BLOCK_IO *blkio; EFI_STATUS status; va_start(args, f); dev = va_arg(args, struct disk_devdesc*); va_end(args); if (dev == NULL) return (EINVAL); pd = efiblk_get_pdinfo((struct devdesc *)dev); if (pd == NULL) return (EIO); if (pd->pd_blkio == NULL) { status = BS->HandleProtocol(pd->pd_handle, &blkio_guid, (void **)&pd->pd_blkio); if (EFI_ERROR(status)) return (efi_status_to_errno(status)); } blkio = pd->pd_blkio; if (!blkio->Media->MediaPresent) return (EAGAIN); pd->pd_open++; if (pd->pd_bcache == NULL) pd->pd_bcache = bcache_allocate(); if (dev->dd.d_dev->dv_type == DEVT_DISK) { int rc; rc = disk_open(dev, blkio->Media->BlockSize * (blkio->Media->LastBlock + 1), blkio->Media->BlockSize); if (rc != 0) { pd->pd_open--; if (pd->pd_open == 0) { pd->pd_blkio = NULL; bcache_free(pd->pd_bcache); pd->pd_bcache = NULL; } } return (rc); } return (0); } static int efipart_close(struct open_file *f) { struct disk_devdesc *dev; pdinfo_t *pd; dev = (struct disk_devdesc *)(f->f_devdata); if (dev == NULL) return (EINVAL); pd = efiblk_get_pdinfo((struct devdesc *)dev); if (pd == NULL) return (EINVAL); pd->pd_open--; if (pd->pd_open == 0) { pd->pd_blkio = NULL; bcache_free(pd->pd_bcache); pd->pd_bcache = NULL; } if (dev->dd.d_dev->dv_type == DEVT_DISK) return (disk_close(dev)); return (0); } static int efipart_ioctl(struct open_file *f, u_long cmd, void *data) { struct disk_devdesc *dev; pdinfo_t *pd; int rc; dev = (struct disk_devdesc *)(f->f_devdata); if (dev == NULL) return (EINVAL); pd = efiblk_get_pdinfo((struct devdesc *)dev); if (pd == NULL) return (EINVAL); if (dev->dd.d_dev->dv_type == DEVT_DISK) { rc = disk_ioctl(dev, cmd, data); if (rc != ENOTTY) return (rc); } switch (cmd) { case DIOCGSECTORSIZE: *(u_int *)data = pd->pd_blkio->Media->BlockSize; break; case DIOCGMEDIASIZE: *(uint64_t *)data = pd->pd_blkio->Media->BlockSize * (pd->pd_blkio->Media->LastBlock + 1); break; default: return (ENOTTY); } return (0); } /* * efipart_readwrite() * Internal equivalent of efipart_strategy(), which operates on the * media-native block size. This function expects all I/O requests * to be within the media size and returns an error if such is not * the case. */ static int efipart_readwrite(EFI_BLOCK_IO *blkio, int rw, daddr_t blk, daddr_t nblks, char *buf) { EFI_STATUS status; if (blkio == NULL) return (ENXIO); if (blk < 0 || blk > blkio->Media->LastBlock) return (EIO); if ((blk + nblks - 1) > blkio->Media->LastBlock) return (EIO); switch (rw & F_MASK) { case F_READ: status = blkio->ReadBlocks(blkio, blkio->Media->MediaId, blk, nblks * blkio->Media->BlockSize, buf); break; case F_WRITE: if (blkio->Media->ReadOnly) return (EROFS); status = blkio->WriteBlocks(blkio, blkio->Media->MediaId, blk, nblks * blkio->Media->BlockSize, buf); break; default: return (ENOSYS); } if (EFI_ERROR(status)) { printf("%s: rw=%d, blk=%ju size=%ju status=%lu\n", __func__, rw, blk, nblks, EFI_ERROR_CODE(status)); } return (efi_status_to_errno(status)); } static int efipart_strategy(void *devdata, int rw, daddr_t blk, size_t size, char *buf, size_t *rsize) { struct bcache_devdata bcd; struct disk_devdesc *dev; pdinfo_t *pd; dev = (struct disk_devdesc *)devdata; if (dev == NULL) return (EINVAL); pd = efiblk_get_pdinfo((struct devdesc *)dev); if (pd == NULL) return (EINVAL); if (pd->pd_blkio->Media->RemovableMedia && !pd->pd_blkio->Media->MediaPresent) return (ENXIO); bcd.dv_strategy = efipart_realstrategy; bcd.dv_devdata = devdata; bcd.dv_cache = pd->pd_bcache; if (dev->dd.d_dev->dv_type == DEVT_DISK) { daddr_t offset; offset = dev->d_offset * pd->pd_blkio->Media->BlockSize; offset /= 512; return (bcache_strategy(&bcd, rw, blk + offset, size, buf, rsize)); } return (bcache_strategy(&bcd, rw, blk, size, buf, rsize)); } static int efipart_realstrategy(void *devdata, int rw, daddr_t blk, size_t size, char *buf, size_t *rsize) { struct disk_devdesc *dev = (struct disk_devdesc *)devdata; pdinfo_t *pd; EFI_BLOCK_IO *blkio; uint64_t off, disk_blocks, d_offset = 0; char *blkbuf; size_t blkoff, blksz; int error; size_t diskend, readstart; if (dev == NULL || blk < 0) return (EINVAL); pd = efiblk_get_pdinfo((struct devdesc *)dev); if (pd == NULL) return (EINVAL); blkio = pd->pd_blkio; if (blkio == NULL) return (ENXIO); if (size == 0 || (size % 512) != 0) return (EIO); off = blk * 512; /* * Get disk blocks, this value is either for whole disk or for * partition. */ disk_blocks = 0; if (dev->dd.d_dev->dv_type == DEVT_DISK) { if (disk_ioctl(dev, DIOCGMEDIASIZE, &disk_blocks) == 0) { /* DIOCGMEDIASIZE does return bytes. */ disk_blocks /= blkio->Media->BlockSize; } d_offset = dev->d_offset; } if (disk_blocks == 0) disk_blocks = blkio->Media->LastBlock + 1 - d_offset; /* make sure we don't read past disk end */ if ((off + size) / blkio->Media->BlockSize > d_offset + disk_blocks) { diskend = d_offset + disk_blocks; readstart = off / blkio->Media->BlockSize; if (diskend <= readstart) { if (rsize != NULL) *rsize = 0; return (EIO); } size = diskend - readstart; size = size * blkio->Media->BlockSize; } if (rsize != NULL) *rsize = size; if ((size % blkio->Media->BlockSize == 0) && (off % blkio->Media->BlockSize == 0)) return (efipart_readwrite(blkio, rw, off / blkio->Media->BlockSize, size / blkio->Media->BlockSize, buf)); /* * The block size of the media is not a multiple of I/O. */ blkbuf = malloc(blkio->Media->BlockSize); if (blkbuf == NULL) return (ENOMEM); error = 0; blk = off / blkio->Media->BlockSize; blkoff = off % blkio->Media->BlockSize; blksz = blkio->Media->BlockSize - blkoff; while (size > 0) { error = efipart_readwrite(blkio, rw, blk, 1, blkbuf); if (error) break; if (size < blksz) blksz = size; bcopy(blkbuf + blkoff, buf, blksz); buf += blksz; size -= blksz; blk++; blkoff = 0; blksz = blkio->Media->BlockSize; } free(blkbuf); return (error); } Index: head/stand/efi/loader/main.c =================================================================== --- head/stand/efi/loader/main.c (revision 345476) +++ head/stand/efi/loader/main.c (revision 345477) @@ -1,1426 +1,1426 @@ /*- * Copyright (c) 2008-2010 Rui Paulo * Copyright (c) 2006 Marcel Moolenaar * All rights reserved. * * Copyright (c) 2018 Netflix, Inc. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include __FBSDID("$FreeBSD$"); #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #ifdef EFI_ZFS_BOOT #include #include "efizfs.h" #endif #include "loader_efi.h" struct arch_switch archsw; /* MI/MD interface boundary */ EFI_GUID acpi = ACPI_TABLE_GUID; EFI_GUID acpi20 = ACPI_20_TABLE_GUID; EFI_GUID devid = DEVICE_PATH_PROTOCOL; EFI_GUID imgid = LOADED_IMAGE_PROTOCOL; EFI_GUID mps = MPS_TABLE_GUID; EFI_GUID netid = EFI_SIMPLE_NETWORK_PROTOCOL; EFI_GUID smbios = SMBIOS_TABLE_GUID; EFI_GUID smbios3 = SMBIOS3_TABLE_GUID; EFI_GUID dxe = DXE_SERVICES_TABLE_GUID; EFI_GUID hoblist = HOB_LIST_TABLE_GUID; EFI_GUID lzmadecomp = LZMA_DECOMPRESSION_GUID; EFI_GUID mpcore = ARM_MP_CORE_INFO_TABLE_GUID; EFI_GUID esrt = ESRT_TABLE_GUID; EFI_GUID memtype = MEMORY_TYPE_INFORMATION_TABLE_GUID; EFI_GUID debugimg = DEBUG_IMAGE_INFO_TABLE_GUID; EFI_GUID fdtdtb = FDT_TABLE_GUID; EFI_GUID inputid = SIMPLE_TEXT_INPUT_PROTOCOL; /* * Number of seconds to wait for a keystroke before exiting with failure * in the event no currdev is found. -2 means always break, -1 means * never break, 0 means poll once and then reboot, > 0 means wait for * that many seconds. "fail_timeout" can be set in the environment as * well. */ static int fail_timeout = 5; /* * Current boot variable */ UINT16 boot_current; static bool has_keyboard(void) { EFI_STATUS status; EFI_DEVICE_PATH *path; EFI_HANDLE *hin, *hin_end, *walker; UINTN sz; bool retval = false; /* * Find all the handles that support the SIMPLE_TEXT_INPUT_PROTOCOL and * do the typical dance to get the right sized buffer. */ sz = 0; hin = NULL; status = BS->LocateHandle(ByProtocol, &inputid, 0, &sz, 0); if (status == EFI_BUFFER_TOO_SMALL) { hin = (EFI_HANDLE *)malloc(sz); status = BS->LocateHandle(ByProtocol, &inputid, 0, &sz, hin); if (EFI_ERROR(status)) free(hin); } if (EFI_ERROR(status)) return retval; /* * Look at each of the handles. If it supports the device path protocol, * use it to get the device path for this handle. Then see if that * device path matches either the USB device path for keyboards or the * legacy device path for keyboards. */ hin_end = &hin[sz / sizeof(*hin)]; for (walker = hin; walker < hin_end; walker++) { status = BS->HandleProtocol(*walker, &devid, (VOID **)&path); if (EFI_ERROR(status)) continue; while (!IsDevicePathEnd(path)) { /* * Check for the ACPI keyboard node. All PNP3xx nodes * are keyboards of different flavors. Note: It is * unclear of there's always a keyboard node when * there's a keyboard controller, or if there's only one * when a keyboard is detected at boot. */ if (DevicePathType(path) == ACPI_DEVICE_PATH && (DevicePathSubType(path) == ACPI_DP || DevicePathSubType(path) == ACPI_EXTENDED_DP)) { ACPI_HID_DEVICE_PATH *acpi; acpi = (ACPI_HID_DEVICE_PATH *)(void *)path; if ((EISA_ID_TO_NUM(acpi->HID) & 0xff00) == 0x300 && (acpi->HID & 0xffff) == PNP_EISA_ID_CONST) { retval = true; goto out; } /* * Check for USB keyboard node, if present. Unlike a * PS/2 keyboard, these definitely only appear when * connected to the system. */ } else if (DevicePathType(path) == MESSAGING_DEVICE_PATH && DevicePathSubType(path) == MSG_USB_CLASS_DP) { USB_CLASS_DEVICE_PATH *usb; usb = (USB_CLASS_DEVICE_PATH *)(void *)path; if (usb->DeviceClass == 3 && /* HID */ usb->DeviceSubClass == 1 && /* Boot devices */ usb->DeviceProtocol == 1) { /* Boot keyboards */ retval = true; goto out; } } path = NextDevicePathNode(path); } } out: free(hin); return retval; } static void set_currdev(const char *devname) { env_setenv("currdev", EV_VOLATILE, devname, efi_setcurrdev, env_nounset); env_setenv("loaddev", EV_VOLATILE, devname, env_noset, env_nounset); } static void set_currdev_devdesc(struct devdesc *currdev) { const char *devname; devname = efi_fmtdev(currdev); printf("Setting currdev to %s\n", devname); set_currdev(devname); } static void set_currdev_devsw(struct devsw *dev, int unit) { struct devdesc currdev; currdev.d_dev = dev; currdev.d_unit = unit; set_currdev_devdesc(&currdev); } static void set_currdev_pdinfo(pdinfo_t *dp) { /* * Disks are special: they have partitions. if the parent * pointer is non-null, we're a partition not a full disk * and we need to adjust currdev appropriately. */ if (dp->pd_devsw->dv_type == DEVT_DISK) { struct disk_devdesc currdev; currdev.dd.d_dev = dp->pd_devsw; if (dp->pd_parent == NULL) { currdev.dd.d_unit = dp->pd_unit; - currdev.d_slice = -1; - currdev.d_partition = -1; + currdev.d_slice = D_SLICENONE; + currdev.d_partition = D_PARTNONE; } else { currdev.dd.d_unit = dp->pd_parent->pd_unit; currdev.d_slice = dp->pd_unit; - currdev.d_partition = 255; /* Assumes GPT */ + currdev.d_partition = D_PARTISGPT; /* XXX Assumes GPT */ } set_currdev_devdesc((struct devdesc *)&currdev); } else { set_currdev_devsw(dp->pd_devsw, dp->pd_unit); } } static bool sanity_check_currdev(void) { struct stat st; return (stat("/boot/defaults/loader.conf", &st) == 0 || stat("/boot/kernel/kernel", &st) == 0); } #ifdef EFI_ZFS_BOOT static bool probe_zfs_currdev(uint64_t guid) { char *devname; struct zfs_devdesc currdev; currdev.dd.d_dev = &zfs_dev; currdev.dd.d_unit = 0; currdev.pool_guid = guid; currdev.root_guid = 0; set_currdev_devdesc((struct devdesc *)&currdev); devname = efi_fmtdev(&currdev); init_zfs_bootenv(devname); return (sanity_check_currdev()); } #endif static bool try_as_currdev(pdinfo_t *hd, pdinfo_t *pp) { uint64_t guid; #ifdef EFI_ZFS_BOOT /* * If there's a zpool on this device, try it as a ZFS * filesystem, which has somewhat different setup than all * other types of fs due to imperfect loader integration. * This all stems from ZFS being both a device (zpool) and * a filesystem, plus the boot env feature. */ if (efizfs_get_guid_by_handle(pp->pd_handle, &guid)) return (probe_zfs_currdev(guid)); #endif /* * All other filesystems just need the pdinfo * initialized in the standard way. */ set_currdev_pdinfo(pp); return (sanity_check_currdev()); } /* * Sometimes we get filenames that are all upper case * and/or have backslashes in them. Filter all this out * if it looks like we need to do so. */ static void fix_dosisms(char *p) { while (*p) { if (isupper(*p)) *p = tolower(*p); else if (*p == '\\') *p = '/'; p++; } } #define SIZE(dp, edp) (size_t)((intptr_t)(void *)edp - (intptr_t)(void *)dp) enum { BOOT_INFO_OK = 0, BAD_CHOICE = 1, NOT_SPECIFIC = 2 }; static int match_boot_info(EFI_LOADED_IMAGE *img __unused, char *boot_info, size_t bisz) { uint32_t attr; uint16_t fplen; size_t len; char *walker, *ep; EFI_DEVICE_PATH *dp, *edp, *first_dp, *last_dp; pdinfo_t *pp; CHAR16 *descr; char *kernel = NULL; FILEPATH_DEVICE_PATH *fp; struct stat st; CHAR16 *text; /* * FreeBSD encodes it's boot loading path into the boot loader * BootXXXX variable. We look for the last one in the path * and use that to load the kernel. However, if we only fine * one DEVICE_PATH, then there's nothing specific and we should * fall back. * * In an ideal world, we'd look at the image handle we were * passed, match up with the loader we are and then return the * next one in the path. This would be most flexible and cover * many chain booting scenarios where you need to use this * boot loader to get to the next boot loader. However, that * doesn't work. We rarely have the path to the image booted * (just the device) so we can't count on that. So, we do the * enxt best thing, we look through the device path(s) passed * in the BootXXXX varaible. If there's only one, we return * NOT_SPECIFIC. Otherwise, we look at the last one and try to * load that. If we can, we return BOOT_INFO_OK. Otherwise we * return BAD_CHOICE for the caller to sort out. */ if (bisz < sizeof(attr) + sizeof(fplen) + sizeof(CHAR16)) return NOT_SPECIFIC; walker = boot_info; ep = walker + bisz; memcpy(&attr, walker, sizeof(attr)); walker += sizeof(attr); memcpy(&fplen, walker, sizeof(fplen)); walker += sizeof(fplen); descr = (CHAR16 *)(intptr_t)walker; len = ucs2len(descr); walker += (len + 1) * sizeof(CHAR16); last_dp = first_dp = dp = (EFI_DEVICE_PATH *)walker; edp = (EFI_DEVICE_PATH *)(walker + fplen); if ((char *)edp > ep) return NOT_SPECIFIC; while (dp < edp && SIZE(dp, edp) > sizeof(EFI_DEVICE_PATH)) { text = efi_devpath_name(dp); if (text != NULL) { printf(" BootInfo Path: %S\n", text); efi_free_devpath_name(text); } last_dp = dp; dp = (EFI_DEVICE_PATH *)((char *)dp + efi_devpath_length(dp)); } /* * If there's only one item in the list, then nothing was * specified. Or if the last path doesn't have a media * path in it. Those show up as various VenHw() nodes * which are basically opaque to us. Don't count those * as something specifc. */ if (last_dp == first_dp) { printf("Ignoring Boot%04x: Only one DP found\n", boot_current); return NOT_SPECIFIC; } if (efi_devpath_to_media_path(last_dp) == NULL) { printf("Ignoring Boot%04x: No Media Path\n", boot_current); return NOT_SPECIFIC; } /* * OK. At this point we either have a good path or a bad one. * Let's check. */ pp = efiblk_get_pdinfo_by_device_path(last_dp); if (pp == NULL) { printf("Ignoring Boot%04x: Device Path not found\n", boot_current); return BAD_CHOICE; } set_currdev_pdinfo(pp); if (!sanity_check_currdev()) { printf("Ignoring Boot%04x: sanity check failed\n", boot_current); return BAD_CHOICE; } /* * OK. We've found a device that matches, next we need to check the last * component of the path. If it's a file, then we set the default kernel * to that. Otherwise, just use this as the default root. * * Reminder: we're running very early, before we've parsed the defaults * file, so we may need to have a hack override. */ dp = efi_devpath_last_node(last_dp); if (DevicePathType(dp) != MEDIA_DEVICE_PATH || DevicePathSubType(dp) != MEDIA_FILEPATH_DP) { printf("Using Boot%04x for root partition\n", boot_current); return (BOOT_INFO_OK); /* use currdir, default kernel */ } fp = (FILEPATH_DEVICE_PATH *)dp; ucs2_to_utf8(fp->PathName, &kernel); if (kernel == NULL) { printf("Not using Boot%04x: can't decode kernel\n", boot_current); return (BAD_CHOICE); } if (*kernel == '\\' || isupper(*kernel)) fix_dosisms(kernel); if (stat(kernel, &st) != 0) { free(kernel); printf("Not using Boot%04x: can't find %s\n", boot_current, kernel); return (BAD_CHOICE); } setenv("kernel", kernel, 1); free(kernel); text = efi_devpath_name(last_dp); if (text) { printf("Using Boot%04x %S + %s\n", boot_current, text, kernel); efi_free_devpath_name(text); } return (BOOT_INFO_OK); } /* * Look at the passed-in boot_info, if any. If we find it then we need * to see if we can find ourselves in the boot chain. If we can, and * there's another specified thing to boot next, assume that the file * is loaded from / and use that for the root filesystem. If can't * find the specified thing, we must fail the boot. If we're last on * the list, then we fallback to looking for the first available / * candidate (ZFS, if there's a bootable zpool, otherwise a UFS * partition that has either /boot/defaults/loader.conf on it or * /boot/kernel/kernel (the default kernel) that we can use. * * We always fail if we can't find the right thing. However, as * a concession to buggy UEFI implementations, like u-boot, if * we have determined that the host is violating the UEFI boot * manager protocol, we'll signal the rest of the program that * a drop to the OK boot loader prompt is possible. */ static int find_currdev(EFI_LOADED_IMAGE *img, bool do_bootmgr, bool is_last, char *boot_info, size_t boot_info_sz) { pdinfo_t *dp, *pp; EFI_DEVICE_PATH *devpath, *copy; EFI_HANDLE h; CHAR16 *text; struct devsw *dev; int unit; uint64_t extra; int rv; char *rootdev; /* * First choice: if rootdev is already set, use that, even if * it's wrong. */ rootdev = getenv("rootdev"); if (rootdev != NULL) { printf("Setting currdev to configured rootdev %s\n", rootdev); set_currdev(rootdev); return (0); } /* * Second choice: If we can find out image boot_info, and there's * a follow-on boot image in that boot_info, use that. In this * case root will be the partition specified in that image and * we'll load the kernel specified by the file path. Should there * not be a filepath, we use the default. This filepath overrides * loader.conf. */ if (do_bootmgr) { rv = match_boot_info(img, boot_info, boot_info_sz); switch (rv) { case BOOT_INFO_OK: /* We found it */ return (0); case BAD_CHOICE: /* specified file not found -> error */ /* XXX do we want to have an escape hatch for last in boot order? */ return (ENOENT); } /* Nothing specified, try normal match */ } #ifdef EFI_ZFS_BOOT /* * Did efi_zfs_probe() detect the boot pool? If so, use the zpool * it found, if it's sane. ZFS is the only thing that looks for * disks and pools to boot. This may change in the future, however, * if we allow specifying which pool to boot from via UEFI variables * rather than the bootenv stuff that FreeBSD uses today. */ if (pool_guid != 0) { printf("Trying ZFS pool\n"); if (probe_zfs_currdev(pool_guid)) return (0); } #endif /* EFI_ZFS_BOOT */ /* * Try to find the block device by its handle based on the * image we're booting. If we can't find a sane partition, * search all the other partitions of the disk. We do not * search other disks because it's a violation of the UEFI * boot protocol to do so. We fail and let UEFI go on to * the next candidate. */ dp = efiblk_get_pdinfo_by_handle(img->DeviceHandle); if (dp != NULL) { text = efi_devpath_name(dp->pd_devpath); if (text != NULL) { printf("Trying ESP: %S\n", text); efi_free_devpath_name(text); } set_currdev_pdinfo(dp); if (sanity_check_currdev()) return (0); if (dp->pd_parent != NULL) { pdinfo_t *espdp = dp; dp = dp->pd_parent; STAILQ_FOREACH(pp, &dp->pd_part, pd_link) { /* Already tried the ESP */ if (espdp == pp) continue; /* * Roll up the ZFS special case * for those partitions that have * zpools on them. */ text = efi_devpath_name(pp->pd_devpath); if (text != NULL) { printf("Trying: %S\n", text); efi_free_devpath_name(text); } if (try_as_currdev(dp, pp)) return (0); } } } /* * Try the device handle from our loaded image first. If that * fails, use the device path from the loaded image and see if * any of the nodes in that path match one of the enumerated * handles. Currently, this handle list is only for netboot. */ if (efi_handle_lookup(img->DeviceHandle, &dev, &unit, &extra) == 0) { set_currdev_devsw(dev, unit); if (sanity_check_currdev()) return (0); } copy = NULL; devpath = efi_lookup_image_devpath(IH); while (devpath != NULL) { h = efi_devpath_handle(devpath); if (h == NULL) break; free(copy); copy = NULL; if (efi_handle_lookup(h, &dev, &unit, &extra) == 0) { set_currdev_devsw(dev, unit); if (sanity_check_currdev()) return (0); } devpath = efi_lookup_devpath(h); if (devpath != NULL) { copy = efi_devpath_trim(devpath); devpath = copy; } } free(copy); return (ENOENT); } static bool interactive_interrupt(const char *msg) { time_t now, then, last; last = 0; now = then = getsecs(); printf("%s\n", msg); if (fail_timeout == -2) /* Always break to OK */ return (true); if (fail_timeout == -1) /* Never break to OK */ return (false); do { if (last != now) { printf("press any key to interrupt reboot in %d seconds\r", fail_timeout - (int)(now - then)); last = now; } /* XXX no pause or timeout wait for char */ if (ischar()) return (true); now = getsecs(); } while (now - then < fail_timeout); return (false); } static int parse_args(int argc, CHAR16 *argv[]) { int i, j, howto; bool vargood; char var[128]; /* * Parse the args to set the console settings, etc * boot1.efi passes these in, if it can read /boot.config or /boot/config * or iPXE may be setup to pass these in. Or the optional argument in the * boot environment was used to pass these arguments in (in which case * neither /boot.config nor /boot/config are consulted). * * Loop through the args, and for each one that contains an '=' that is * not the first character, add it to the environment. This allows * loader and kernel env vars to be passed on the command line. Convert * args from UCS-2 to ASCII (16 to 8 bit) as they are copied (though this * method is flawed for non-ASCII characters). */ howto = 0; for (i = 1; i < argc; i++) { cpy16to8(argv[i], var, sizeof(var)); howto |= boot_parse_arg(var); } return (howto); } static void setenv_int(const char *key, int val) { char buf[20]; snprintf(buf, sizeof(buf), "%d", val); setenv(key, buf, 1); } /* * Parse ConOut (the list of consoles active) and see if we can find a * serial port and/or a video port. It would be nice to also walk the * ACPI name space to map the UID for the serial port to a port. The * latter is especially hard. */ static int parse_uefi_con_out(void) { int how, rv; int vid_seen = 0, com_seen = 0, seen = 0; size_t sz; char buf[4096], *ep; EFI_DEVICE_PATH *node; ACPI_HID_DEVICE_PATH *acpi; UART_DEVICE_PATH *uart; bool pci_pending; how = 0; sz = sizeof(buf); rv = efi_global_getenv("ConOut", buf, &sz); if (rv != EFI_SUCCESS) goto out; ep = buf + sz; node = (EFI_DEVICE_PATH *)buf; while ((char *)node < ep) { pci_pending = false; if (DevicePathType(node) == ACPI_DEVICE_PATH && DevicePathSubType(node) == ACPI_DP) { /* Check for Serial node */ acpi = (void *)node; if (EISA_ID_TO_NUM(acpi->HID) == 0x501) { setenv_int("efi_8250_uid", acpi->UID); com_seen = ++seen; } } else if (DevicePathType(node) == MESSAGING_DEVICE_PATH && DevicePathSubType(node) == MSG_UART_DP) { uart = (void *)node; setenv_int("efi_com_speed", uart->BaudRate); } else if (DevicePathType(node) == ACPI_DEVICE_PATH && DevicePathSubType(node) == ACPI_ADR_DP) { /* Check for AcpiAdr() Node for video */ vid_seen = ++seen; } else if (DevicePathType(node) == HARDWARE_DEVICE_PATH && DevicePathSubType(node) == HW_PCI_DP) { /* * Note, vmware fusion has a funky console device * PciRoot(0x0)/Pci(0xf,0x0) * which we can only detect at the end since we also * have to cope with: * PciRoot(0x0)/Pci(0x1f,0x0)/Serial(0x1) * so only match it if it's last. */ pci_pending = true; } node = NextDevicePathNode(node); /* Skip the end node */ } if (pci_pending && vid_seen == 0) vid_seen = ++seen; /* * Truth table for RB_MULTIPLE | RB_SERIAL * Value Result * 0 Use only video console * RB_SERIAL Use only serial console * RB_MULTIPLE Use both video and serial console * (but video is primary so gets rc messages) * both Use both video and serial console * (but serial is primary so gets rc messages) * * Try to honor this as best we can. If only one of serial / video * found, then use that. Otherwise, use the first one we found. * This also implies if we found nothing, default to video. */ how = 0; if (vid_seen && com_seen) { how |= RB_MULTIPLE; if (com_seen < vid_seen) how |= RB_SERIAL; } else if (com_seen) how |= RB_SERIAL; out: return (how); } EFI_STATUS main(int argc, CHAR16 *argv[]) { EFI_GUID *guid; int howto, i, uhowto; UINTN k; bool has_kbd, is_last; char *s; EFI_DEVICE_PATH *imgpath; CHAR16 *text; EFI_STATUS rv; size_t sz, bosz = 0, bisz = 0; UINT16 boot_order[100]; char boot_info[4096]; EFI_LOADED_IMAGE *img; char buf[32]; bool uefi_boot_mgr; archsw.arch_autoload = efi_autoload; archsw.arch_getdev = efi_getdev; archsw.arch_copyin = efi_copyin; archsw.arch_copyout = efi_copyout; archsw.arch_readin = efi_readin; #ifdef EFI_ZFS_BOOT /* Note this needs to be set before ZFS init. */ archsw.arch_zfs_probe = efi_zfs_probe; #endif /* Get our loaded image protocol interface structure. */ BS->HandleProtocol(IH, &imgid, (VOID**)&img); #ifdef EFI_ZFS_BOOT /* Tell ZFS probe code where we booted from */ efizfs_set_preferred(img->DeviceHandle); #endif /* Init the time source */ efi_time_init(); has_kbd = has_keyboard(); /* * XXX Chicken-and-egg problem; we want to have console output * early, but some console attributes may depend on reading from * eg. the boot device, which we can't do yet. We can use * printf() etc. once this is done. */ setenv("console", "efi", 1); cons_probe(); /* * Initialise the block cache. Set the upper limit. */ bcache_init(32768, 512); howto = parse_args(argc, argv); if (!has_kbd && (howto & RB_PROBE)) howto |= RB_SERIAL | RB_MULTIPLE; howto &= ~RB_PROBE; uhowto = parse_uefi_con_out(); /* * We now have two notions of console. howto should be viewed as * overrides. If console is already set, don't set it again. */ #define VIDEO_ONLY 0 #define SERIAL_ONLY RB_SERIAL #define VID_SER_BOTH RB_MULTIPLE #define SER_VID_BOTH (RB_SERIAL | RB_MULTIPLE) #define CON_MASK (RB_SERIAL | RB_MULTIPLE) if (strcmp(getenv("console"), "efi") == 0) { if ((howto & CON_MASK) == 0) { /* No override, uhowto is controlling and efi cons is perfect */ howto = howto | (uhowto & CON_MASK); setenv("console", "efi", 1); } else if ((howto & CON_MASK) == (uhowto & CON_MASK)) { /* override matches what UEFI told us, efi console is perfect */ setenv("console", "efi", 1); } else if ((uhowto & (CON_MASK)) != 0) { /* * We detected a serial console on ConOut. All possible * overrides include serial. We can't really override what efi * gives us, so we use it knowing it's the best choice. */ setenv("console", "efi", 1); } else { /* * We detected some kind of serial in the override, but ConOut * has no serial, so we have to sort out which case it really is. */ switch (howto & CON_MASK) { case SERIAL_ONLY: setenv("console", "comconsole", 1); break; case VID_SER_BOTH: setenv("console", "efi comconsole", 1); break; case SER_VID_BOTH: setenv("console", "comconsole efi", 1); break; /* case VIDEO_ONLY can't happen -- it's the first if above */ } } } /* * howto is set now how we want to export the flags to the kernel, so * set the env based on it. */ boot_howto_to_env(howto); if (efi_copy_init()) { printf("failed to allocate staging area\n"); return (EFI_BUFFER_TOO_SMALL); } if ((s = getenv("fail_timeout")) != NULL) fail_timeout = strtol(s, NULL, 10); /* * Scan the BLOCK IO MEDIA handles then * march through the device switch probing for things. */ i = efipart_inithandles(); if (i != 0 && i != ENOENT) { printf("efipart_inithandles failed with ERRNO %d, expect " "failures\n", i); } for (i = 0; devsw[i] != NULL; i++) if (devsw[i]->dv_init != NULL) (devsw[i]->dv_init)(); printf("%s\n", bootprog_info); printf(" Command line arguments:"); for (i = 0; i < argc; i++) printf(" %S", argv[i]); printf("\n"); printf(" EFI version: %d.%02d\n", ST->Hdr.Revision >> 16, ST->Hdr.Revision & 0xffff); printf(" EFI Firmware: %S (rev %d.%02d)\n", ST->FirmwareVendor, ST->FirmwareRevision >> 16, ST->FirmwareRevision & 0xffff); printf(" Console: %s (%#x)\n", getenv("console"), howto); /* Determine the devpath of our image so we can prefer it. */ text = efi_devpath_name(img->FilePath); if (text != NULL) { printf(" Load Path: %S\n", text); efi_setenv_freebsd_wcs("LoaderPath", text); efi_free_devpath_name(text); } rv = BS->HandleProtocol(img->DeviceHandle, &devid, (void **)&imgpath); if (rv == EFI_SUCCESS) { text = efi_devpath_name(imgpath); if (text != NULL) { printf(" Load Device: %S\n", text); efi_setenv_freebsd_wcs("LoaderDev", text); efi_free_devpath_name(text); } } uefi_boot_mgr = true; boot_current = 0; sz = sizeof(boot_current); rv = efi_global_getenv("BootCurrent", &boot_current, &sz); if (rv == EFI_SUCCESS) printf(" BootCurrent: %04x\n", boot_current); else { boot_current = 0xffff; uefi_boot_mgr = false; } sz = sizeof(boot_order); rv = efi_global_getenv("BootOrder", &boot_order, &sz); if (rv == EFI_SUCCESS) { printf(" BootOrder:"); for (i = 0; i < sz / sizeof(boot_order[0]); i++) printf(" %04x%s", boot_order[i], boot_order[i] == boot_current ? "[*]" : ""); printf("\n"); is_last = boot_order[(sz / sizeof(boot_order[0])) - 1] == boot_current; bosz = sz; } else if (uefi_boot_mgr) { /* * u-boot doesn't set BootOrder, but otherwise participates in the * boot manager protocol. So we fake it here and don't consider it * a failure. */ bosz = sizeof(boot_order[0]); boot_order[0] = boot_current; is_last = true; } /* * Next, find the boot info structure the UEFI boot manager is * supposed to setup. We need this so we can walk through it to * find where we are in the booting process and what to try to * boot next. */ if (uefi_boot_mgr) { snprintf(buf, sizeof(buf), "Boot%04X", boot_current); sz = sizeof(boot_info); rv = efi_global_getenv(buf, &boot_info, &sz); if (rv == EFI_SUCCESS) bisz = sz; else uefi_boot_mgr = false; } /* * Disable the watchdog timer. By default the boot manager sets * the timer to 5 minutes before invoking a boot option. If we * want to return to the boot manager, we have to disable the * watchdog timer and since we're an interactive program, we don't * want to wait until the user types "quit". The timer may have * fired by then. We don't care if this fails. It does not prevent * normal functioning in any way... */ BS->SetWatchdogTimer(0, 0, 0, NULL); /* * Initialize the trusted/forbidden certificates from UEFI. * They will be later used to verify the manifest(s), * which should contain hashes of verified files. * This needs to be initialized before any configuration files * are loaded. */ #ifdef EFI_SECUREBOOT ve_efi_init(); #endif /* * Try and find a good currdev based on the image that was booted. * It might be desirable here to have a short pause to allow falling * through to the boot loader instead of returning instantly to follow * the boot protocol and also allow an escape hatch for users wishing * to try something different. */ if (find_currdev(img, uefi_boot_mgr, is_last, boot_info, bisz) != 0) if (!interactive_interrupt("Failed to find bootable partition")) return (EFI_NOT_FOUND); efi_init_environment(); #if !defined(__arm__) for (k = 0; k < ST->NumberOfTableEntries; k++) { guid = &ST->ConfigurationTable[k].VendorGuid; if (!memcmp(guid, &smbios, sizeof(EFI_GUID))) { char buf[40]; snprintf(buf, sizeof(buf), "%p", ST->ConfigurationTable[k].VendorTable); setenv("hint.smbios.0.mem", buf, 1); smbios_detect(ST->ConfigurationTable[k].VendorTable); break; } } #endif interact(); /* doesn't return */ return (EFI_SUCCESS); /* keep compiler happy */ } COMMAND_SET(poweroff, "poweroff", "power off the system", command_poweroff); static int command_poweroff(int argc __unused, char *argv[] __unused) { int i; for (i = 0; devsw[i] != NULL; ++i) if (devsw[i]->dv_cleanup != NULL) (devsw[i]->dv_cleanup)(); RS->ResetSystem(EfiResetShutdown, EFI_SUCCESS, 0, NULL); /* NOTREACHED */ return (CMD_ERROR); } COMMAND_SET(reboot, "reboot", "reboot the system", command_reboot); static int command_reboot(int argc, char *argv[]) { int i; for (i = 0; devsw[i] != NULL; ++i) if (devsw[i]->dv_cleanup != NULL) (devsw[i]->dv_cleanup)(); RS->ResetSystem(EfiResetCold, EFI_SUCCESS, 0, NULL); /* NOTREACHED */ return (CMD_ERROR); } COMMAND_SET(quit, "quit", "exit the loader", command_quit); static int command_quit(int argc, char *argv[]) { exit(0); return (CMD_OK); } COMMAND_SET(memmap, "memmap", "print memory map", command_memmap); static int command_memmap(int argc __unused, char *argv[] __unused) { UINTN sz; EFI_MEMORY_DESCRIPTOR *map, *p; UINTN key, dsz; UINT32 dver; EFI_STATUS status; int i, ndesc; char line[80]; 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 (CMD_ERROR); } map = malloc(sz); status = BS->GetMemoryMap(&sz, map, &key, &dsz, &dver); if (EFI_ERROR(status)) { printf("Can't read memory map\n"); return (CMD_ERROR); } ndesc = sz / dsz; snprintf(line, sizeof(line), "%23s %12s %12s %8s %4s\n", "Type", "Physical", "Virtual", "#Pages", "Attr"); pager_open(); if (pager_output(line)) { pager_close(); return (CMD_OK); } for (i = 0, p = map; i < ndesc; i++, p = NextMemoryDescriptor(p, dsz)) { snprintf(line, sizeof(line), "%23s %012jx %012jx %08jx ", efi_memory_type(p->Type), (uintmax_t)p->PhysicalStart, (uintmax_t)p->VirtualStart, (uintmax_t)p->NumberOfPages); if (pager_output(line)) break; if (p->Attribute & EFI_MEMORY_UC) printf("UC "); if (p->Attribute & EFI_MEMORY_WC) printf("WC "); if (p->Attribute & EFI_MEMORY_WT) printf("WT "); if (p->Attribute & EFI_MEMORY_WB) printf("WB "); if (p->Attribute & EFI_MEMORY_UCE) printf("UCE "); if (p->Attribute & EFI_MEMORY_WP) printf("WP "); if (p->Attribute & EFI_MEMORY_RP) printf("RP "); if (p->Attribute & EFI_MEMORY_XP) printf("XP "); if (p->Attribute & EFI_MEMORY_NV) printf("NV "); if (p->Attribute & EFI_MEMORY_MORE_RELIABLE) printf("MR "); if (p->Attribute & EFI_MEMORY_RO) printf("RO "); if (pager_output("\n")) break; } pager_close(); return (CMD_OK); } COMMAND_SET(configuration, "configuration", "print configuration tables", command_configuration); static int command_configuration(int argc, char *argv[]) { UINTN i; char *name; printf("NumberOfTableEntries=%lu\n", (unsigned long)ST->NumberOfTableEntries); for (i = 0; i < ST->NumberOfTableEntries; i++) { EFI_GUID *guid; printf(" "); guid = &ST->ConfigurationTable[i].VendorGuid; if (efi_guid_to_name(guid, &name) == true) { printf(name); free(name); } else { printf("Error while translating UUID to name"); } printf(" at %p\n", ST->ConfigurationTable[i].VendorTable); } return (CMD_OK); } COMMAND_SET(mode, "mode", "change or display EFI text modes", command_mode); static int command_mode(int argc, char *argv[]) { UINTN cols, rows; unsigned int mode; int i; char *cp; char rowenv[8]; EFI_STATUS status; SIMPLE_TEXT_OUTPUT_INTERFACE *conout; extern void HO(void); conout = ST->ConOut; if (argc > 1) { mode = strtol(argv[1], &cp, 0); if (cp[0] != '\0') { printf("Invalid mode\n"); return (CMD_ERROR); } status = conout->QueryMode(conout, mode, &cols, &rows); if (EFI_ERROR(status)) { printf("invalid mode %d\n", mode); return (CMD_ERROR); } status = conout->SetMode(conout, mode); if (EFI_ERROR(status)) { printf("couldn't set mode %d\n", mode); return (CMD_ERROR); } sprintf(rowenv, "%u", (unsigned)rows); setenv("LINES", rowenv, 1); HO(); /* set cursor */ return (CMD_OK); } printf("Current mode: %d\n", conout->Mode->Mode); for (i = 0; i <= conout->Mode->MaxMode; i++) { status = conout->QueryMode(conout, i, &cols, &rows); if (EFI_ERROR(status)) continue; printf("Mode %d: %u columns, %u rows\n", i, (unsigned)cols, (unsigned)rows); } if (i != 0) printf("Select a mode with the command \"mode \"\n"); return (CMD_OK); } COMMAND_SET(lsefi, "lsefi", "list EFI handles", command_lsefi); static int command_lsefi(int argc __unused, char *argv[] __unused) { char *name; EFI_HANDLE *buffer = NULL; EFI_HANDLE handle; UINTN bufsz = 0, i, j; EFI_STATUS status; int ret; status = BS->LocateHandle(AllHandles, NULL, NULL, &bufsz, buffer); if (status != EFI_BUFFER_TOO_SMALL) { snprintf(command_errbuf, sizeof (command_errbuf), "unexpected error: %lld", (long long)status); return (CMD_ERROR); } if ((buffer = malloc(bufsz)) == NULL) { sprintf(command_errbuf, "out of memory"); return (CMD_ERROR); } status = BS->LocateHandle(AllHandles, NULL, NULL, &bufsz, buffer); if (EFI_ERROR(status)) { free(buffer); snprintf(command_errbuf, sizeof (command_errbuf), "LocateHandle() error: %lld", (long long)status); return (CMD_ERROR); } pager_open(); for (i = 0; i < (bufsz / sizeof (EFI_HANDLE)); i++) { UINTN nproto = 0; EFI_GUID **protocols = NULL; handle = buffer[i]; printf("Handle %p", handle); if (pager_output("\n")) break; /* device path */ status = BS->ProtocolsPerHandle(handle, &protocols, &nproto); if (EFI_ERROR(status)) { snprintf(command_errbuf, sizeof (command_errbuf), "ProtocolsPerHandle() error: %lld", (long long)status); continue; } for (j = 0; j < nproto; j++) { if (efi_guid_to_name(protocols[j], &name) == true) { printf(" %s", name); free(name); } else { printf("Error while translating UUID to name"); } if ((ret = pager_output("\n")) != 0) break; } BS->FreePool(protocols); if (ret != 0) break; } pager_close(); free(buffer); return (CMD_OK); } #ifdef LOADER_FDT_SUPPORT extern int command_fdt_internal(int argc, char *argv[]); /* * 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 /* * Chain load another efi loader. */ static int command_chain(int argc, char *argv[]) { EFI_GUID LoadedImageGUID = LOADED_IMAGE_PROTOCOL; EFI_HANDLE loaderhandle; EFI_LOADED_IMAGE *loaded_image; EFI_STATUS status; struct stat st; struct devdesc *dev; char *name, *path; void *buf; int fd; if (argc < 2) { command_errmsg = "wrong number of arguments"; return (CMD_ERROR); } name = argv[1]; if ((fd = open(name, O_RDONLY)) < 0) { command_errmsg = "no such file"; return (CMD_ERROR); } if (fstat(fd, &st) < -1) { command_errmsg = "stat failed"; close(fd); return (CMD_ERROR); } status = BS->AllocatePool(EfiLoaderCode, (UINTN)st.st_size, &buf); if (status != EFI_SUCCESS) { command_errmsg = "failed to allocate buffer"; close(fd); return (CMD_ERROR); } if (read(fd, buf, st.st_size) != st.st_size) { command_errmsg = "error while reading the file"; (void)BS->FreePool(buf); close(fd); return (CMD_ERROR); } close(fd); status = BS->LoadImage(FALSE, IH, NULL, buf, st.st_size, &loaderhandle); (void)BS->FreePool(buf); if (status != EFI_SUCCESS) { command_errmsg = "LoadImage failed"; return (CMD_ERROR); } status = BS->HandleProtocol(loaderhandle, &LoadedImageGUID, (void **)&loaded_image); if (argc > 2) { int i, len = 0; CHAR16 *argp; for (i = 2; i < argc; i++) len += strlen(argv[i]) + 1; len *= sizeof (*argp); loaded_image->LoadOptions = argp = malloc (len); loaded_image->LoadOptionsSize = len; for (i = 2; i < argc; i++) { char *ptr = argv[i]; while (*ptr) *(argp++) = *(ptr++); *(argp++) = ' '; } *(--argv) = 0; } if (efi_getdev((void **)&dev, name, (const char **)&path) == 0) { #ifdef EFI_ZFS_BOOT struct zfs_devdesc *z_dev; #endif struct disk_devdesc *d_dev; pdinfo_t *hd, *pd; switch (dev->d_dev->dv_type) { #ifdef EFI_ZFS_BOOT case DEVT_ZFS: z_dev = (struct zfs_devdesc *)dev; loaded_image->DeviceHandle = efizfs_get_handle_by_guid(z_dev->pool_guid); break; #endif case DEVT_NET: loaded_image->DeviceHandle = efi_find_handle(dev->d_dev, dev->d_unit); break; default: hd = efiblk_get_pdinfo(dev); if (STAILQ_EMPTY(&hd->pd_part)) { loaded_image->DeviceHandle = hd->pd_handle; break; } d_dev = (struct disk_devdesc *)dev; STAILQ_FOREACH(pd, &hd->pd_part, pd_link) { /* * d_partition should be 255 */ if (pd->pd_unit == (uint32_t)d_dev->d_slice) { loaded_image->DeviceHandle = pd->pd_handle; break; } } break; } } dev_cleanup(); status = BS->StartImage(loaderhandle, NULL, NULL); if (status != EFI_SUCCESS) { command_errmsg = "StartImage failed"; free(loaded_image->LoadOptions); loaded_image->LoadOptions = NULL; status = BS->UnloadImage(loaded_image); return (CMD_ERROR); } return (CMD_ERROR); /* not reached */ } COMMAND_SET(chain, "chain", "chain load file", command_chain); Index: head/stand/i386/libi386/biosdisk.c =================================================================== --- head/stand/i386/libi386/biosdisk.c (revision 345476) +++ head/stand/i386/libi386/biosdisk.c (revision 345477) @@ -1,1317 +1,1317 @@ /*- * 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$"); /* * BIOS disk device handling. * * Ideas and algorithms from: * * - NetBSD libi386/biosdisk.c * - FreeBSD biosboot/disk.c * */ #include #include #include #include #include #include #include #include #include #include #include "disk.h" #include "libi386.h" #define BIOS_NUMDRIVES 0x475 #define BIOSDISK_SECSIZE 512 #define BUFSIZE (1 * BIOSDISK_SECSIZE) #define DT_ATAPI 0x10 /* disk type for ATAPI floppies */ #define WDMAJOR 0 /* major numbers for devices we frontend for */ #define WFDMAJOR 1 #define FDMAJOR 2 #define DAMAJOR 4 #define ACDMAJOR 117 #define CDMAJOR 15 #ifdef DISK_DEBUG #define DEBUG(fmt, args...) printf("%s: " fmt "\n", __func__, ## args) #else #define DEBUG(fmt, args...) #endif struct specification_packet { uint8_t sp_size; uint8_t sp_bootmedia; uint8_t sp_drive; uint8_t sp_controller; uint32_t sp_lba; uint16_t sp_devicespec; uint16_t sp_buffersegment; uint16_t sp_loadsegment; uint16_t sp_sectorcount; uint16_t sp_cylsec; uint8_t sp_head; }; /* * List of BIOS devices, translation from disk unit number to * BIOS unit number. */ typedef struct bdinfo { STAILQ_ENTRY(bdinfo) bd_link; /* link in device list */ int bd_unit; /* BIOS unit number */ int bd_cyl; /* BIOS geometry */ int bd_hds; int bd_sec; int bd_flags; #define BD_MODEINT13 0x0000 #define BD_MODEEDD1 0x0001 #define BD_MODEEDD3 0x0002 #define BD_MODEEDD (BD_MODEEDD1 | BD_MODEEDD3) #define BD_MODEMASK 0x0003 #define BD_FLOPPY 0x0004 #define BD_CDROM 0x0008 #define BD_NO_MEDIA 0x0010 int bd_type; /* BIOS 'drive type' (floppy only) */ uint16_t bd_sectorsize; /* Sector size */ uint64_t bd_sectors; /* Disk size */ int bd_open; /* reference counter */ void *bd_bcache; /* buffer cache data */ } bdinfo_t; #define BD_RD 0 #define BD_WR 1 typedef STAILQ_HEAD(bdinfo_list, bdinfo) bdinfo_list_t; static bdinfo_list_t fdinfo = STAILQ_HEAD_INITIALIZER(fdinfo); static bdinfo_list_t cdinfo = STAILQ_HEAD_INITIALIZER(cdinfo); static bdinfo_list_t hdinfo = STAILQ_HEAD_INITIALIZER(hdinfo); static void bd_io_workaround(bdinfo_t *); static int bd_io(struct disk_devdesc *, bdinfo_t *, daddr_t, int, caddr_t, int); static bool bd_int13probe(bdinfo_t *); static int bd_init(void); static int cd_init(void); static int fd_init(void); static int bd_strategy(void *devdata, int flag, daddr_t dblk, size_t size, char *buf, size_t *rsize); static int bd_realstrategy(void *devdata, int flag, daddr_t dblk, size_t size, char *buf, size_t *rsize); static int bd_open(struct open_file *f, ...); static int bd_close(struct open_file *f); static int bd_ioctl(struct open_file *f, u_long cmd, void *data); static int bd_print(int verbose); static int cd_print(int verbose); static int fd_print(int verbose); static void bd_reset_disk(int); static int bd_get_diskinfo_std(struct bdinfo *); struct devsw biosfd = { .dv_name = "fd", .dv_type = DEVT_FD, .dv_init = fd_init, .dv_strategy = bd_strategy, .dv_open = bd_open, .dv_close = bd_close, .dv_ioctl = bd_ioctl, .dv_print = fd_print, .dv_cleanup = NULL }; struct devsw bioscd = { .dv_name = "cd", .dv_type = DEVT_CD, .dv_init = cd_init, .dv_strategy = bd_strategy, .dv_open = bd_open, .dv_close = bd_close, .dv_ioctl = bd_ioctl, .dv_print = cd_print, .dv_cleanup = NULL }; struct devsw bioshd = { .dv_name = "disk", .dv_type = DEVT_DISK, .dv_init = bd_init, .dv_strategy = bd_strategy, .dv_open = bd_open, .dv_close = bd_close, .dv_ioctl = bd_ioctl, .dv_print = bd_print, .dv_cleanup = NULL }; static bdinfo_list_t * bd_get_bdinfo_list(struct devsw *dev) { if (dev->dv_type == DEVT_DISK) return (&hdinfo); if (dev->dv_type == DEVT_CD) return (&cdinfo); if (dev->dv_type == DEVT_FD) return (&fdinfo); return (NULL); } /* XXX this gets called way way too often, investigate */ static bdinfo_t * bd_get_bdinfo(struct devdesc *dev) { bdinfo_list_t *bdi; bdinfo_t *bd = NULL; int unit; bdi = bd_get_bdinfo_list(dev->d_dev); if (bdi == NULL) return (bd); unit = 0; STAILQ_FOREACH(bd, bdi, bd_link) { if (unit == dev->d_unit) return (bd); unit++; } return (bd); } /* * Translate between BIOS device numbers and our private unit numbers. */ int bd_bios2unit(int biosdev) { bdinfo_list_t *bdi[] = { &fdinfo, &cdinfo, &hdinfo, NULL }; bdinfo_t *bd; int i, unit; DEBUG("looking for bios device 0x%x", biosdev); for (i = 0; bdi[i] != NULL; i++) { unit = 0; STAILQ_FOREACH(bd, bdi[i], bd_link) { if (bd->bd_unit == biosdev) { DEBUG("bd unit %d is BIOS device 0x%x", unit, bd->bd_unit); return (unit); } unit++; } } return (-1); } int bd_unit2bios(struct i386_devdesc *dev) { bdinfo_list_t *bdi; bdinfo_t *bd; int unit; bdi = bd_get_bdinfo_list(dev->dd.d_dev); if (bdi == NULL) return (-1); unit = 0; STAILQ_FOREACH(bd, bdi, bd_link) { if (unit == dev->dd.d_unit) return (bd->bd_unit); unit++; } return (-1); } /* * Use INT13 AH=15 - Read Drive Type. */ static int fd_count(void) { int drive; for (drive = 0; drive < MAXBDDEV; drive++) { bd_reset_disk(drive); v86.ctl = V86_FLAGS; v86.addr = 0x13; v86.eax = 0x1500; v86.edx = drive; v86int(); if (V86_CY(v86.efl)) break; if ((v86.eax & 0x300) == 0) break; } return (drive); } /* * Quiz the BIOS for disk devices, save a little info about them. */ static int fd_init(void) { int unit, numfd; bdinfo_t *bd; numfd = fd_count(); for (unit = 0; unit < numfd; unit++) { if ((bd = calloc(1, sizeof(*bd))) == NULL) break; bd->bd_sectorsize = BIOSDISK_SECSIZE; bd->bd_flags = BD_FLOPPY; bd->bd_unit = unit; /* Use std diskinfo for floppy drive */ if (bd_get_diskinfo_std(bd) != 0) { free(bd); break; } if (bd->bd_sectors == 0) bd->bd_flags |= BD_NO_MEDIA; printf("BIOS drive %c: is %s%d\n", ('A' + unit), biosfd.dv_name, unit); STAILQ_INSERT_TAIL(&fdinfo, bd, bd_link); } bcache_add_dev(unit); return (0); } static int bd_init(void) { int base, unit; bdinfo_t *bd; base = 0x80; for (unit = 0; unit < *(unsigned char *)PTOV(BIOS_NUMDRIVES); unit++) { /* * Check the BIOS equipment list for number of fixed disks. */ if ((bd = calloc(1, sizeof(*bd))) == NULL) break; bd->bd_unit = base + unit; if (!bd_int13probe(bd)) { free(bd); break; } printf("BIOS drive %c: is %s%d\n", ('C' + unit), bioshd.dv_name, unit); STAILQ_INSERT_TAIL(&hdinfo, bd, bd_link); } bcache_add_dev(unit); return (0); } /* * We can't quiz, we have to be told what device to use, so this function * doesn't do anything. Instead, the loader calls bc_add() with the BIOS * device number to add. */ static int cd_init(void) { return (0); } int bc_add(int biosdev) { bdinfo_t *bd; struct specification_packet bc_sp; int nbcinfo = 0; if (!STAILQ_EMPTY(&cdinfo)) return (-1); v86.ctl = V86_FLAGS; v86.addr = 0x13; v86.eax = 0x4b01; v86.edx = biosdev; v86.ds = VTOPSEG(&bc_sp); v86.esi = VTOPOFF(&bc_sp); v86int(); if ((v86.eax & 0xff00) != 0) return (-1); if ((bd = calloc(1, sizeof(*bd))) == NULL) return (-1); bd->bd_flags = BD_CDROM; bd->bd_unit = biosdev; /* * Ignore result from bd_int13probe(), we will use local * workaround below. */ (void)bd_int13probe(bd); if (bd->bd_cyl == 0) { bd->bd_cyl = ((bc_sp.sp_cylsec & 0xc0) << 2) + ((bc_sp.sp_cylsec & 0xff00) >> 8) + 1; } if (bd->bd_hds == 0) bd->bd_hds = bc_sp.sp_head + 1; if (bd->bd_sec == 0) bd->bd_sec = bc_sp.sp_cylsec & 0x3f; if (bd->bd_sectors == 0) bd->bd_sectors = (uint64_t)bd->bd_cyl * bd->bd_hds * bd->bd_sec; /* Still no size? use 7.961GB */ if (bd->bd_sectors == 0) bd->bd_sectors = 4173824; STAILQ_INSERT_TAIL(&cdinfo, bd, bd_link); printf("BIOS CD is cd%d\n", nbcinfo); nbcinfo++; bcache_add_dev(nbcinfo); /* register cd device in bcache */ return(0); } /* * Return EDD version or 0 if EDD is not supported on this drive. */ static int bd_check_extensions(int unit) { /* do not use ext calls for floppy devices */ if (unit < 0x80) return (0); /* Determine if we can use EDD with this device. */ v86.ctl = V86_FLAGS; v86.addr = 0x13; v86.eax = 0x4100; v86.edx = unit; v86.ebx = 0x55aa; v86int(); if (V86_CY(v86.efl) || /* carry set */ (v86.ebx & 0xffff) != 0xaa55) /* signature */ return (0); /* extended disk access functions (AH=42h-44h,47h,48h) supported */ if ((v86.ecx & EDD_INTERFACE_FIXED_DISK) == 0) return (0); return ((v86.eax >> 8) & 0xff); } static void bd_reset_disk(int unit) { /* reset disk */ v86.ctl = V86_FLAGS; v86.addr = 0x13; v86.eax = 0; v86.edx = unit; v86int(); } /* * Read CHS info. Return 0 on success, error otherwise. */ static int bd_get_diskinfo_std(struct bdinfo *bd) { bzero(&v86, sizeof(v86)); v86.ctl = V86_FLAGS; v86.addr = 0x13; v86.eax = 0x800; v86.edx = bd->bd_unit; v86int(); if (V86_CY(v86.efl) && ((v86.eax & 0xff00) != 0)) return ((v86.eax & 0xff00) >> 8); /* return custom error on absurd sector number */ if ((v86.ecx & 0x3f) == 0) return (0x60); bd->bd_cyl = ((v86.ecx & 0xc0) << 2) + ((v86.ecx & 0xff00) >> 8) + 1; /* Convert max head # -> # of heads */ bd->bd_hds = ((v86.edx & 0xff00) >> 8) + 1; bd->bd_sec = v86.ecx & 0x3f; bd->bd_type = v86.ebx; bd->bd_sectors = (uint64_t)bd->bd_cyl * bd->bd_hds * bd->bd_sec; return (0); } /* * Read EDD info. Return 0 on success, error otherwise. */ static int bd_get_diskinfo_ext(struct bdinfo *bd) { struct edd_params params; uint64_t total; /* Get disk params */ bzero(¶ms, sizeof(params)); params.len = sizeof(params); v86.ctl = V86_FLAGS; v86.addr = 0x13; v86.eax = 0x4800; v86.edx = bd->bd_unit; v86.ds = VTOPSEG(¶ms); v86.esi = VTOPOFF(¶ms); v86int(); if (V86_CY(v86.efl) && ((v86.eax & 0xff00) != 0)) return ((v86.eax & 0xff00) >> 8); /* * Sector size must be a multiple of 512 bytes. * An alternate test would be to check power of 2, * powerof2(params.sector_size). * 16K is largest read buffer we can use at this time. */ if (params.sector_size >= 512 && params.sector_size <= 16384 && (params.sector_size % BIOSDISK_SECSIZE) == 0) bd->bd_sectorsize = params.sector_size; bd->bd_cyl = params.cylinders; bd->bd_hds = params.heads; bd->bd_sec = params.sectors_per_track; if (params.sectors != 0) { total = params.sectors; } else { total = (uint64_t)params.cylinders * params.heads * params.sectors_per_track; } bd->bd_sectors = total; return (0); } /* * Try to detect a device supported by the legacy int13 BIOS */ static bool bd_int13probe(bdinfo_t *bd) { int edd, ret; bd->bd_flags &= ~BD_NO_MEDIA; edd = bd_check_extensions(bd->bd_unit); if (edd == 0) bd->bd_flags |= BD_MODEINT13; else if (edd < 0x30) bd->bd_flags |= BD_MODEEDD1; else bd->bd_flags |= BD_MODEEDD3; /* Default sector size */ bd->bd_sectorsize = BIOSDISK_SECSIZE; /* * Test if the floppy device is present, so we can avoid receiving * bogus information from bd_get_diskinfo_std(). */ if (bd->bd_unit < 0x80) { /* reset disk */ bd_reset_disk(bd->bd_unit); /* Get disk type */ v86.ctl = V86_FLAGS; v86.addr = 0x13; v86.eax = 0x1500; v86.edx = bd->bd_unit; v86int(); if (V86_CY(v86.efl) || (v86.eax & 0x300) == 0) return (false); } ret = 1; if (edd != 0) ret = bd_get_diskinfo_ext(bd); if (ret != 0 || bd->bd_sectors == 0) ret = bd_get_diskinfo_std(bd); if (ret != 0 && bd->bd_unit < 0x80) { /* Set defaults for 1.44 floppy */ bd->bd_cyl = 80; bd->bd_hds = 2; bd->bd_sec = 18; bd->bd_sectors = 2880; /* Since we are there, there most likely is no media */ bd->bd_flags |= BD_NO_MEDIA; ret = 0; } if (ret != 0) { /* CD is special case, bc_add() has its own fallback. */ if ((bd->bd_flags & BD_CDROM) != 0) return (true); if (bd->bd_sectors != 0 && edd != 0) { bd->bd_sec = 63; bd->bd_hds = 255; bd->bd_cyl = (bd->bd_sectors + bd->bd_sec * bd->bd_hds - 1) / bd->bd_sec * bd->bd_hds; } else { const char *dv_name; if ((bd->bd_flags & BD_FLOPPY) != 0) dv_name = biosfd.dv_name; else if ((bd->bd_flags & BD_CDROM) != 0) dv_name = bioscd.dv_name; else dv_name = bioshd.dv_name; printf("Can not get information about %s unit %#x\n", dv_name, bd->bd_unit); return (false); } } if (bd->bd_sec == 0) bd->bd_sec = 63; if (bd->bd_hds == 0) bd->bd_hds = 255; if (bd->bd_sectors == 0) bd->bd_sectors = (uint64_t)bd->bd_cyl * bd->bd_hds * bd->bd_sec; DEBUG("unit 0x%x geometry %d/%d/%d\n", bd->bd_unit, bd->bd_cyl, bd->bd_hds, bd->bd_sec); return (true); } static int bd_count(bdinfo_list_t *bdi) { bdinfo_t *bd; int i; i = 0; STAILQ_FOREACH(bd, bdi, bd_link) i++; return (i); } /* * Print information about disks */ static int bd_print_common(struct devsw *dev, bdinfo_list_t *bdi, int verbose) { char line[80]; struct disk_devdesc devd; bdinfo_t *bd; int i, ret = 0; char drive; if (STAILQ_EMPTY(bdi)) return (0); printf("%s devices:", dev->dv_name); if ((ret = pager_output("\n")) != 0) return (ret); i = -1; STAILQ_FOREACH(bd, bdi, bd_link) { i++; switch (dev->dv_type) { case DEVT_FD: drive = 'A'; break; case DEVT_CD: drive = 'C' + bd_count(&hdinfo); break; default: drive = 'C'; break; } snprintf(line, sizeof(line), " %s%d: BIOS drive %c (%s%ju X %u):\n", dev->dv_name, i, drive + i, (bd->bd_flags & BD_NO_MEDIA) == BD_NO_MEDIA ? "no media, " : "", (uintmax_t)bd->bd_sectors, bd->bd_sectorsize); if ((ret = pager_output(line)) != 0) break; if ((bd->bd_flags & BD_NO_MEDIA) == BD_NO_MEDIA) continue; if (dev->dv_type != DEVT_DISK) continue; devd.dd.d_dev = dev; devd.dd.d_unit = i; - devd.d_slice = -1; - devd.d_partition = -1; + devd.d_slice = D_SLICENONE; + devd.d_partition = D_PARTNONE; if (disk_open(&devd, bd->bd_sectorsize * bd->bd_sectors, bd->bd_sectorsize) == 0) { snprintf(line, sizeof(line), " %s%d", dev->dv_name, i); ret = disk_print(&devd, line, verbose); disk_close(&devd); if (ret != 0) break; } } return (ret); } static int fd_print(int verbose) { return (bd_print_common(&biosfd, &fdinfo, verbose)); } static int bd_print(int verbose) { return (bd_print_common(&bioshd, &hdinfo, verbose)); } static int cd_print(int verbose) { return (bd_print_common(&bioscd, &cdinfo, verbose)); } /* * Read disk size from partition. * This is needed to work around buggy BIOS systems returning * wrong (truncated) disk media size. * During bd_probe() we tested if the multiplication of bd_sectors * would overflow so it should be safe to perform here. */ static uint64_t bd_disk_get_sectors(struct disk_devdesc *dev) { bdinfo_t *bd; struct disk_devdesc disk; uint64_t size; bd = bd_get_bdinfo(&dev->dd); if (bd == NULL) return (0); disk.dd.d_dev = dev->dd.d_dev; disk.dd.d_unit = dev->dd.d_unit; - disk.d_slice = -1; - disk.d_partition = -1; + disk.d_slice = D_SLICENONE; + disk.d_partition = D_PARTNONE; disk.d_offset = 0; size = bd->bd_sectors * bd->bd_sectorsize; if (disk_open(&disk, size, bd->bd_sectorsize) == 0) { (void) disk_ioctl(&disk, DIOCGMEDIASIZE, &size); disk_close(&disk); } return (size / bd->bd_sectorsize); } /* * Attempt to open the disk described by (dev) for use by (f). * * Note that the philosophy here is "give them exactly what * they ask for". This is necessary because being too "smart" * about what the user might want leads to complications. * (eg. given no slice or partition value, with a disk that is * sliced - are they after the first BSD slice, or the DOS * slice before it?) */ static int bd_open(struct open_file *f, ...) { bdinfo_t *bd; struct disk_devdesc *dev; va_list ap; int rc; va_start(ap, f); dev = va_arg(ap, struct disk_devdesc *); va_end(ap); bd = bd_get_bdinfo(&dev->dd); if (bd == NULL) return (EIO); if ((bd->bd_flags & BD_NO_MEDIA) == BD_NO_MEDIA) { if (!bd_int13probe(bd)) return (EIO); if ((bd->bd_flags & BD_NO_MEDIA) == BD_NO_MEDIA) return (EIO); } if (bd->bd_bcache == NULL) bd->bd_bcache = bcache_allocate(); if (bd->bd_open == 0) bd->bd_sectors = bd_disk_get_sectors(dev); bd->bd_open++; rc = 0; if (dev->dd.d_dev->dv_type == DEVT_DISK) { rc = disk_open(dev, bd->bd_sectors * bd->bd_sectorsize, bd->bd_sectorsize); if (rc != 0) { bd->bd_open--; if (bd->bd_open == 0) { bcache_free(bd->bd_bcache); bd->bd_bcache = NULL; } } } return (rc); } static int bd_close(struct open_file *f) { struct disk_devdesc *dev; bdinfo_t *bd; int rc = 0; dev = (struct disk_devdesc *)f->f_devdata; bd = bd_get_bdinfo(&dev->dd); if (bd == NULL) return (EIO); bd->bd_open--; if (bd->bd_open == 0) { bcache_free(bd->bd_bcache); bd->bd_bcache = NULL; } if (dev->dd.d_dev->dv_type == DEVT_DISK) rc = disk_close(dev); return (rc); } static int bd_ioctl(struct open_file *f, u_long cmd, void *data) { bdinfo_t *bd; struct disk_devdesc *dev; int rc; dev = (struct disk_devdesc *)f->f_devdata; bd = bd_get_bdinfo(&dev->dd); if (bd == NULL) return (EIO); if (dev->dd.d_dev->dv_type == DEVT_DISK) { rc = disk_ioctl(dev, cmd, data); if (rc != ENOTTY) return (rc); } switch (cmd) { case DIOCGSECTORSIZE: *(uint32_t *)data = bd->bd_sectorsize; break; case DIOCGMEDIASIZE: *(uint64_t *)data = bd->bd_sectors * bd->bd_sectorsize; break; default: return (ENOTTY); } return (0); } static int bd_strategy(void *devdata, int rw, daddr_t dblk, size_t size, char *buf, size_t *rsize) { bdinfo_t *bd; struct bcache_devdata bcd; struct disk_devdesc *dev; daddr_t offset; dev = (struct disk_devdesc *)devdata; bd = bd_get_bdinfo(&dev->dd); if (bd == NULL) return (EINVAL); bcd.dv_strategy = bd_realstrategy; bcd.dv_devdata = devdata; bcd.dv_cache = bd->bd_bcache; offset = 0; if (dev->dd.d_dev->dv_type == DEVT_DISK) { offset = dev->d_offset * bd->bd_sectorsize; offset /= BIOSDISK_SECSIZE; } return (bcache_strategy(&bcd, rw, dblk + offset, size, buf, rsize)); } static int bd_realstrategy(void *devdata, int rw, daddr_t dblk, size_t size, char *buf, size_t *rsize) { struct disk_devdesc *dev = (struct disk_devdesc *)devdata; bdinfo_t *bd; uint64_t disk_blocks, offset, d_offset; size_t blks, blkoff, bsize, bio_size, rest; caddr_t bbuf = NULL; int rc; bd = bd_get_bdinfo(&dev->dd); if (bd == NULL || (bd->bd_flags & BD_NO_MEDIA) == BD_NO_MEDIA) return (EIO); /* * First make sure the IO size is a multiple of 512 bytes. While we do * process partial reads below, the strategy mechanism is built * assuming IO is a multiple of 512B blocks. If the request is not * a multiple of 512B blocks, it has to be some sort of bug. */ if (size == 0 || (size % BIOSDISK_SECSIZE) != 0) { printf("bd_strategy: %d bytes I/O not multiple of %d\n", size, BIOSDISK_SECSIZE); return (EIO); } DEBUG("open_disk %p", dev); offset = dblk * BIOSDISK_SECSIZE; dblk = offset / bd->bd_sectorsize; blkoff = offset % bd->bd_sectorsize; /* * Check the value of the size argument. We do have quite small * heap (64MB), but we do not know good upper limit, so we check against * INT_MAX here. This will also protect us against possible overflows * while translating block count to bytes. */ if (size > INT_MAX) { DEBUG("too large I/O: %zu bytes", size); return (EIO); } blks = size / bd->bd_sectorsize; if (blks == 0 || (size % bd->bd_sectorsize) != 0) blks++; if (dblk > dblk + blks) return (EIO); if (rsize) *rsize = 0; /* * Get disk blocks, this value is either for whole disk or for * partition. */ d_offset = 0; disk_blocks = 0; if (dev->dd.d_dev->dv_type == DEVT_DISK) { if (disk_ioctl(dev, DIOCGMEDIASIZE, &disk_blocks) == 0) { /* DIOCGMEDIASIZE does return bytes. */ disk_blocks /= bd->bd_sectorsize; } d_offset = dev->d_offset; } if (disk_blocks == 0) disk_blocks = bd->bd_sectors - d_offset; /* Validate source block address. */ if (dblk < d_offset || dblk >= d_offset + disk_blocks) return (EIO); /* * Truncate if we are crossing disk or partition end. */ if (dblk + blks >= d_offset + disk_blocks) { blks = d_offset + disk_blocks - dblk; size = blks * bd->bd_sectorsize; DEBUG("short I/O %d", blks); } bio_size = min(BIO_BUFFER_SIZE, size); while (bio_size > bd->bd_sectorsize) { bbuf = bio_alloc(bio_size); if (bbuf != NULL) break; bio_size -= bd->bd_sectorsize; } if (bbuf == NULL) { bio_size = V86_IO_BUFFER_SIZE; if (bio_size / bd->bd_sectorsize == 0) panic("BUG: Real mode buffer is too small"); /* Use alternate 4k buffer */ bbuf = PTOV(V86_IO_BUFFER); } rest = size; rc = 0; while (blks > 0) { int x = min(blks, bio_size / bd->bd_sectorsize); switch (rw & F_MASK) { case F_READ: DEBUG("read %d from %lld to %p", x, dblk, buf); bsize = bd->bd_sectorsize * x - blkoff; if (rest < bsize) bsize = rest; if ((rc = bd_io(dev, bd, dblk, x, bbuf, BD_RD)) != 0) { rc = EIO; goto error; } bcopy(bbuf + blkoff, buf, bsize); break; case F_WRITE : DEBUG("write %d from %lld to %p", x, dblk, buf); if (blkoff != 0) { /* * We got offset to sector, read 1 sector to * bbuf. */ x = 1; bsize = bd->bd_sectorsize - blkoff; bsize = min(bsize, rest); rc = bd_io(dev, bd, dblk, x, bbuf, BD_RD); } else if (rest < bd->bd_sectorsize) { /* * The remaining block is not full * sector. Read 1 sector to bbuf. */ x = 1; bsize = rest; rc = bd_io(dev, bd, dblk, x, bbuf, BD_RD); } else { /* We can write full sector(s). */ bsize = bd->bd_sectorsize * x; } /* * Put your Data In, Put your Data out, * Put your Data In, and shake it all about */ bcopy(buf, bbuf + blkoff, bsize); if ((rc = bd_io(dev, bd, dblk, x, bbuf, BD_WR)) != 0) { rc = EIO; goto error; } break; default: /* DO NOTHING */ rc = EROFS; goto error; } blkoff = 0; buf += bsize; rest -= bsize; blks -= x; dblk += x; } if (rsize != NULL) *rsize = size; error: if (bbuf != PTOV(V86_IO_BUFFER)) bio_free(bbuf, bio_size); return (rc); } static int bd_edd_io(bdinfo_t *bd, daddr_t dblk, int blks, caddr_t dest, int dowrite) { static struct edd_packet packet; packet.len = sizeof(struct edd_packet); packet.count = blks; packet.off = VTOPOFF(dest); packet.seg = VTOPSEG(dest); packet.lba = dblk; v86.ctl = V86_FLAGS; v86.addr = 0x13; /* Should we Write with verify ?? 0x4302 ? */ if (dowrite == BD_WR) v86.eax = 0x4300; else v86.eax = 0x4200; v86.edx = bd->bd_unit; v86.ds = VTOPSEG(&packet); v86.esi = VTOPOFF(&packet); v86int(); if (V86_CY(v86.efl)) return (v86.eax >> 8); return (0); } static int bd_chs_io(bdinfo_t *bd, daddr_t dblk, int blks, caddr_t dest, int dowrite) { uint32_t x, bpc, cyl, hd, sec; bpc = bd->bd_sec * bd->bd_hds; /* blocks per cylinder */ x = dblk; cyl = x / bpc; /* block # / blocks per cylinder */ x %= bpc; /* block offset into cylinder */ hd = x / bd->bd_sec; /* offset / blocks per track */ sec = x % bd->bd_sec; /* offset into track */ /* correct sector number for 1-based BIOS numbering */ sec++; if (cyl > 1023) { /* CHS doesn't support cylinders > 1023. */ return (1); } v86.ctl = V86_FLAGS; v86.addr = 0x13; if (dowrite == BD_WR) v86.eax = 0x300 | blks; else v86.eax = 0x200 | blks; v86.ecx = ((cyl & 0xff) << 8) | ((cyl & 0x300) >> 2) | sec; v86.edx = (hd << 8) | bd->bd_unit; v86.es = VTOPSEG(dest); v86.ebx = VTOPOFF(dest); v86int(); if (V86_CY(v86.efl)) return (v86.eax >> 8); return (0); } static void bd_io_workaround(bdinfo_t *bd) { uint8_t buf[8 * 1024]; bd_edd_io(bd, 0xffffffff, 1, (caddr_t)buf, BD_RD); } static int bd_io(struct disk_devdesc *dev, bdinfo_t *bd, daddr_t dblk, int blks, caddr_t dest, int dowrite) { int result, retry; /* Just in case some idiot actually tries to read/write -1 blocks... */ if (blks < 0) return (-1); /* * Workaround for a problem with some HP ProLiant BIOS failing to work * out the boot disk after installation. hrs and kuriyama discovered * this problem with an HP ProLiant DL320e Gen 8 with a 3TB HDD, and * discovered that an int13h call seems to cause a buffer overrun in * the bios. The problem is alleviated by doing an extra read before * the buggy read. It is not immediately known whether other models * are similarly affected. * Loop retrying the operation a couple of times. The BIOS * may also retry. */ if (dowrite == BD_RD && dblk >= 0x100000000) bd_io_workaround(bd); for (retry = 0; retry < 3; retry++) { if (bd->bd_flags & BD_MODEEDD) result = bd_edd_io(bd, dblk, blks, dest, dowrite); else result = bd_chs_io(bd, dblk, blks, dest, dowrite); if (result == 0) { if (bd->bd_flags & BD_NO_MEDIA) bd->bd_flags &= ~BD_NO_MEDIA; break; } bd_reset_disk(bd->bd_unit); /* * Error codes: * 20h controller failure * 31h no media in drive (IBM/MS INT 13 extensions) * 80h no media in drive, VMWare (Fusion) * There is no reason to repeat the IO with errors above. */ if (result == 0x20 || result == 0x31 || result == 0x80) { bd->bd_flags |= BD_NO_MEDIA; break; } } if (result != 0 && (bd->bd_flags & BD_NO_MEDIA) == 0) { if (dowrite == BD_WR) { printf("%s%d: Write %d sector(s) from %p (0x%x) " "to %lld: 0x%x\n", dev->dd.d_dev->dv_name, dev->dd.d_unit, blks, dest, VTOP(dest), dblk, result); } else { printf("%s%d: Read %d sector(s) from %lld to %p " "(0x%x): 0x%x\n", dev->dd.d_dev->dv_name, dev->dd.d_unit, blks, dblk, dest, VTOP(dest), result); } } return (result); } /* * Return the BIOS geometry of a given "fixed drive" in a format * suitable for the legacy bootinfo structure. Since the kernel is * expecting raw int 0x13/0x8 values for N_BIOS_GEOM drives, we * prefer to get the information directly, rather than rely on being * able to put it together from information already maintained for * different purposes and for a probably different number of drives. * * For valid drives, the geometry is expected in the format (31..0) * "000000cc cccccccc hhhhhhhh 00ssssss"; and invalid drives are * indicated by returning the geometry of a "1.2M" PC-format floppy * disk. And, incidentally, what is returned is not the geometry as * such but the highest valid cylinder, head, and sector numbers. */ uint32_t bd_getbigeom(int bunit) { v86.ctl = V86_FLAGS; v86.addr = 0x13; v86.eax = 0x800; v86.edx = 0x80 + bunit; v86int(); if (V86_CY(v86.efl)) return (0x4f010f); return (((v86.ecx & 0xc0) << 18) | ((v86.ecx & 0xff00) << 8) | (v86.edx & 0xff00) | (v86.ecx & 0x3f)); } /* * Return a suitable dev_t value for (dev). * * In the case where it looks like (dev) is a SCSI disk, we allow the number of * IDE disks to be specified in $num_ide_disks. There should be a Better Way. */ int bd_getdev(struct i386_devdesc *d) { struct disk_devdesc *dev; bdinfo_t *bd; int biosdev; int major; int rootdev; char *nip, *cp; int i, unit, slice, partition; /* XXX: Assume partition 'a'. */ slice = 0; partition = 0; dev = (struct disk_devdesc *)d; bd = bd_get_bdinfo(&dev->dd); if (bd == NULL) return (-1); biosdev = bd_unit2bios(d); DEBUG("unit %d BIOS device %d", dev->dd.d_unit, biosdev); if (biosdev == -1) /* not a BIOS device */ return (-1); if (dev->dd.d_dev->dv_type == DEVT_DISK) { if (disk_open(dev, bd->bd_sectors * bd->bd_sectorsize, bd->bd_sectorsize) != 0) /* oops, not a viable device */ return (-1); else disk_close(dev); slice = dev->d_slice + 1; partition = dev->d_partition; } if (biosdev < 0x80) { /* floppy (or emulated floppy) or ATAPI device */ if (bd->bd_type == DT_ATAPI) { /* is an ATAPI disk */ major = WFDMAJOR; } else { /* is a floppy disk */ major = FDMAJOR; } } else { /* assume an IDE disk */ major = WDMAJOR; } /* default root disk unit number */ unit = biosdev & 0x7f; if (dev->dd.d_dev->dv_type == DEVT_CD) { /* * XXX: Need to examine device spec here to figure out if * SCSI or ATAPI. No idea on how to figure out device number. * All we can really pass to the kernel is what bus and device * on which bus we were booted from, which dev_t isn't well * suited to since those number don't match to unit numbers * very well. We may just need to engage in a hack where * we pass -C to the boot args if we are the boot device. */ major = ACDMAJOR; unit = 0; /* XXX */ } /* XXX a better kludge to set the root disk unit number */ if ((nip = getenv("root_disk_unit")) != NULL) { i = strtol(nip, &cp, 0); /* check for parse error */ if ((cp != nip) && (*cp == 0)) unit = i; } rootdev = MAKEBOOTDEV(major, slice, unit, partition); DEBUG("dev is 0x%x\n", rootdev); return (rootdev); } Index: head/stand/libsa/zfs/zfs.c =================================================================== --- head/stand/libsa/zfs/zfs.c (revision 345476) +++ head/stand/libsa/zfs/zfs.c (revision 345477) @@ -1,1066 +1,1066 @@ /*- * Copyright (c) 2007 Doug Rabson * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * * $FreeBSD$ */ #include __FBSDID("$FreeBSD$"); /* * Stand-alone file reading package. */ #include #include #include #include #include #include #include #include #include #include #include #include "libzfs.h" #include "zfsimpl.c" /* Define the range of indexes to be populated with ZFS Boot Environments */ #define ZFS_BE_FIRST 4 #define ZFS_BE_LAST 8 static int zfs_open(const char *path, struct open_file *f); static int zfs_close(struct open_file *f); static int zfs_read(struct open_file *f, void *buf, size_t size, size_t *resid); static off_t zfs_seek(struct open_file *f, off_t offset, int where); static int zfs_stat(struct open_file *f, struct stat *sb); static int zfs_readdir(struct open_file *f, struct dirent *d); static void zfs_bootenv_initial(const char *); struct devsw zfs_dev; struct fs_ops zfs_fsops = { "zfs", zfs_open, zfs_close, zfs_read, null_write, zfs_seek, zfs_stat, zfs_readdir }; /* * In-core open file. */ struct file { off_t f_seekp; /* seek pointer */ dnode_phys_t f_dnode; uint64_t f_zap_type; /* zap type for readdir */ uint64_t f_num_leafs; /* number of fzap leaf blocks */ zap_leaf_phys_t *f_zap_leaf; /* zap leaf buffer */ }; static int zfs_env_index; static int zfs_env_count; SLIST_HEAD(zfs_be_list, zfs_be_entry) zfs_be_head = SLIST_HEAD_INITIALIZER(zfs_be_head); struct zfs_be_list *zfs_be_headp; struct zfs_be_entry { const char *name; SLIST_ENTRY(zfs_be_entry) entries; } *zfs_be, *zfs_be_tmp; /* * Open a file. */ static int zfs_open(const char *upath, struct open_file *f) { struct zfsmount *mount = (struct zfsmount *)f->f_devdata; struct file *fp; int rc; if (f->f_dev != &zfs_dev) return (EINVAL); /* allocate file system specific data structure */ fp = malloc(sizeof(struct file)); bzero(fp, sizeof(struct file)); f->f_fsdata = (void *)fp; rc = zfs_lookup(mount, upath, &fp->f_dnode); fp->f_seekp = 0; if (rc) { f->f_fsdata = NULL; free(fp); } return (rc); } static int zfs_close(struct open_file *f) { struct file *fp = (struct file *)f->f_fsdata; dnode_cache_obj = NULL; f->f_fsdata = (void *)0; if (fp == (struct file *)0) return (0); free(fp); return (0); } /* * Copy a portion of a file into kernel memory. * Cross block boundaries when necessary. */ static int zfs_read(struct open_file *f, void *start, size_t size, size_t *resid /* out */) { const spa_t *spa = ((struct zfsmount *)f->f_devdata)->spa; struct file *fp = (struct file *)f->f_fsdata; struct stat sb; size_t n; int rc; rc = zfs_stat(f, &sb); if (rc) return (rc); n = size; if (fp->f_seekp + n > sb.st_size) n = sb.st_size - fp->f_seekp; rc = dnode_read(spa, &fp->f_dnode, fp->f_seekp, start, n); if (rc) return (rc); if (0) { int i; for (i = 0; i < n; i++) putchar(((char*) start)[i]); } fp->f_seekp += n; if (resid) *resid = size - n; return (0); } static off_t zfs_seek(struct open_file *f, off_t offset, int where) { struct file *fp = (struct file *)f->f_fsdata; switch (where) { case SEEK_SET: fp->f_seekp = offset; break; case SEEK_CUR: fp->f_seekp += offset; break; case SEEK_END: { struct stat sb; int error; error = zfs_stat(f, &sb); if (error != 0) { errno = error; return (-1); } fp->f_seekp = sb.st_size - offset; break; } default: errno = EINVAL; return (-1); } return (fp->f_seekp); } static int zfs_stat(struct open_file *f, struct stat *sb) { const spa_t *spa = ((struct zfsmount *)f->f_devdata)->spa; struct file *fp = (struct file *)f->f_fsdata; return (zfs_dnode_stat(spa, &fp->f_dnode, sb)); } static int zfs_readdir(struct open_file *f, struct dirent *d) { const spa_t *spa = ((struct zfsmount *)f->f_devdata)->spa; struct file *fp = (struct file *)f->f_fsdata; mzap_ent_phys_t mze; struct stat sb; size_t bsize = fp->f_dnode.dn_datablkszsec << SPA_MINBLOCKSHIFT; int rc; rc = zfs_stat(f, &sb); if (rc) return (rc); if (!S_ISDIR(sb.st_mode)) return (ENOTDIR); /* * If this is the first read, get the zap type. */ if (fp->f_seekp == 0) { rc = dnode_read(spa, &fp->f_dnode, 0, &fp->f_zap_type, sizeof(fp->f_zap_type)); if (rc) return (rc); if (fp->f_zap_type == ZBT_MICRO) { fp->f_seekp = offsetof(mzap_phys_t, mz_chunk); } else { rc = dnode_read(spa, &fp->f_dnode, offsetof(zap_phys_t, zap_num_leafs), &fp->f_num_leafs, sizeof(fp->f_num_leafs)); if (rc) return (rc); fp->f_seekp = bsize; fp->f_zap_leaf = (zap_leaf_phys_t *)malloc(bsize); rc = dnode_read(spa, &fp->f_dnode, fp->f_seekp, fp->f_zap_leaf, bsize); if (rc) return (rc); } } if (fp->f_zap_type == ZBT_MICRO) { mzap_next: if (fp->f_seekp >= bsize) return (ENOENT); rc = dnode_read(spa, &fp->f_dnode, fp->f_seekp, &mze, sizeof(mze)); if (rc) return (rc); fp->f_seekp += sizeof(mze); if (!mze.mze_name[0]) goto mzap_next; d->d_fileno = ZFS_DIRENT_OBJ(mze.mze_value); d->d_type = ZFS_DIRENT_TYPE(mze.mze_value); strcpy(d->d_name, mze.mze_name); d->d_namlen = strlen(d->d_name); return (0); } else { zap_leaf_t zl; zap_leaf_chunk_t *zc, *nc; int chunk; size_t namelen; char *p; uint64_t value; /* * Initialise this so we can use the ZAP size * calculating macros. */ zl.l_bs = ilog2(bsize); zl.l_phys = fp->f_zap_leaf; /* * Figure out which chunk we are currently looking at * and consider seeking to the next leaf. We use the * low bits of f_seekp as a simple chunk index. */ fzap_next: chunk = fp->f_seekp & (bsize - 1); if (chunk == ZAP_LEAF_NUMCHUNKS(&zl)) { fp->f_seekp = rounddown2(fp->f_seekp, bsize) + bsize; chunk = 0; /* * Check for EOF and read the new leaf. */ if (fp->f_seekp >= bsize * fp->f_num_leafs) return (ENOENT); rc = dnode_read(spa, &fp->f_dnode, fp->f_seekp, fp->f_zap_leaf, bsize); if (rc) return (rc); } zc = &ZAP_LEAF_CHUNK(&zl, chunk); fp->f_seekp++; if (zc->l_entry.le_type != ZAP_CHUNK_ENTRY) goto fzap_next; namelen = zc->l_entry.le_name_numints; if (namelen > sizeof(d->d_name)) namelen = sizeof(d->d_name); /* * Paste the name back together. */ nc = &ZAP_LEAF_CHUNK(&zl, zc->l_entry.le_name_chunk); p = d->d_name; while (namelen > 0) { int len; len = namelen; if (len > ZAP_LEAF_ARRAY_BYTES) len = ZAP_LEAF_ARRAY_BYTES; memcpy(p, nc->l_array.la_array, len); p += len; namelen -= len; nc = &ZAP_LEAF_CHUNK(&zl, nc->l_array.la_next); } d->d_name[sizeof(d->d_name) - 1] = 0; /* * Assume the first eight bytes of the value are * a uint64_t. */ value = fzap_leaf_value(&zl, zc); d->d_fileno = ZFS_DIRENT_OBJ(value); d->d_type = ZFS_DIRENT_TYPE(value); d->d_namlen = strlen(d->d_name); return (0); } } static int vdev_read(vdev_t *vdev, void *priv, off_t offset, void *buf, size_t bytes) { int fd, ret; size_t res, head, tail, total_size, full_sec_size; unsigned secsz, do_tail_read; off_t start_sec; char *outbuf, *bouncebuf; fd = (uintptr_t) priv; outbuf = (char *) buf; bouncebuf = NULL; ret = ioctl(fd, DIOCGSECTORSIZE, &secsz); if (ret != 0) return (ret); /* * Handling reads of arbitrary offset and size - multi-sector case * and single-sector case. * * Multi-sector Case * (do_tail_read = true if tail > 0) * * |<----------------------total_size--------------------->| * | | * |<--head-->|<--------------bytes------------>|<--tail-->| * | | | | * | | |<~full_sec_size~>| | | * +------------------+ +------------------+ * | |0101010| . . . |0101011| | * +------------------+ +------------------+ * start_sec start_sec + n * * * Single-sector Case * (do_tail_read = false) * * |<------total_size = secsz----->| * | | * |<-head->|<---bytes--->|<-tail->| * +-------------------------------+ * | |0101010101010| | * +-------------------------------+ * start_sec */ start_sec = offset / secsz; head = offset % secsz; total_size = roundup2(head + bytes, secsz); tail = total_size - (head + bytes); do_tail_read = ((tail > 0) && (head + bytes > secsz)); full_sec_size = total_size; if (head > 0) full_sec_size -= secsz; if (do_tail_read) full_sec_size -= secsz; /* Return of partial sector data requires a bounce buffer. */ if ((head > 0) || do_tail_read) { bouncebuf = zfs_alloc(secsz); if (bouncebuf == NULL) { printf("vdev_read: out of memory\n"); return (ENOMEM); } } if (lseek(fd, start_sec * secsz, SEEK_SET) == -1) return (errno); /* Partial data return from first sector */ if (head > 0) { res = read(fd, bouncebuf, secsz); if (res != secsz) { ret = EIO; goto error; } memcpy(outbuf, bouncebuf + head, min(secsz - head, bytes)); outbuf += min(secsz - head, bytes); } /* Full data return from read sectors */ if (full_sec_size > 0) { res = read(fd, outbuf, full_sec_size); if (res != full_sec_size) { ret = EIO; goto error; } outbuf += full_sec_size; } /* Partial data return from last sector */ if (do_tail_read) { res = read(fd, bouncebuf, secsz); if (res != secsz) { ret = EIO; goto error; } memcpy(outbuf, bouncebuf, secsz - tail); } ret = 0; error: if (bouncebuf != NULL) zfs_free(bouncebuf, secsz); return (ret); } static int zfs_dev_init(void) { spa_t *spa; spa_t *next; spa_t *prev; zfs_init(); if (archsw.arch_zfs_probe == NULL) return (ENXIO); archsw.arch_zfs_probe(); prev = NULL; spa = STAILQ_FIRST(&zfs_pools); while (spa != NULL) { next = STAILQ_NEXT(spa, spa_link); if (zfs_spa_init(spa)) { if (prev == NULL) STAILQ_REMOVE_HEAD(&zfs_pools, spa_link); else STAILQ_REMOVE_AFTER(&zfs_pools, prev, spa_link); } else prev = spa; spa = next; } return (0); } struct zfs_probe_args { int fd; const char *devname; uint64_t *pool_guid; u_int secsz; }; static int zfs_diskread(void *arg, void *buf, size_t blocks, uint64_t offset) { struct zfs_probe_args *ppa; ppa = (struct zfs_probe_args *)arg; return (vdev_read(NULL, (void *)(uintptr_t)ppa->fd, offset * ppa->secsz, buf, blocks * ppa->secsz)); } static int zfs_probe(int fd, uint64_t *pool_guid) { spa_t *spa; int ret; spa = NULL; ret = vdev_probe(vdev_read, (void *)(uintptr_t)fd, &spa); if (ret == 0 && pool_guid != NULL) *pool_guid = spa->spa_guid; return (ret); } static int zfs_probe_partition(void *arg, const char *partname, const struct ptable_entry *part) { struct zfs_probe_args *ppa, pa; struct ptable *table; char devname[32]; int ret; /* Probe only freebsd-zfs and freebsd partitions */ if (part->type != PART_FREEBSD && part->type != PART_FREEBSD_ZFS) return (0); ppa = (struct zfs_probe_args *)arg; strncpy(devname, ppa->devname, strlen(ppa->devname) - 1); devname[strlen(ppa->devname) - 1] = '\0'; sprintf(devname, "%s%s:", devname, partname); pa.fd = open(devname, O_RDONLY); if (pa.fd == -1) return (0); ret = zfs_probe(pa.fd, ppa->pool_guid); if (ret == 0) return (0); /* Do we have BSD label here? */ if (part->type == PART_FREEBSD) { pa.devname = devname; pa.pool_guid = ppa->pool_guid; pa.secsz = ppa->secsz; table = ptable_open(&pa, part->end - part->start + 1, ppa->secsz, zfs_diskread); if (table != NULL) { ptable_iterate(table, &pa, zfs_probe_partition); ptable_close(table); } } close(pa.fd); return (0); } int zfs_probe_dev(const char *devname, uint64_t *pool_guid) { struct disk_devdesc *dev; struct ptable *table; struct zfs_probe_args pa; uint64_t mediasz; int ret; if (pool_guid) *pool_guid = 0; pa.fd = open(devname, O_RDONLY); if (pa.fd == -1) return (ENXIO); /* * We will not probe the whole disk, we can not boot from such * disks and some systems will misreport the disk sizes and will * hang while accessing the disk. */ if (archsw.arch_getdev((void **)&dev, devname, NULL) == 0) { int partition = dev->d_partition; int slice = dev->d_slice; free(dev); - if (partition != -1 && slice != -1) { + if (partition != D_PARTNONE && slice != D_SLICENONE) { ret = zfs_probe(pa.fd, pool_guid); if (ret == 0) return (0); } } /* Probe each partition */ ret = ioctl(pa.fd, DIOCGMEDIASIZE, &mediasz); if (ret == 0) ret = ioctl(pa.fd, DIOCGSECTORSIZE, &pa.secsz); if (ret == 0) { pa.devname = devname; pa.pool_guid = pool_guid; table = ptable_open(&pa, mediasz / pa.secsz, pa.secsz, zfs_diskread); if (table != NULL) { ptable_iterate(table, &pa, zfs_probe_partition); ptable_close(table); } } close(pa.fd); if (pool_guid && *pool_guid == 0) ret = ENXIO; return (ret); } /* * Print information about ZFS pools */ static int zfs_dev_print(int verbose) { spa_t *spa; char line[80]; int ret = 0; if (STAILQ_EMPTY(&zfs_pools)) return (0); printf("%s devices:", zfs_dev.dv_name); if ((ret = pager_output("\n")) != 0) return (ret); if (verbose) { return (spa_all_status()); } STAILQ_FOREACH(spa, &zfs_pools, spa_link) { snprintf(line, sizeof(line), " zfs:%s\n", spa->spa_name); ret = pager_output(line); if (ret != 0) break; } return (ret); } /* * Attempt to open the pool described by (dev) for use by (f). */ static int zfs_dev_open(struct open_file *f, ...) { va_list args; struct zfs_devdesc *dev; struct zfsmount *mount; spa_t *spa; int rv; va_start(args, f); dev = va_arg(args, struct zfs_devdesc *); va_end(args); if (dev->pool_guid == 0) spa = STAILQ_FIRST(&zfs_pools); else spa = spa_find_by_guid(dev->pool_guid); if (!spa) return (ENXIO); mount = malloc(sizeof(*mount)); rv = zfs_mount(spa, dev->root_guid, mount); if (rv != 0) { free(mount); return (rv); } if (mount->objset.os_type != DMU_OST_ZFS) { printf("Unexpected object set type %ju\n", (uintmax_t)mount->objset.os_type); free(mount); return (EIO); } f->f_devdata = mount; free(dev); return (0); } static int zfs_dev_close(struct open_file *f) { free(f->f_devdata); f->f_devdata = NULL; return (0); } static int zfs_dev_strategy(void *devdata, int rw, daddr_t dblk, size_t size, char *buf, size_t *rsize) { return (ENOSYS); } struct devsw zfs_dev = { .dv_name = "zfs", .dv_type = DEVT_ZFS, .dv_init = zfs_dev_init, .dv_strategy = zfs_dev_strategy, .dv_open = zfs_dev_open, .dv_close = zfs_dev_close, .dv_ioctl = noioctl, .dv_print = zfs_dev_print, .dv_cleanup = NULL }; int zfs_parsedev(struct zfs_devdesc *dev, const char *devspec, const char **path) { static char rootname[ZFS_MAXNAMELEN]; static char poolname[ZFS_MAXNAMELEN]; spa_t *spa; const char *end; const char *np; const char *sep; int rv; np = devspec; if (*np != ':') return (EINVAL); np++; end = strrchr(np, ':'); if (end == NULL) return (EINVAL); sep = strchr(np, '/'); if (sep == NULL || sep >= end) sep = end; memcpy(poolname, np, sep - np); poolname[sep - np] = '\0'; if (sep < end) { sep++; memcpy(rootname, sep, end - sep); rootname[end - sep] = '\0'; } else rootname[0] = '\0'; spa = spa_find_by_name(poolname); if (!spa) return (ENXIO); dev->pool_guid = spa->spa_guid; rv = zfs_lookup_dataset(spa, rootname, &dev->root_guid); if (rv != 0) return (rv); if (path != NULL) *path = (*end == '\0') ? end : end + 1; dev->dd.d_dev = &zfs_dev; return (0); } char * zfs_fmtdev(void *vdev) { static char rootname[ZFS_MAXNAMELEN]; static char buf[2 * ZFS_MAXNAMELEN + 8]; struct zfs_devdesc *dev = (struct zfs_devdesc *)vdev; spa_t *spa; buf[0] = '\0'; if (dev->dd.d_dev->dv_type != DEVT_ZFS) return (buf); if (dev->pool_guid == 0) { spa = STAILQ_FIRST(&zfs_pools); dev->pool_guid = spa->spa_guid; } else spa = spa_find_by_guid(dev->pool_guid); if (spa == NULL) { printf("ZFS: can't find pool by guid\n"); return (buf); } if (dev->root_guid == 0 && zfs_get_root(spa, &dev->root_guid)) { printf("ZFS: can't find root filesystem\n"); return (buf); } if (zfs_rlookup(spa, dev->root_guid, rootname)) { printf("ZFS: can't find filesystem by guid\n"); return (buf); } if (rootname[0] == '\0') sprintf(buf, "%s:%s:", dev->dd.d_dev->dv_name, spa->spa_name); else sprintf(buf, "%s:%s/%s:", dev->dd.d_dev->dv_name, spa->spa_name, rootname); return (buf); } int zfs_list(const char *name) { static char poolname[ZFS_MAXNAMELEN]; uint64_t objid; spa_t *spa; const char *dsname; int len; int rv; len = strlen(name); dsname = strchr(name, '/'); if (dsname != NULL) { len = dsname - name; dsname++; } else dsname = ""; memcpy(poolname, name, len); poolname[len] = '\0'; spa = spa_find_by_name(poolname); if (!spa) return (ENXIO); rv = zfs_lookup_dataset(spa, dsname, &objid); if (rv != 0) return (rv); return (zfs_list_dataset(spa, objid)); } void init_zfs_bootenv(const char *currdev_in) { char *beroot, *currdev; int currdev_len; currdev = NULL; currdev_len = strlen(currdev_in); if (currdev_len == 0) return; if (strncmp(currdev_in, "zfs:", 4) != 0) return; currdev = strdup(currdev_in); if (currdev == NULL) return; /* Remove the trailing : */ currdev[currdev_len - 1] = '\0'; setenv("zfs_be_active", currdev, 1); setenv("zfs_be_currpage", "1", 1); /* Remove the last element (current bootenv) */ beroot = strrchr(currdev, '/'); if (beroot != NULL) beroot[0] = '\0'; beroot = strchr(currdev, ':') + 1; setenv("zfs_be_root", beroot, 1); zfs_bootenv_initial(beroot); free(currdev); } static void zfs_bootenv_initial(const char *name) { char poolname[ZFS_MAXNAMELEN], *dsname; char envname[32], envval[256]; uint64_t objid; spa_t *spa; int bootenvs_idx, len, rv; SLIST_INIT(&zfs_be_head); zfs_env_count = 0; len = strlen(name); dsname = strchr(name, '/'); if (dsname != NULL) { len = dsname - name; dsname++; } else dsname = ""; strlcpy(poolname, name, len + 1); spa = spa_find_by_name(poolname); if (spa == NULL) return; rv = zfs_lookup_dataset(spa, dsname, &objid); if (rv != 0) return; rv = zfs_callback_dataset(spa, objid, zfs_belist_add); bootenvs_idx = 0; /* Populate the initial environment variables */ SLIST_FOREACH_SAFE(zfs_be, &zfs_be_head, entries, zfs_be_tmp) { /* Enumerate all bootenvs for general usage */ snprintf(envname, sizeof(envname), "bootenvs[%d]", bootenvs_idx); snprintf(envval, sizeof(envval), "zfs:%s/%s", name, zfs_be->name); rv = setenv(envname, envval, 1); if (rv != 0) break; bootenvs_idx++; } snprintf(envval, sizeof(envval), "%d", bootenvs_idx); setenv("bootenvs_count", envval, 1); /* Clean up the SLIST of ZFS BEs */ while (!SLIST_EMPTY(&zfs_be_head)) { zfs_be = SLIST_FIRST(&zfs_be_head); SLIST_REMOVE_HEAD(&zfs_be_head, entries); free(zfs_be); } return; } int zfs_bootenv(const char *name) { static char poolname[ZFS_MAXNAMELEN], *dsname, *root; char becount[4]; uint64_t objid; spa_t *spa; int len, rv, pages, perpage, currpage; if (name == NULL) return (EINVAL); if ((root = getenv("zfs_be_root")) == NULL) return (EINVAL); if (strcmp(name, root) != 0) { if (setenv("zfs_be_root", name, 1) != 0) return (ENOMEM); } SLIST_INIT(&zfs_be_head); zfs_env_count = 0; len = strlen(name); dsname = strchr(name, '/'); if (dsname != NULL) { len = dsname - name; dsname++; } else dsname = ""; memcpy(poolname, name, len); poolname[len] = '\0'; spa = spa_find_by_name(poolname); if (!spa) return (ENXIO); rv = zfs_lookup_dataset(spa, dsname, &objid); if (rv != 0) return (rv); rv = zfs_callback_dataset(spa, objid, zfs_belist_add); /* Calculate and store the number of pages of BEs */ perpage = (ZFS_BE_LAST - ZFS_BE_FIRST + 1); pages = (zfs_env_count / perpage) + ((zfs_env_count % perpage) > 0 ? 1 : 0); snprintf(becount, 4, "%d", pages); if (setenv("zfs_be_pages", becount, 1) != 0) return (ENOMEM); /* Roll over the page counter if it has exceeded the maximum */ currpage = strtol(getenv("zfs_be_currpage"), NULL, 10); if (currpage > pages) { if (setenv("zfs_be_currpage", "1", 1) != 0) return (ENOMEM); } /* Populate the menu environment variables */ zfs_set_env(); /* Clean up the SLIST of ZFS BEs */ while (!SLIST_EMPTY(&zfs_be_head)) { zfs_be = SLIST_FIRST(&zfs_be_head); SLIST_REMOVE_HEAD(&zfs_be_head, entries); free(zfs_be); } return (rv); } int zfs_belist_add(const char *name, uint64_t value __unused) { /* Skip special datasets that start with a $ character */ if (strncmp(name, "$", 1) == 0) { return (0); } /* Add the boot environment to the head of the SLIST */ zfs_be = malloc(sizeof(struct zfs_be_entry)); if (zfs_be == NULL) { return (ENOMEM); } zfs_be->name = name; SLIST_INSERT_HEAD(&zfs_be_head, zfs_be, entries); zfs_env_count++; return (0); } int zfs_set_env(void) { char envname[32], envval[256]; char *beroot, *pagenum; int rv, page, ctr; beroot = getenv("zfs_be_root"); if (beroot == NULL) { return (1); } pagenum = getenv("zfs_be_currpage"); if (pagenum != NULL) { page = strtol(pagenum, NULL, 10); } else { page = 1; } ctr = 1; rv = 0; zfs_env_index = ZFS_BE_FIRST; SLIST_FOREACH_SAFE(zfs_be, &zfs_be_head, entries, zfs_be_tmp) { /* Skip to the requested page number */ if (ctr <= ((ZFS_BE_LAST - ZFS_BE_FIRST + 1) * (page - 1))) { ctr++; continue; } snprintf(envname, sizeof(envname), "bootenvmenu_caption[%d]", zfs_env_index); snprintf(envval, sizeof(envval), "%s", zfs_be->name); rv = setenv(envname, envval, 1); if (rv != 0) { break; } snprintf(envname, sizeof(envname), "bootenvansi_caption[%d]", zfs_env_index); rv = setenv(envname, envval, 1); if (rv != 0){ break; } snprintf(envname, sizeof(envname), "bootenvmenu_command[%d]", zfs_env_index); rv = setenv(envname, "set_bootenv", 1); if (rv != 0){ break; } snprintf(envname, sizeof(envname), "bootenv_root[%d]", zfs_env_index); snprintf(envval, sizeof(envval), "zfs:%s/%s", beroot, zfs_be->name); rv = setenv(envname, envval, 1); if (rv != 0){ break; } zfs_env_index++; if (zfs_env_index > ZFS_BE_LAST) { break; } } for (; zfs_env_index <= ZFS_BE_LAST; zfs_env_index++) { snprintf(envname, sizeof(envname), "bootenvmenu_caption[%d]", zfs_env_index); (void)unsetenv(envname); snprintf(envname, sizeof(envname), "bootenvansi_caption[%d]", zfs_env_index); (void)unsetenv(envname); snprintf(envname, sizeof(envname), "bootenvmenu_command[%d]", zfs_env_index); (void)unsetenv(envname); snprintf(envname, sizeof(envname), "bootenv_root[%d]", zfs_env_index); (void)unsetenv(envname); } return (rv); } Index: head/stand/mips/beri/loader/beri_disk_cfi.c =================================================================== --- head/stand/mips/beri/loader/beri_disk_cfi.c (revision 345476) +++ head/stand/mips/beri/loader/beri_disk_cfi.c (revision 345477) @@ -1,141 +1,141 @@ /*- * Copyright (c) 2013-2014 Robert N. M. Watson * All rights reserved. * * This software was developed by SRI International and the University of * Cambridge Computer Laboratory under DARPA/AFRL contract (FA8750-10-C-0237) * ("CTSRD"), as part of the DARPA CRASH research programme. * * 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 static int beri_cfi_disk_init(void); static int beri_cfi_disk_open(struct open_file *, ...); static int beri_cfi_disk_close(struct open_file *); static int beri_cfi_disk_strategy(void *, int, daddr_t, size_t, char *, size_t *); static int beri_cfi_disk_print(int); struct devsw beri_cfi_disk = { .dv_name = "cfi", .dv_type = DEVT_DISK, .dv_init = beri_cfi_disk_init, .dv_strategy = beri_cfi_disk_strategy, .dv_open = beri_cfi_disk_open, .dv_close = beri_cfi_disk_close, .dv_ioctl = noioctl, .dv_print = beri_cfi_disk_print, .dv_cleanup = NULL, }; static int beri_cfi_disk_init(void) { return (0); } static int beri_cfi_disk_strategy(void *devdata, int flag, daddr_t dblk, size_t size, char *buf, size_t *rsizep) { int error; flag &= F_MASK; if (flag == F_WRITE) return (EROFS); if (flag != F_READ) return (EINVAL); if (rsizep != NULL) *rsizep = 0; error = cfi_read(buf, dblk, size >> 9); if (error == 0 && rsizep != NULL) *rsizep = size; else if (error != 0) printf("%s: error %d\n", __func__, error); return (error); } static int beri_cfi_disk_open(struct open_file *f, ...) { va_list ap; struct disk_devdesc *dev; va_start(ap, f); dev = va_arg(ap, struct disk_devdesc *); va_end(ap); if (dev->dd.d_unit != 0) return (EIO); return (disk_open(dev, cfi_get_mediasize(), cfi_get_sectorsize())); } static int beri_cfi_disk_close(struct open_file *f) { struct disk_devdesc *dev; dev = (struct disk_devdesc *)f->f_devdata; return (disk_close(dev)); } static int beri_cfi_disk_print(int verbose) { struct disk_devdesc dev; char line[80]; int ret; printf("%s devices:", beri_cfi_disk.dv_name); if ((ret = pager_output("\n")) != 0) return (ret); snprintf(line, sizeof(line), " cfi%d CFI flash device\n", 0); ret = pager_output(line); if (ret != 0) return (ret); dev.dd.d_dev = &beri_cfi_disk; dev.dd.d_unit = 0; - dev.d_slice = -1; - dev.d_partition = -1; + dev.d_slice = D_SLICENONE; + dev.d_partition = D_PARTNONE; if (disk_open(&dev, cfi_get_mediasize(), cfi_get_sectorsize()) == 0) { snprintf(line, sizeof(line), " cfi%d", 0); ret = disk_print(&dev, line, verbose); disk_close(&dev); } return (ret); } Index: head/stand/mips/beri/loader/beri_disk_sdcard.c =================================================================== --- head/stand/mips/beri/loader/beri_disk_sdcard.c (revision 345476) +++ head/stand/mips/beri/loader/beri_disk_sdcard.c (revision 345477) @@ -1,147 +1,147 @@ /*- * Copyright (c) 2013-2014 Robert N. M. Watson * All rights reserved. * * This software was developed by SRI International and the University of * Cambridge Computer Laboratory under DARPA/AFRL contract (FA8750-10-C-0237) * ("CTSRD"), as part of the DARPA CRASH research programme. * * 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 static int beri_sdcard_disk_init(void); static int beri_sdcard_disk_open(struct open_file *, ...); static int beri_sdcard_disk_close(struct open_file *); static int beri_sdcard_disk_strategy(void *, int, daddr_t, size_t, char *, size_t *); static int beri_sdcard_disk_print(int); struct devsw beri_sdcard_disk = { .dv_name = "sdcard", .dv_type = DEVT_DISK, .dv_init = beri_sdcard_disk_init, .dv_strategy = beri_sdcard_disk_strategy, .dv_open = beri_sdcard_disk_open, .dv_close = beri_sdcard_disk_close, .dv_ioctl = noioctl, .dv_print = beri_sdcard_disk_print, .dv_cleanup = NULL, }; static int beri_sdcard_disk_init(void) { return (0); } static int beri_sdcard_disk_strategy(void *devdata, int flag, daddr_t dblk, size_t size, char *buf, size_t *rsizep) { int error; flag &= F_MASK; if (flag == F_WRITE) return (EROFS); if (flag != F_READ) return (EINVAL); if (rsizep != NULL) *rsizep = 0; error = altera_sdcard_read(buf, dblk, size >> 9); if (error == 0 && rsizep != NULL) *rsizep = size; else if (error != 0) printf("%s: error %d\n", __func__, error); return (error); } static int beri_sdcard_disk_open(struct open_file *f, ...) { va_list ap; struct disk_devdesc *dev; va_start(ap, f); dev = va_arg(ap, struct disk_devdesc *); va_end(ap); if (!(altera_sdcard_get_present())) { printf("SD card not present or not supported\n"); return (ENXIO); } if (dev->dd.d_unit != 0) return (EIO); return (disk_open(dev, altera_sdcard_get_mediasize(), altera_sdcard_get_sectorsize())); } static int beri_sdcard_disk_close(struct open_file *f) { struct disk_devdesc *dev; dev = (struct disk_devdesc *)f->f_devdata; return (disk_close(dev)); } static int beri_sdcard_disk_print(int verbose) { struct disk_devdesc dev; char line[80]; int ret; printf("%s devices:", beri_sdcard_disk.dv_name); if ((ret = pager_output("\n")) != 0) return (ret); snprintf(line, sizeof(line), " sdcard%d Altera SD card drive\n", 0); ret = pager_output(line); if (ret != 0) return (ret); dev.dd.d_dev = &beri_sdcard_disk; dev.dd.d_unit = 0; - dev.d_slice = -1; - dev.d_partition = -1; + dev.d_slice = D_SLICENONE; + dev.d_partition = D_PARTNONE; if (disk_open(&dev, altera_sdcard_get_mediasize(), altera_sdcard_get_sectorsize()) == 0) { snprintf(line, sizeof(line), " sdcard%d", 0); ret = disk_print(&dev, line, verbose); disk_close(&dev); } return (ret); } Index: head/stand/uboot/common/main.c =================================================================== --- head/stand/uboot/common/main.c (revision 345476) +++ head/stand/uboot/common/main.c (revision 345477) @@ -1,718 +1,723 @@ /*- * 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 = DEV_TYP_NONE; return (NULL); } static const char * device_typename(int type) { int i; for (i = 0; i < nitems(device_types); i++) if (device_types[i].type == type) return (device_types[i].name); return (""); } /* * Parse a device string into type, unit, slice and partition numbers. A * returned value of -1 for type indicates a search should be done for the * first loadable device, otherwise a returned value of -1 for unit * indicates a search should be done for the first loadable device of the * given type. * * The returned values for slice and partition are interpreted by * disk_open(). * * The device string can be a standard loader(8) disk specifier: * * disks disk0s1 * disks disk1s2a * diskp disk0p4 * * or one of the following formats: * * Valid device strings: For device types: * * DEV_TYP_STOR, DEV_TYP_NET * DEV_TYP_STOR, DEV_TYP_NET * : DEV_TYP_STOR, DEV_TYP_NET * : DEV_TYP_STOR * :. DEV_TYP_STOR * :. DEV_TYP_STOR * * For valid type names, see the device_types array, above. * * Slice numbers are 1-based. 0 is a wildcard. */ static void get_load_device(int *type, int *unit, int *slice, int *partition) { struct disk_devdesc dev; char *devstr; const char *p; char *endp; *type = DEV_TYP_NONE; *unit = -1; - *slice = 0; - *partition = -1; + *slice = D_SLICEWILD; + *partition = D_PARTWILD; devstr = ub_env_get("loaderdev"); if (devstr == NULL) { printf("U-Boot env: loaderdev not set, will probe all devices.\n"); return; } printf("U-Boot env: loaderdev='%s'\n", devstr); p = get_device_type(devstr, type); /* * If type is DEV_TYP_STOR we have a disk-like device. If the remainder * of the string contains spaces, dots, or a colon in any location other * than the last char, it's legacy format. Otherwise it might be * standard loader(8) format (e.g., disk0s2a or mmc1p12), so try to * parse the remainder of the string as such, and if it works, return * those results. Otherwise we'll fall through to the code that parses * the legacy format. */ 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 == DEV_TYP_NONE) || (*p == '\0')) { return; } /* Malformed unit number. */ if (!isdigit(*p)) { *type = DEV_TYP_NONE; return; } /* Guaranteed to extract a number from the string, as *p is a digit. */ *unit = strtol(p, &endp, 10); p = endp; /* Known device name with unit number and nothing else. */ if (*p == '\0') { return; } /* Device string is malformed beyond unit number. */ if (*p != ':') { *type = DEV_TYP_NONE; *unit = -1; return; } p++; /* No slice and partition specification. */ if ('\0' == *p ) return; /* Only DEV_TYP_STOR devices can have a slice specification. */ if (!(*type & DEV_TYP_STOR)) { *type = DEV_TYP_NONE; *unit = -1; return; } *slice = strtoul(p, &endp, 10); /* Malformed slice number. */ if (p == endp) { *type = DEV_TYP_NONE; *unit = -1; - *slice = 0; + *slice = D_SLICEWILD; return; } p = endp; /* No partition specification. */ if (*p == '\0') return; /* Device string is malformed beyond slice number. */ if (*p != '.') { *type = DEV_TYP_NONE; *unit = -1; - *slice = 0; + *slice = D_SLICEWILD; return; } p++; /* No partition specification. */ if (*p == '\0') return; *partition = strtol(p, &endp, 10); p = endp; /* Full, valid device string. */ if (*endp == '\0') return; /* Junk beyond partition number. */ *type = DEV_TYP_NONE; *unit = -1; - *slice = 0; - *partition = -1; + *slice = D_SLICEWILD; + *partition = D_PARTWILD; } static void print_disk_probe_info() { char slice[32]; char partition[32]; - if (currdev.d_disk.d_slice > 0) - sprintf(slice, "%d", currdev.d_disk.d_slice); + if (currdev.d_disk.d_slice == D_SLICENONE) + strlcpy(slice, "", sizeof(slice)); + else if (currdev.d_disk.d_slice == D_SLICEWILD) + strlcpy(slice, "", sizeof(slice)); else - strcpy(slice, ""); + snprintf(slice, sizeof(slice), "%d", currdev.d_disk.d_slice); - if (currdev.d_disk.d_partition >= 0) - sprintf(partition, "%d", currdev.d_disk.d_partition); + if (currdev.d_disk.d_partition == D_PARTNONE) + strlcpy(partition, "", sizeof(partition)); + else if (currdev.d_disk.d_partition == D_PARTWILD) + strlcpy(partition, "", sizeof(partition)); else - strcpy(partition, ""); + snprintf(partition, sizeof(partition), "%d", + currdev.d_disk.d_partition); printf(" Checking unit=%d slice=%s partition=%s...", currdev.dd.d_unit, slice, partition); } static int probe_disks(int devidx, int load_type, int load_unit, int load_slice, int load_partition) { int open_result, unit; struct open_file f; currdev.d_disk.d_slice = load_slice; currdev.d_disk.d_partition = load_partition; f.f_devdata = &currdev; open_result = -1; if (load_type == -1) { printf(" Probing all disk devices...\n"); /* Try each disk in succession until one works. */ for (currdev.dd.d_unit = 0; currdev.dd.d_unit < UB_MAX_DEV; currdev.dd.d_unit++) { print_disk_probe_info(); open_result = devsw[devidx]->dv_open(&f, &currdev); if (open_result == 0) { printf(" good.\n"); return (0); } printf("\n"); } return (-1); } if (load_unit == -1) { printf(" Probing all %s devices...\n", device_typename(load_type)); /* Try each disk of given type in succession until one works. */ for (unit = 0; unit < UB_MAX_DEV; unit++) { currdev.dd.d_unit = uboot_diskgetunit(load_type, unit); if (currdev.dd.d_unit == -1) break; print_disk_probe_info(); open_result = devsw[devidx]->dv_open(&f, &currdev); if (open_result == 0) { printf(" good.\n"); return (0); } printf("\n"); } return (-1); } if ((currdev.dd.d_unit = uboot_diskgetunit(load_type, load_unit)) != -1) { print_disk_probe_info(); open_result = devsw[devidx]->dv_open(&f,&currdev); if (open_result == 0) { printf(" good.\n"); return (0); } printf("\n"); } printf(" Requested disk type/unit/slice/partition not found\n"); return (-1); } int main(int argc, char **argv) { struct api_signature *sig = NULL; int load_type, load_unit, load_slice, load_partition; int i; const char *ldev; /* * We first check if a command line argument was passed to us containing * API's signature address. If it wasn't then we try to search for the * API signature via the usual hinted address. * If we can't find the magic signature and related info, exit with a * unique error code that U-Boot reports as "## Application terminated, * rc = 0xnnbadab1". Hopefully 'badab1' looks enough like "bad api" to * provide a clue. It's better than 0xffffffff anyway. */ if (!api_parse_cmdline_sig(argc, argv, &sig) && !api_search_sig(&sig)) return (0x01badab1); syscall_ptr = sig->syscall; if (syscall_ptr == NULL) return (0x02badab1); if (sig->version > API_SIG_VERSION) return (0x03badab1); /* Clear BSS sections */ bzero(__sbss_start, __sbss_end - __sbss_start); bzero(__bss_start, _end - __bss_start); /* * Initialise the heap as early as possible. Once this is done, * alloc() is usable. We are using the stack u-boot set up near the top * of physical ram; hopefully there is sufficient space between the end * of our bss and the bottom of the u-boot stack to avoid overlap. */ uboot_heap_start = round_page((uintptr_t)end); uboot_heap_end = uboot_heap_start + HEAP_SIZE; setheap((void *)uboot_heap_start, (void *)uboot_heap_end); /* * Set up console. */ cons_probe(); printf("Compatible U-Boot API signature found @%p\n", sig); printf("\n%s", bootprog_info); printf("\n"); dump_sig(sig); dump_addr_info(); meminfo(); /* * Enumerate U-Boot devices */ if ((devs_no = ub_dev_enum()) == 0) { printf("no U-Boot devices found"); goto do_interact; } printf("Number of U-Boot devices: %d\n", devs_no); get_load_device(&load_type, &load_unit, &load_slice, &load_partition); /* * March through the device switch probing for things. */ for (i = 0; devsw[i] != NULL; i++) { if (devsw[i]->dv_init == NULL) continue; if ((devsw[i]->dv_init)() != 0) continue; printf("Found U-Boot device: %s\n", devsw[i]->dv_name); currdev.dd.d_dev = devsw[i]; currdev.dd.d_unit = 0; if ((load_type == DEV_TYP_NONE || (load_type & DEV_TYP_STOR)) && strcmp(devsw[i]->dv_name, "disk") == 0) { if (probe_disks(i, load_type, load_unit, load_slice, load_partition) == 0) break; } if ((load_type == DEV_TYP_NONE || (load_type & DEV_TYP_NET)) && strcmp(devsw[i]->dv_name, "net") == 0) break; } /* * If we couldn't find a boot device, return an error to u-boot. * U-boot may be running a boot script that can try something different * so returning an error is better than forcing a reboot. */ if (devsw[i] == NULL) { printf("No boot device found!\n"); return (0xbadef1ce); } ldev = 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); #ifdef __powerpc__ setenv("usefdt", "1", 1); #endif 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: head/stand/uboot/lib/disk.c =================================================================== --- head/stand/uboot/lib/disk.c (revision 345476) +++ head/stand/uboot/lib/disk.c (revision 345477) @@ -1,320 +1,320 @@ /*- * Copyright (c) 2008 Semihalf, Rafal Jaworowski * Copyright (c) 2009 Semihalf, Piotr Ziecik * Copyright (c) 2012 Andrey V. Elsukov * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * */ /* * Block storage I/O routines for U-Boot */ #include __FBSDID("$FreeBSD$"); #include #include #include #include #include "api_public.h" #include "bootstrap.h" #include "disk.h" #include "glue.h" #include "libuboot.h" #define stor_printf(fmt, args...) do { \ printf("%s%d: ", dev->dd.d_dev->dv_name, dev->dd.d_unit); \ printf(fmt, ##args); \ } while (0) #ifdef DEBUG #define debugf(fmt, args...) do { printf("%s(): ", __func__); \ printf(fmt,##args); } while (0) #else #define debugf(fmt, args...) #endif static struct { int opened; /* device is opened */ int handle; /* storage device handle */ int type; /* storage type */ off_t blocks; /* block count */ u_int bsize; /* block size */ } stor_info[UB_MAX_DEV]; #define SI(dev) (stor_info[(dev)->dd.d_unit]) static int stor_info_no = 0; static int stor_opendev(struct disk_devdesc *); static int stor_readdev(struct disk_devdesc *, daddr_t, size_t, char *); /* devsw I/F */ static int stor_init(void); static int stor_strategy(void *, int, daddr_t, size_t, char *, size_t *); static int stor_open(struct open_file *, ...); static int stor_close(struct open_file *); static int stor_ioctl(struct open_file *f, u_long cmd, void *data); static int stor_print(int); static void stor_cleanup(void); struct devsw uboot_storage = { "disk", DEVT_DISK, stor_init, stor_strategy, stor_open, stor_close, stor_ioctl, stor_print, stor_cleanup }; static int stor_init(void) { struct device_info *di; int i; if (devs_no == 0) { printf("No U-Boot devices! Really enumerated?\n"); return (-1); } for (i = 0; i < devs_no; i++) { di = ub_dev_get(i); if ((di != NULL) && (di->type & DEV_TYP_STOR)) { if (stor_info_no >= UB_MAX_DEV) { printf("Too many storage devices: %d\n", stor_info_no); return (-1); } stor_info[stor_info_no].handle = i; stor_info[stor_info_no].opened = 0; stor_info[stor_info_no].type = di->type; stor_info[stor_info_no].blocks = di->di_stor.block_count; stor_info[stor_info_no].bsize = di->di_stor.block_size; stor_info_no++; } } if (!stor_info_no) { debugf("No storage devices\n"); return (-1); } debugf("storage devices found: %d\n", stor_info_no); return (0); } static void stor_cleanup(void) { int i; for (i = 0; i < stor_info_no; i++) if (stor_info[i].opened > 0) ub_dev_close(stor_info[i].handle); } static int stor_strategy(void *devdata, int rw, daddr_t blk, size_t size, char *buf, size_t *rsize) { struct disk_devdesc *dev = (struct disk_devdesc *)devdata; daddr_t bcount; int err; rw &= F_MASK; if (rw != F_READ) { stor_printf("write attempt, operation not supported!\n"); return (EROFS); } if (size % SI(dev).bsize) { stor_printf("size=%zu not multiple of device " "block size=%d\n", size, SI(dev).bsize); return (EIO); } bcount = size / SI(dev).bsize; if (rsize) *rsize = 0; err = stor_readdev(dev, blk + dev->d_offset, bcount, buf); if (!err && rsize) *rsize = size; return (err); } static int stor_open(struct open_file *f, ...) { va_list ap; struct disk_devdesc *dev; va_start(ap, f); dev = va_arg(ap, struct disk_devdesc *); va_end(ap); return (stor_opendev(dev)); } static int stor_opendev(struct disk_devdesc *dev) { int err; if (dev->dd.d_unit < 0 || dev->dd.d_unit >= stor_info_no) return (EIO); if (SI(dev).opened == 0) { err = ub_dev_open(SI(dev).handle); if (err != 0) { stor_printf("device open failed with error=%d, " "handle=%d\n", err, SI(dev).handle); return (ENXIO); } SI(dev).opened++; } return (disk_open(dev, SI(dev).blocks * SI(dev).bsize, SI(dev).bsize)); } static int stor_close(struct open_file *f) { struct disk_devdesc *dev; dev = (struct disk_devdesc *)(f->f_devdata); return (disk_close(dev)); } static int stor_readdev(struct disk_devdesc *dev, daddr_t blk, size_t size, char *buf) { lbasize_t real_size; int err; debugf("reading blk=%d size=%d @ 0x%08x\n", (int)blk, size, (uint32_t)buf); err = ub_dev_read(SI(dev).handle, buf, size, blk, &real_size); if (err != 0) { stor_printf("read failed, error=%d\n", err); return (EIO); } if (real_size != size) { stor_printf("real size != size\n"); err = EIO; } return (err); } static int stor_print(int verbose) { struct disk_devdesc dev; static char line[80]; int i, ret = 0; if (stor_info_no == 0) return (ret); printf("%s devices:", uboot_storage.dv_name); if ((ret = pager_output("\n")) != 0) return (ret); for (i = 0; i < stor_info_no; i++) { dev.dd.d_dev = &uboot_storage; dev.dd.d_unit = i; - dev.d_slice = -1; - dev.d_partition = -1; + dev.d_slice = D_SLICENONE; + dev.d_partition = D_PARTNONE; snprintf(line, sizeof(line), "\tdisk%d (%s)\n", i, ub_stor_type(SI(&dev).type)); if ((ret = pager_output(line)) != 0) break; if (stor_opendev(&dev) == 0) { sprintf(line, "\tdisk%d", i); ret = disk_print(&dev, line, verbose); disk_close(&dev); if (ret != 0) break; } } return (ret); } static int stor_ioctl(struct open_file *f, u_long cmd, void *data) { struct disk_devdesc *dev; int rc; dev = (struct disk_devdesc *)f->f_devdata; rc = disk_ioctl(dev, cmd, data); if (rc != ENOTTY) return (rc); switch (cmd) { case DIOCGSECTORSIZE: *(u_int *)data = SI(dev).bsize; break; case DIOCGMEDIASIZE: *(uint64_t *)data = SI(dev).bsize * SI(dev).blocks; break; default: return (ENOTTY); } return (0); } /* * Return the device unit number for the given type and type-relative unit * number. */ int uboot_diskgetunit(int type, int type_unit) { int local_type_unit; int i; local_type_unit = 0; for (i = 0; i < stor_info_no; i++) { if ((stor_info[i].type & type) == type) { if (local_type_unit == type_unit) { return (i); } local_type_unit++; } } return (-1); } Index: head/stand/usb/storage/umass_loader.c =================================================================== --- head/stand/usb/storage/umass_loader.c (revision 345476) +++ head/stand/usb/storage/umass_loader.c (revision 345477) @@ -1,239 +1,239 @@ /* $FreeBSD$ */ /*- * Copyright (c) 2014 Hans Petter Selasky * All rights reserved. * * This software was developed by SRI International and the University of * Cambridge Computer Laboratory under DARPA/AFRL contract (FA8750-10-C-0237) * ("CTSRD"), as part of the DARPA CRASH research programme. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ #include #include #include #include #include #define HAVE_STANDARD_DEFS #include USB_GLOBAL_INCLUDE_FILE #include "umass_common.h" static int umass_disk_init(void); static int umass_disk_open(struct open_file *,...); static int umass_disk_close(struct open_file *); static void umass_disk_cleanup(void); static int umass_disk_ioctl(struct open_file *, u_long, void *); static int umass_disk_strategy(void *, int, daddr_t, size_t, char *, size_t *); static int umass_disk_print(int); struct devsw umass_disk = { .dv_name = "umass", .dv_type = DEVT_DISK, .dv_init = umass_disk_init, .dv_strategy = umass_disk_strategy, .dv_open = umass_disk_open, .dv_close = umass_disk_close, .dv_ioctl = umass_disk_ioctl, .dv_print = umass_disk_print, .dv_cleanup = umass_disk_cleanup, }; static int umass_disk_init(void) { uint32_t time; usb_init(); usb_needs_explore_all(); /* wait 8 seconds for a USB mass storage device to appear */ for (time = 0; time < (8 * hz); time++) { usb_idle(); delay(1000000 / hz); time++; callout_process(1); if (umass_uaa.device != NULL) return (0); } return (0); } static int umass_disk_strategy(void *devdata, int flag, daddr_t dblk, size_t size, char *buf, size_t *rsizep) { if (umass_uaa.device == NULL) return (ENXIO); if (rsizep != NULL) *rsizep = 0; flag &= F_MASK; if (flag == F_WRITE) { if (usb_msc_write_10(umass_uaa.device, 0, dblk, size >> 9, buf) != 0) return (EINVAL); } else if (flag == F_READ) { if (usb_msc_read_10(umass_uaa.device, 0, dblk, size >> 9, buf) != 0) return (EINVAL); } else { return (EROFS); } if (rsizep != NULL) *rsizep = size; return (0); } static int umass_disk_open_sub(struct disk_devdesc *dev) { uint32_t nblock; uint32_t blocksize; if (usb_msc_read_capacity(umass_uaa.device, 0, &nblock, &blocksize) != 0) return (EINVAL); return (disk_open(dev, ((uint64_t)nblock + 1) * (uint64_t)blocksize, blocksize)); } static int umass_disk_open(struct open_file *f,...) { va_list ap; struct disk_devdesc *dev; va_start(ap, f); dev = va_arg(ap, struct disk_devdesc *); va_end(ap); if (umass_uaa.device == NULL) return (ENXIO); if (dev->d_unit != 0) return (EIO); return (umass_disk_open_sub(dev)); } static int umass_disk_ioctl(struct open_file *f, u_long cmd, void *buf) { struct disk_devdesc *dev; uint32_t nblock; uint32_t blocksize; int rc; dev = (struct disk_devdesc *)(f->f_devdata); if (dev == NULL) return (EINVAL); rc = disk_ioctl(dev, cmd, buf); if (rc != ENOTTY) return (rc); switch (cmd) { case DIOCGSECTORSIZE: case DIOCGMEDIASIZE: if (usb_msc_read_capacity(umass_uaa.device, 0, &nblock, &blocksize) != 0) return (EINVAL); if (cmd == DIOCGMEDIASIZE) *(uint64_t*)buf = nblock; else *(uint32_t*)buf = blocksize; return (0); default: return (ENXIO); } } static int umass_disk_close(struct open_file *f) { struct disk_devdesc *dev; dev = (struct disk_devdesc *)f->f_devdata; return (disk_close(dev)); } static int umass_disk_print(int verbose) { struct disk_devdesc dev; printf("%s devices:", umass_disk.dv_name); if (pager_output("\n") != 0) return (1); memset(&dev, 0, sizeof(dev)); ret = pager_output(" umass0 UMASS device\n"); if (ret != 0) return (ret); dev.d_dev = &umass_disk; dev.d_unit = 0; - dev.d_slice = -1; - dev.d_partition = -1; + dev.d_slice = D_SLICENONE; + dev.d_partition = D_PARTNONE; if (umass_disk_open_sub(&dev) == 0) { ret = disk_print(&dev, " umass0", verbose); disk_close(&dev); } return (ret); } static void umass_disk_cleanup(void) { usb_uninit(); } /* USB specific functions */ extern void callout_process(int); extern void usb_idle(void); extern void usb_init(void); extern void usb_uninit(void); void DELAY(unsigned int usdelay) { delay(usdelay); } int pause(const char *what, int timeout) { if (timeout == 0) timeout = 1; delay((1000000 / hz) * timeout); return (0); } Index: head/stand/userboot/userboot/main.c =================================================================== --- head/stand/userboot/userboot/main.c (revision 345476) +++ head/stand/userboot/userboot/main.c (revision 345477) @@ -1,306 +1,306 @@ /*- * Copyright (c) 1998 Michael Smith * Copyright (c) 1998,2000 Doug Rabson * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ #include __FBSDID("$FreeBSD$"); #include #include #include #include #include "bootstrap.h" #include "disk.h" #include "libuserboot.h" #if defined(USERBOOT_ZFS_SUPPORT) #include "libzfs.h" static void userboot_zfs_probe(void); static int userboot_zfs_found; #endif /* Minimum version required */ #define USERBOOT_VERSION USERBOOT_VERSION_3 #define LOADER_PATH "/boot/loader" #define INTERP_MARKER "$Interpreter:" #define MALLOCSZ (64*1024*1024) struct loader_callbacks *callbacks; void *callbacks_arg; static jmp_buf jb; struct arch_switch archsw; /* MI/MD interface boundary */ static void extract_currdev(void); static void check_interpreter(void); void delay(int usec) { CALLBACK(delay, usec); } void exit(int v) { CALLBACK(exit, v); longjmp(jb, 1); } static void check_interpreter(void) { struct stat st; size_t marklen, rdsize; const char *guest_interp, *my_interp; char *buf; int fd; /* * If we can't stat(2) or open(2) LOADER_PATH, then we'll fail by * simply letting us roll on with whatever interpreter we were compiled * with. This is likely not going to be an issue in reality. */ buf = NULL; if (stat(LOADER_PATH, &st) != 0) return; if ((fd = open(LOADER_PATH, O_RDONLY)) < 0) return; rdsize = st.st_size; buf = malloc(rdsize); if (buf == NULL) goto out; if (read(fd, buf, rdsize) < rdsize) goto out; marklen = strlen(INTERP_MARKER); my_interp = bootprog_interp + marklen; /* * Here we make the assumption that a loader binary without the * interpreter marker is a 4th one. All loader binaries going forward * should have this properly specified, so our assumption should always * be a good one. */ if ((guest_interp = memmem(buf, rdsize, INTERP_MARKER, marklen)) != NULL) guest_interp += marklen; else guest_interp = "4th"; /* * The guest interpreter may not have a version of loader that * specifies the interpreter installed. If that's the case, we'll * assume it's legacy (4th) and request a swap to that if we're * a Lua-userboot. */ if (strcmp(my_interp, guest_interp) != 0) CALLBACK(swap_interpreter, guest_interp); out: free(buf); close(fd); return; } void loader_main(struct loader_callbacks *cb, void *arg, int version, int ndisks) { static char mallocbuf[MALLOCSZ]; char *var; int i; if (version < USERBOOT_VERSION) abort(); callbacks = cb; callbacks_arg = arg; userboot_disk_maxunit = ndisks; /* * initialise the heap as early as possible. Once this is done, * alloc() is usable. */ setheap((void *)mallocbuf, (void *)(mallocbuf + sizeof(mallocbuf))); /* * Hook up the console */ cons_probe(); printf("\n%s", bootprog_info); #if 0 printf("Memory: %ld k\n", memsize() / 1024); #endif setenv("LINES", "24", 1); /* optional */ /* * Set custom environment variables */ i = 0; while (1) { var = CALLBACK(getenv, i++); if (var == NULL) break; putenv(var); } archsw.arch_autoload = userboot_autoload; archsw.arch_getdev = userboot_getdev; archsw.arch_copyin = userboot_copyin; archsw.arch_copyout = userboot_copyout; archsw.arch_readin = userboot_readin; #if defined(USERBOOT_ZFS_SUPPORT) archsw.arch_zfs_probe = userboot_zfs_probe; #endif /* * Initialise the block cache. Set the upper limit. */ bcache_init(32768, 512); /* * March through the device switch probing for things. */ for (i = 0; devsw[i] != NULL; i++) if (devsw[i]->dv_init != NULL) (devsw[i]->dv_init)(); extract_currdev(); /* * Checking the interpreter isn't worth the overhead unless we * actually have the swap_interpreter callback, so we actually version * check here rather than later on. */ if (version >= USERBOOT_VERSION_5) check_interpreter(); if (setjmp(jb)) return; interact(); /* doesn't return */ exit(0); } /* * Set the 'current device' by (if possible) recovering the boot device as * supplied by the initial bootstrap. */ static void extract_currdev(void) { struct disk_devdesc dev; struct devdesc *dd; #if defined(USERBOOT_ZFS_SUPPORT) struct zfs_devdesc zdev; if (userboot_zfs_found) { /* Leave the pool/root guid's unassigned */ bzero(&zdev, sizeof(zdev)); zdev.dd.d_dev = &zfs_dev; init_zfs_bootenv(zfs_fmtdev(&zdev)); dd = &zdev.dd; } else #endif if (userboot_disk_maxunit > 0) { dev.dd.d_dev = &userboot_disk; dev.dd.d_unit = 0; - dev.d_slice = 0; - dev.d_partition = 0; + dev.d_slice = D_SLICEWILD; + dev.d_partition = D_PARTWILD; /* * If we cannot auto-detect the partition type then * access the disk as a raw device. */ if (dev.dd.d_dev->dv_open(NULL, &dev)) { - dev.d_slice = -1; - dev.d_partition = -1; + dev.d_slice = D_SLICENONE; + dev.d_partition = D_PARTNONE; } dd = &dev.dd; } else { dev.dd.d_dev = &host_dev; dev.dd.d_unit = 0; dd = &dev.dd; } env_setenv("currdev", EV_VOLATILE, userboot_fmtdev(dd), userboot_setcurrdev, env_nounset); env_setenv("loaddev", EV_VOLATILE, userboot_fmtdev(dd), env_noset, env_nounset); } #if defined(USERBOOT_ZFS_SUPPORT) static void userboot_zfs_probe(void) { char devname[32]; uint64_t pool_guid; int unit; /* * Open all the disks we can find and see if we can reconstruct * ZFS pools from them. Record if any were found. */ for (unit = 0; unit < userboot_disk_maxunit; unit++) { sprintf(devname, "disk%d:", unit); pool_guid = 0; zfs_probe_dev(devname, &pool_guid); if (pool_guid != 0) userboot_zfs_found = 1; } } #endif COMMAND_SET(quit, "quit", "exit the loader", command_quit); static int command_quit(int argc, char *argv[]) { exit(USERBOOT_EXIT_QUIT); return (CMD_OK); } COMMAND_SET(reboot, "reboot", "reboot the system", command_reboot); static int command_reboot(int argc, char *argv[]) { exit(USERBOOT_EXIT_REBOOT); return (CMD_OK); } Index: head/stand/userboot/userboot/userboot_disk.c =================================================================== --- head/stand/userboot/userboot/userboot_disk.c (revision 345476) +++ head/stand/userboot/userboot/userboot_disk.c (revision 345477) @@ -1,242 +1,242 @@ /*- * Copyright (c) 2011 Google, Inc. * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ #include __FBSDID("$FreeBSD$"); /* * Userboot disk image handling. */ #include #include #include #include #include "disk.h" #include "libuserboot.h" struct userdisk_info { uint64_t mediasize; uint16_t sectorsize; int ud_open; /* reference counter */ void *ud_bcache; /* buffer cache data */ }; int userboot_disk_maxunit = 0; static int userdisk_maxunit = 0; static struct userdisk_info *ud_info; static int userdisk_init(void); static void userdisk_cleanup(void); static int userdisk_strategy(void *devdata, int flag, daddr_t dblk, size_t size, char *buf, size_t *rsize); static int userdisk_realstrategy(void *devdata, int flag, daddr_t dblk, size_t size, char *buf, size_t *rsize); static int userdisk_open(struct open_file *f, ...); static int userdisk_close(struct open_file *f); static int userdisk_ioctl(struct open_file *f, u_long cmd, void *data); static int userdisk_print(int verbose); struct devsw userboot_disk = { "disk", DEVT_DISK, userdisk_init, userdisk_strategy, userdisk_open, userdisk_close, userdisk_ioctl, userdisk_print, userdisk_cleanup }; /* * Initialize userdisk_info structure for each disk. */ static int userdisk_init(void) { off_t mediasize; u_int sectorsize; int i; userdisk_maxunit = userboot_disk_maxunit; if (userdisk_maxunit > 0) { ud_info = malloc(sizeof(*ud_info) * userdisk_maxunit); if (ud_info == NULL) return (ENOMEM); for (i = 0; i < userdisk_maxunit; i++) { if (CALLBACK(diskioctl, i, DIOCGSECTORSIZE, §orsize) != 0 || CALLBACK(diskioctl, i, DIOCGMEDIASIZE, &mediasize) != 0) return (ENXIO); ud_info[i].mediasize = mediasize; ud_info[i].sectorsize = sectorsize; ud_info[i].ud_open = 0; ud_info[i].ud_bcache = NULL; } } bcache_add_dev(userdisk_maxunit); return(0); } static void userdisk_cleanup(void) { if (userdisk_maxunit > 0) free(ud_info); } /* * Print information about disks */ static int userdisk_print(int verbose) { struct disk_devdesc dev; char line[80]; int i, ret = 0; if (userdisk_maxunit == 0) return (0); printf("%s devices:", userboot_disk.dv_name); if ((ret = pager_output("\n")) != 0) return (ret); for (i = 0; i < userdisk_maxunit; i++) { snprintf(line, sizeof(line), " disk%d: Guest drive image\n", i); ret = pager_output(line); if (ret != 0) break; dev.dd.d_dev = &userboot_disk; dev.dd.d_unit = i; - dev.d_slice = -1; - dev.d_partition = -1; + dev.d_slice = D_SLICENONE; + dev.d_partition = D_PARTNONE; if (disk_open(&dev, ud_info[i].mediasize, ud_info[i].sectorsize) == 0) { snprintf(line, sizeof(line), " disk%d", i); ret = disk_print(&dev, line, verbose); disk_close(&dev); if (ret != 0) break; } } return (ret); } /* * Attempt to open the disk described by (dev) for use by (f). */ static int userdisk_open(struct open_file *f, ...) { va_list ap; struct disk_devdesc *dev; va_start(ap, f); dev = va_arg(ap, struct disk_devdesc *); va_end(ap); if (dev->dd.d_unit < 0 || dev->dd.d_unit >= userdisk_maxunit) return (EIO); ud_info[dev->dd.d_unit].ud_open++; if (ud_info[dev->dd.d_unit].ud_bcache == NULL) ud_info[dev->dd.d_unit].ud_bcache = bcache_allocate(); return (disk_open(dev, ud_info[dev->dd.d_unit].mediasize, ud_info[dev->dd.d_unit].sectorsize)); } static int userdisk_close(struct open_file *f) { struct disk_devdesc *dev; dev = (struct disk_devdesc *)f->f_devdata; ud_info[dev->dd.d_unit].ud_open--; if (ud_info[dev->dd.d_unit].ud_open == 0) { bcache_free(ud_info[dev->dd.d_unit].ud_bcache); ud_info[dev->dd.d_unit].ud_bcache = NULL; } return (disk_close(dev)); } static int userdisk_strategy(void *devdata, int rw, daddr_t dblk, size_t size, char *buf, size_t *rsize) { struct bcache_devdata bcd; struct disk_devdesc *dev; dev = (struct disk_devdesc *)devdata; bcd.dv_strategy = userdisk_realstrategy; bcd.dv_devdata = devdata; bcd.dv_cache = ud_info[dev->dd.d_unit].ud_bcache; return (bcache_strategy(&bcd, rw, dblk + dev->d_offset, size, buf, rsize)); } static int userdisk_realstrategy(void *devdata, int rw, daddr_t dblk, size_t size, char *buf, size_t *rsize) { struct disk_devdesc *dev = devdata; uint64_t off; size_t resid; int rc; rw &= F_MASK; if (rw == F_WRITE) return (EROFS); if (rw != F_READ) return (EINVAL); if (rsize) *rsize = 0; off = dblk * ud_info[dev->dd.d_unit].sectorsize; rc = CALLBACK(diskread, dev->dd.d_unit, off, buf, size, &resid); if (rc) return (rc); if (rsize) *rsize = size - resid; return (0); } static int userdisk_ioctl(struct open_file *f, u_long cmd, void *data) { struct disk_devdesc *dev; int rc; dev = (struct disk_devdesc *)f->f_devdata; rc = disk_ioctl(dev, cmd, data); if (rc != ENOTTY) return (rc); return (CALLBACK(diskioctl, dev->dd.d_unit, cmd, data)); }