1ca987d46SWarner Losh /*- 2ca987d46SWarner Losh * Copyright (c) 1998 Michael Smith <msmith@freebsd.org> 3ca987d46SWarner Losh * Copyright (c) 2006 Marcel Moolenaar 4ca987d46SWarner Losh * All rights reserved. 5ca987d46SWarner Losh * 6ca987d46SWarner Losh * Redistribution and use in source and binary forms, with or without 7ca987d46SWarner Losh * modification, are permitted provided that the following conditions 8ca987d46SWarner Losh * are met: 9ca987d46SWarner Losh * 1. Redistributions of source code must retain the above copyright 10ca987d46SWarner Losh * notice, this list of conditions and the following disclaimer. 11ca987d46SWarner Losh * 2. Redistributions in binary form must reproduce the above copyright 12ca987d46SWarner Losh * notice, this list of conditions and the following disclaimer in the 13ca987d46SWarner Losh * documentation and/or other materials provided with the distribution. 14ca987d46SWarner Losh * 15ca987d46SWarner Losh * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 16ca987d46SWarner Losh * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 17ca987d46SWarner Losh * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 18ca987d46SWarner Losh * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 19ca987d46SWarner Losh * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 20ca987d46SWarner Losh * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 21ca987d46SWarner Losh * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 22ca987d46SWarner Losh * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 23ca987d46SWarner Losh * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 24ca987d46SWarner Losh * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 25ca987d46SWarner Losh * SUCH DAMAGE. 26ca987d46SWarner Losh */ 27ca987d46SWarner Losh 28ca987d46SWarner Losh #include <sys/cdefs.h> 29ca987d46SWarner Losh __FBSDID("$FreeBSD$"); 30ca987d46SWarner Losh 31ca987d46SWarner Losh #include <stand.h> 32ca987d46SWarner Losh #include <string.h> 33ca987d46SWarner Losh #include <sys/disklabel.h> 34ca987d46SWarner Losh #include <sys/param.h> 35ca987d46SWarner Losh #include <bootstrap.h> 36ca987d46SWarner Losh #include <disk.h> 37ca987d46SWarner Losh #ifdef EFI_ZFS_BOOT 38ca987d46SWarner Losh #include <libzfs.h> 39ca987d46SWarner Losh #endif 40ca987d46SWarner Losh 41ca987d46SWarner Losh #include <efi.h> 42ca987d46SWarner Losh #include <efilib.h> 43ca987d46SWarner Losh 44ca987d46SWarner Losh static int efi_parsedev(struct devdesc **, const char *, const char **); 45ca987d46SWarner Losh 46ca987d46SWarner Losh /* 47ca987d46SWarner Losh * Point (dev) at an allocated device specifier for the device matching the 48ca987d46SWarner Losh * path in (devspec). If it contains an explicit device specification, 49ca987d46SWarner Losh * use that. If not, use the default device. 50ca987d46SWarner Losh */ 51ca987d46SWarner Losh int 52ca987d46SWarner Losh efi_getdev(void **vdev, const char *devspec, const char **path) 53ca987d46SWarner Losh { 54ca987d46SWarner Losh struct devdesc **dev = (struct devdesc **)vdev; 55ca987d46SWarner Losh int rv; 56ca987d46SWarner Losh 57ca987d46SWarner Losh /* 58ca987d46SWarner Losh * If it looks like this is just a path and no device, then 59ca987d46SWarner Losh * use the current device instead. 60ca987d46SWarner Losh */ 61ca987d46SWarner Losh if (devspec == NULL || *devspec == '/' || !strchr(devspec, ':')) { 62ca987d46SWarner Losh rv = efi_parsedev(dev, getenv("currdev"), NULL); 63ca987d46SWarner Losh if (rv == 0 && path != NULL) 64ca987d46SWarner Losh *path = devspec; 65ca987d46SWarner Losh return (rv); 66ca987d46SWarner Losh } 67ca987d46SWarner Losh 68ca987d46SWarner Losh /* Parse the device name off the beginning of the devspec. */ 69ca987d46SWarner Losh return (efi_parsedev(dev, devspec, path)); 70ca987d46SWarner Losh } 71ca987d46SWarner Losh 72ca987d46SWarner Losh /* 73ca987d46SWarner Losh * Point (dev) at an allocated device specifier matching the string version 74ca987d46SWarner Losh * at the beginning of (devspec). Return a pointer to the remaining 75ca987d46SWarner Losh * text in (path). 76ca987d46SWarner Losh * 77ca987d46SWarner Losh * In all cases, the beginning of (devspec) is compared to the names 78ca987d46SWarner Losh * of known devices in the device switch, and then any following text 79ca987d46SWarner Losh * is parsed according to the rules applied to the device type. 80ca987d46SWarner Losh * 81ca987d46SWarner Losh * For disk-type devices, the syntax is: 82ca987d46SWarner Losh * 83ca987d46SWarner Losh * fs<unit>: 84ca987d46SWarner Losh */ 85ca987d46SWarner Losh static int 86ca987d46SWarner Losh efi_parsedev(struct devdesc **dev, const char *devspec, const char **path) 87ca987d46SWarner Losh { 88ca987d46SWarner Losh struct devdesc *idev; 89ca987d46SWarner Losh struct devsw *dv; 90ca987d46SWarner Losh int i, unit, err; 91ca987d46SWarner Losh char *cp; 92ca987d46SWarner Losh const char *np; 93ca987d46SWarner Losh 94ca987d46SWarner Losh /* minimum length check */ 95ca987d46SWarner Losh if (strlen(devspec) < 2) 96ca987d46SWarner Losh return (EINVAL); 97ca987d46SWarner Losh 98ca987d46SWarner Losh /* look for a device that matches */ 99ca987d46SWarner Losh for (i = 0; devsw[i] != NULL; i++) { 100ca987d46SWarner Losh dv = devsw[i]; 101ca987d46SWarner Losh if (!strncmp(devspec, dv->dv_name, strlen(dv->dv_name))) 102ca987d46SWarner Losh break; 103ca987d46SWarner Losh } 104ca987d46SWarner Losh if (devsw[i] == NULL) 105ca987d46SWarner Losh return (ENOENT); 106ca987d46SWarner Losh 107ca987d46SWarner Losh np = devspec + strlen(dv->dv_name); 108ca987d46SWarner Losh idev = NULL; 109ca987d46SWarner Losh err = 0; 110ca987d46SWarner Losh 111ca987d46SWarner Losh switch (dv->dv_type) { 112ca987d46SWarner Losh case DEVT_NONE: 113ca987d46SWarner Losh break; 114ca987d46SWarner Losh 115ca987d46SWarner Losh case DEVT_DISK: 116ca987d46SWarner Losh idev = malloc(sizeof(struct disk_devdesc)); 117ca987d46SWarner Losh if (idev == NULL) 118ca987d46SWarner Losh return (ENOMEM); 119ca987d46SWarner Losh 120ca987d46SWarner Losh err = disk_parsedev((struct disk_devdesc *)idev, np, path); 121ca987d46SWarner Losh if (err != 0) 122ca987d46SWarner Losh goto fail; 123ca987d46SWarner Losh break; 124ca987d46SWarner Losh 125ca987d46SWarner Losh #ifdef EFI_ZFS_BOOT 126ca987d46SWarner Losh case DEVT_ZFS: 127ca987d46SWarner Losh idev = malloc(sizeof(struct zfs_devdesc)); 128ca987d46SWarner Losh if (idev == NULL) 129ca987d46SWarner Losh return (ENOMEM); 130ca987d46SWarner Losh 131ca987d46SWarner Losh err = zfs_parsedev((struct zfs_devdesc*)idev, np, path); 132ca987d46SWarner Losh if (err != 0) 133ca987d46SWarner Losh goto fail; 134ca987d46SWarner Losh break; 135ca987d46SWarner Losh #endif 136ca987d46SWarner Losh default: 137ca987d46SWarner Losh idev = malloc(sizeof(struct devdesc)); 138ca987d46SWarner Losh if (idev == NULL) 139ca987d46SWarner Losh return (ENOMEM); 140ca987d46SWarner Losh 141ca987d46SWarner Losh unit = 0; 142ca987d46SWarner Losh cp = (char *)np; 143ca987d46SWarner Losh 144ca987d46SWarner Losh if (*np != '\0' && *np != ':') { 145ca987d46SWarner Losh errno = 0; 146ca987d46SWarner Losh unit = strtol(np, &cp, 0); 147ca987d46SWarner Losh if (errno != 0 || cp == np) { 148ca987d46SWarner Losh err = EUNIT; 149ca987d46SWarner Losh goto fail; 150ca987d46SWarner Losh } 151ca987d46SWarner Losh } 152ca987d46SWarner Losh if (*cp != '\0' && *cp != ':') { 153ca987d46SWarner Losh err = EINVAL; 154ca987d46SWarner Losh goto fail; 155ca987d46SWarner Losh } 156ca987d46SWarner Losh 157ca987d46SWarner Losh idev->d_unit = unit; 158ca987d46SWarner Losh if (path != NULL) 159ca987d46SWarner Losh *path = (*cp == 0) ? cp : cp + 1; 160ca987d46SWarner Losh break; 161ca987d46SWarner Losh } 162ca987d46SWarner Losh 163ca987d46SWarner Losh idev->d_dev = dv; 164ca987d46SWarner Losh idev->d_type = dv->dv_type; 165ca987d46SWarner Losh 166ca987d46SWarner Losh if (dev != NULL) 167ca987d46SWarner Losh *dev = idev; 168ca987d46SWarner Losh else 169ca987d46SWarner Losh free(idev); 170ca987d46SWarner Losh return (0); 171ca987d46SWarner Losh 172ca987d46SWarner Losh fail: 173ca987d46SWarner Losh free(idev); 174ca987d46SWarner Losh return (err); 175ca987d46SWarner Losh } 176ca987d46SWarner Losh 177ca987d46SWarner Losh char * 178ca987d46SWarner Losh efi_fmtdev(void *vdev) 179ca987d46SWarner Losh { 180ca987d46SWarner Losh struct devdesc *dev = (struct devdesc *)vdev; 181ca987d46SWarner Losh static char buf[SPECNAMELEN + 1]; 182ca987d46SWarner Losh 183ca987d46SWarner Losh switch(dev->d_type) { 184ca987d46SWarner Losh case DEVT_NONE: 185ca987d46SWarner Losh strcpy(buf, "(no device)"); 186ca987d46SWarner Losh break; 187ca987d46SWarner Losh 188ca987d46SWarner Losh case DEVT_DISK: 189ca987d46SWarner Losh return (disk_fmtdev(vdev)); 190ca987d46SWarner Losh 191ca987d46SWarner Losh #ifdef EFI_ZFS_BOOT 192ca987d46SWarner Losh case DEVT_ZFS: 193ca987d46SWarner Losh return (zfs_fmtdev(dev)); 194ca987d46SWarner Losh #endif 195ca987d46SWarner Losh default: 196ca987d46SWarner Losh sprintf(buf, "%s%d:", dev->d_dev->dv_name, dev->d_unit); 197ca987d46SWarner Losh break; 198ca987d46SWarner Losh } 199ca987d46SWarner Losh 200ca987d46SWarner Losh return (buf); 201ca987d46SWarner Losh } 202ca987d46SWarner Losh 203ca987d46SWarner Losh /* 204ca987d46SWarner Losh * Set currdev to suit the value being supplied in (value) 205ca987d46SWarner Losh */ 206ca987d46SWarner Losh int 207ca987d46SWarner Losh efi_setcurrdev(struct env_var *ev, int flags, const void *value) 208ca987d46SWarner Losh { 209ca987d46SWarner Losh struct devdesc *ncurr; 210ca987d46SWarner Losh int rv; 211ca987d46SWarner Losh 212ca987d46SWarner Losh rv = efi_parsedev(&ncurr, value, NULL); 213ca987d46SWarner Losh if (rv != 0) 214ca987d46SWarner Losh return (rv); 215ca987d46SWarner Losh 216ca987d46SWarner Losh free(ncurr); 217ca987d46SWarner Losh env_setenv(ev->ev_name, flags | EV_NOHOOK, value, NULL, NULL); 218ca987d46SWarner Losh return (0); 219ca987d46SWarner Losh } 220