1223695Sdfr/*- 2223695Sdfr * Copyright (c) 1998 Michael Smith <msmith@freebsd.org> 3223695Sdfr * All rights reserved. 4223695Sdfr * 5223695Sdfr * Redistribution and use in source and binary forms, with or without 6223695Sdfr * modification, are permitted provided that the following conditions 7223695Sdfr * are met: 8223695Sdfr * 1. Redistributions of source code must retain the above copyright 9223695Sdfr * notice, this list of conditions and the following disclaimer. 10223695Sdfr * 2. Redistributions in binary form must reproduce the above copyright 11223695Sdfr * notice, this list of conditions and the following disclaimer in the 12223695Sdfr * documentation and/or other materials provided with the distribution. 13223695Sdfr * 14223695Sdfr * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 15223695Sdfr * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 16223695Sdfr * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 17223695Sdfr * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 18223695Sdfr * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 19223695Sdfr * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 20223695Sdfr * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 21223695Sdfr * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 22223695Sdfr * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 23223695Sdfr * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 24223695Sdfr * SUCH DAMAGE. 25223695Sdfr */ 26223695Sdfr 27223695Sdfr#include <sys/cdefs.h> 28223695Sdfr__FBSDID("$FreeBSD$"); 29223695Sdfr 30223695Sdfr#include <stand.h> 31223695Sdfr#include <string.h> 32223695Sdfr 33223695Sdfr#include "bootstrap.h" 34223695Sdfr#include "disk.h" 35223695Sdfr#include "libuserboot.h" 36223695Sdfr 37268932Sjhb#if defined(USERBOOT_ZFS_SUPPORT) 38268932Sjhb#include "../zfs/libzfs.h" 39268932Sjhb#endif 40268932Sjhb 41223695Sdfrstatic int userboot_parsedev(struct disk_devdesc **dev, const char *devspec, const char **path); 42223695Sdfr 43223695Sdfr/* 44223695Sdfr * Point (dev) at an allocated device specifier for the device matching the 45223695Sdfr * path in (devspec). If it contains an explicit device specification, 46223695Sdfr * use that. If not, use the default device. 47223695Sdfr */ 48223695Sdfrint 49223695Sdfruserboot_getdev(void **vdev, const char *devspec, const char **path) 50223695Sdfr{ 51223695Sdfr struct disk_devdesc **dev = (struct disk_devdesc **)vdev; 52223695Sdfr int rv; 53223695Sdfr 54223695Sdfr /* 55223695Sdfr * If it looks like this is just a path and no 56223695Sdfr * device, go with the current device. 57223695Sdfr */ 58223695Sdfr if ((devspec == NULL) || 59223695Sdfr (devspec[0] == '/') || 60223695Sdfr (strchr(devspec, ':') == NULL)) { 61223695Sdfr 62223695Sdfr if (((rv = userboot_parsedev(dev, getenv("currdev"), NULL)) == 0) && 63223695Sdfr (path != NULL)) 64223695Sdfr *path = devspec; 65223695Sdfr return(rv); 66223695Sdfr } 67223695Sdfr 68223695Sdfr /* 69223695Sdfr * Try to parse the device name off the beginning of the devspec 70223695Sdfr */ 71223695Sdfr return(userboot_parsedev(dev, devspec, path)); 72223695Sdfr} 73223695Sdfr 74223695Sdfr/* 75223695Sdfr * Point (dev) at an allocated device specifier matching the string version 76223695Sdfr * at the beginning of (devspec). Return a pointer to the remaining 77223695Sdfr * text in (path). 78223695Sdfr * 79223695Sdfr * In all cases, the beginning of (devspec) is compared to the names 80223695Sdfr * of known devices in the device switch, and then any following text 81223695Sdfr * is parsed according to the rules applied to the device type. 82223695Sdfr * 83223695Sdfr * For disk-type devices, the syntax is: 84223695Sdfr * 85223695Sdfr * disk<unit>[s<slice>][<partition>]: 86223695Sdfr * 87223695Sdfr */ 88223695Sdfrstatic int 89223695Sdfruserboot_parsedev(struct disk_devdesc **dev, const char *devspec, const char **path) 90223695Sdfr{ 91223695Sdfr struct disk_devdesc *idev; 92223695Sdfr struct devsw *dv; 93239058Sae int i, unit, err; 94223695Sdfr char *cp; 95223695Sdfr const char *np; 96223695Sdfr 97223695Sdfr /* minimum length check */ 98223695Sdfr if (strlen(devspec) < 2) 99223695Sdfr return(EINVAL); 100223695Sdfr 101223695Sdfr /* look for a device that matches */ 102223695Sdfr for (i = 0, dv = NULL; devsw[i] != NULL; i++) { 103223695Sdfr if (!strncmp(devspec, devsw[i]->dv_name, strlen(devsw[i]->dv_name))) { 104223695Sdfr dv = devsw[i]; 105223695Sdfr break; 106223695Sdfr } 107223695Sdfr } 108223695Sdfr if (dv == NULL) 109223695Sdfr return(ENOENT); 110223695Sdfr idev = malloc(sizeof(struct disk_devdesc)); 111223695Sdfr err = 0; 112223695Sdfr np = (devspec + strlen(dv->dv_name)); 113223695Sdfr 114223695Sdfr switch(dv->dv_type) { 115223695Sdfr case DEVT_NONE: /* XXX what to do here? Do we care? */ 116223695Sdfr break; 117223695Sdfr 118223695Sdfr case DEVT_DISK: 119239058Sae err = disk_parsedev(idev, np, path); 120239058Sae if (err != 0) 121223695Sdfr goto fail; 122223695Sdfr break; 123223695Sdfr 124223695Sdfr case DEVT_CD: 125223695Sdfr case DEVT_NET: 126223695Sdfr unit = 0; 127223695Sdfr 128223695Sdfr if (*np && (*np != ':')) { 129223695Sdfr unit = strtol(np, &cp, 0); /* get unit number if present */ 130223695Sdfr if (cp == np) { 131223695Sdfr err = EUNIT; 132223695Sdfr goto fail; 133223695Sdfr } 134223695Sdfr } else { 135223695Sdfr cp = np; 136223695Sdfr } 137223695Sdfr if (*cp && (*cp != ':')) { 138223695Sdfr err = EINVAL; 139223695Sdfr goto fail; 140223695Sdfr } 141223695Sdfr 142223695Sdfr idev->d_unit = unit; 143223695Sdfr if (path != NULL) 144223695Sdfr *path = (*cp == 0) ? cp : cp + 1; 145223695Sdfr break; 146223695Sdfr 147268932Sjhb case DEVT_ZFS: 148268932Sjhb#if defined(USERBOOT_ZFS_SUPPORT) 149268932Sjhb err = zfs_parsedev((struct zfs_devdesc *)idev, np, path); 150268932Sjhb if (err != 0) 151268932Sjhb goto fail; 152268932Sjhb break; 153268932Sjhb#else 154268932Sjhb /* FALLTHROUGH */ 155268932Sjhb#endif 156268932Sjhb 157223695Sdfr default: 158223695Sdfr err = EINVAL; 159223695Sdfr goto fail; 160223695Sdfr } 161223695Sdfr idev->d_dev = dv; 162223695Sdfr idev->d_type = dv->dv_type; 163223695Sdfr if (dev == NULL) { 164223695Sdfr free(idev); 165223695Sdfr } else { 166223695Sdfr *dev = idev; 167223695Sdfr } 168223695Sdfr return(0); 169223695Sdfr 170223695Sdfr fail: 171223695Sdfr free(idev); 172223695Sdfr return(err); 173223695Sdfr} 174223695Sdfr 175223695Sdfr 176223695Sdfrchar * 177223695Sdfruserboot_fmtdev(void *vdev) 178223695Sdfr{ 179223695Sdfr struct disk_devdesc *dev = (struct disk_devdesc *)vdev; 180223695Sdfr static char buf[128]; /* XXX device length constant? */ 181239058Sae 182223695Sdfr switch(dev->d_type) { 183223695Sdfr case DEVT_NONE: 184223695Sdfr strcpy(buf, "(no device)"); 185223695Sdfr break; 186223695Sdfr 187223695Sdfr case DEVT_CD: 188223695Sdfr sprintf(buf, "%s%d:", dev->d_dev->dv_name, dev->d_unit); 189223695Sdfr break; 190223695Sdfr 191223695Sdfr case DEVT_DISK: 192239058Sae return (disk_fmtdev(vdev)); 193223695Sdfr 194223695Sdfr case DEVT_NET: 195268932Sjhb sprintf(buf, "%s%d:", dev->d_dev->dv_name, dev->d_unit); 196268932Sjhb break; 197268932Sjhb 198223695Sdfr case DEVT_ZFS: 199268932Sjhb#if defined(USERBOOT_ZFS_SUPPORT) 200268932Sjhb return (zfs_fmtdev(vdev)); 201268932Sjhb#else 202223695Sdfr sprintf(buf, "%s%d:", dev->d_dev->dv_name, dev->d_unit); 203268932Sjhb#endif 204223695Sdfr break; 205223695Sdfr } 206223695Sdfr return(buf); 207223695Sdfr} 208223695Sdfr 209223695Sdfr 210223695Sdfr/* 211223695Sdfr * Set currdev to suit the value being supplied in (value) 212223695Sdfr */ 213223695Sdfrint 214223695Sdfruserboot_setcurrdev(struct env_var *ev, int flags, const void *value) 215223695Sdfr{ 216223695Sdfr struct disk_devdesc *ncurr; 217223695Sdfr int rv; 218223695Sdfr 219223695Sdfr if ((rv = userboot_parsedev(&ncurr, value, NULL)) != 0) 220223695Sdfr return(rv); 221223695Sdfr free(ncurr); 222223695Sdfr env_setenv(ev->ev_name, flags | EV_NOHOOK, value, NULL, NULL); 223223695Sdfr return(0); 224223695Sdfr} 225