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 37223695Sdfrstatic int userboot_parsedev(struct disk_devdesc **dev, const char *devspec, const char **path); 38223695Sdfr 39223695Sdfr/* 40223695Sdfr * Point (dev) at an allocated device specifier for the device matching the 41223695Sdfr * path in (devspec). If it contains an explicit device specification, 42223695Sdfr * use that. If not, use the default device. 43223695Sdfr */ 44223695Sdfrint 45223695Sdfruserboot_getdev(void **vdev, const char *devspec, const char **path) 46223695Sdfr{ 47223695Sdfr struct disk_devdesc **dev = (struct disk_devdesc **)vdev; 48223695Sdfr int rv; 49223695Sdfr 50223695Sdfr /* 51223695Sdfr * If it looks like this is just a path and no 52223695Sdfr * device, go with the current device. 53223695Sdfr */ 54223695Sdfr if ((devspec == NULL) || 55223695Sdfr (devspec[0] == '/') || 56223695Sdfr (strchr(devspec, ':') == NULL)) { 57223695Sdfr 58223695Sdfr if (((rv = userboot_parsedev(dev, getenv("currdev"), NULL)) == 0) && 59223695Sdfr (path != NULL)) 60223695Sdfr *path = devspec; 61223695Sdfr return(rv); 62223695Sdfr } 63223695Sdfr 64223695Sdfr /* 65223695Sdfr * Try to parse the device name off the beginning of the devspec 66223695Sdfr */ 67223695Sdfr return(userboot_parsedev(dev, devspec, path)); 68223695Sdfr} 69223695Sdfr 70223695Sdfr/* 71223695Sdfr * Point (dev) at an allocated device specifier matching the string version 72223695Sdfr * at the beginning of (devspec). Return a pointer to the remaining 73223695Sdfr * text in (path). 74223695Sdfr * 75223695Sdfr * In all cases, the beginning of (devspec) is compared to the names 76223695Sdfr * of known devices in the device switch, and then any following text 77223695Sdfr * is parsed according to the rules applied to the device type. 78223695Sdfr * 79223695Sdfr * For disk-type devices, the syntax is: 80223695Sdfr * 81223695Sdfr * disk<unit>[s<slice>][<partition>]: 82223695Sdfr * 83223695Sdfr */ 84223695Sdfrstatic int 85223695Sdfruserboot_parsedev(struct disk_devdesc **dev, const char *devspec, const char **path) 86223695Sdfr{ 87223695Sdfr struct disk_devdesc *idev; 88223695Sdfr struct devsw *dv; 89243243Sae int i, unit, err; 90223695Sdfr char *cp; 91223695Sdfr const char *np; 92223695Sdfr 93223695Sdfr /* minimum length check */ 94223695Sdfr if (strlen(devspec) < 2) 95223695Sdfr return(EINVAL); 96223695Sdfr 97223695Sdfr /* look for a device that matches */ 98223695Sdfr for (i = 0, dv = NULL; devsw[i] != NULL; i++) { 99223695Sdfr if (!strncmp(devspec, devsw[i]->dv_name, strlen(devsw[i]->dv_name))) { 100223695Sdfr dv = devsw[i]; 101223695Sdfr break; 102223695Sdfr } 103223695Sdfr } 104223695Sdfr if (dv == NULL) 105223695Sdfr return(ENOENT); 106223695Sdfr idev = malloc(sizeof(struct disk_devdesc)); 107223695Sdfr err = 0; 108223695Sdfr np = (devspec + strlen(dv->dv_name)); 109223695Sdfr 110223695Sdfr switch(dv->dv_type) { 111223695Sdfr case DEVT_NONE: /* XXX what to do here? Do we care? */ 112223695Sdfr break; 113223695Sdfr 114223695Sdfr case DEVT_DISK: 115243243Sae err = disk_parsedev(idev, np, path); 116243243Sae if (err != 0) 117223695Sdfr goto fail; 118223695Sdfr break; 119223695Sdfr 120223695Sdfr case DEVT_CD: 121223695Sdfr case DEVT_NET: 122223695Sdfr case DEVT_ZFS: 123223695Sdfr unit = 0; 124223695Sdfr 125223695Sdfr if (*np && (*np != ':')) { 126223695Sdfr unit = strtol(np, &cp, 0); /* get unit number if present */ 127223695Sdfr if (cp == np) { 128223695Sdfr err = EUNIT; 129223695Sdfr goto fail; 130223695Sdfr } 131223695Sdfr } else { 132223695Sdfr cp = np; 133223695Sdfr } 134223695Sdfr if (*cp && (*cp != ':')) { 135223695Sdfr err = EINVAL; 136223695Sdfr goto fail; 137223695Sdfr } 138223695Sdfr 139223695Sdfr idev->d_unit = unit; 140223695Sdfr if (path != NULL) 141223695Sdfr *path = (*cp == 0) ? cp : cp + 1; 142223695Sdfr break; 143223695Sdfr 144223695Sdfr default: 145223695Sdfr err = EINVAL; 146223695Sdfr goto fail; 147223695Sdfr } 148223695Sdfr idev->d_dev = dv; 149223695Sdfr idev->d_type = dv->dv_type; 150223695Sdfr if (dev == NULL) { 151223695Sdfr free(idev); 152223695Sdfr } else { 153223695Sdfr *dev = idev; 154223695Sdfr } 155223695Sdfr return(0); 156223695Sdfr 157223695Sdfr fail: 158223695Sdfr free(idev); 159223695Sdfr return(err); 160223695Sdfr} 161223695Sdfr 162223695Sdfr 163223695Sdfrchar * 164223695Sdfruserboot_fmtdev(void *vdev) 165223695Sdfr{ 166223695Sdfr struct disk_devdesc *dev = (struct disk_devdesc *)vdev; 167223695Sdfr static char buf[128]; /* XXX device length constant? */ 168243243Sae 169223695Sdfr switch(dev->d_type) { 170223695Sdfr case DEVT_NONE: 171223695Sdfr strcpy(buf, "(no device)"); 172223695Sdfr break; 173223695Sdfr 174223695Sdfr case DEVT_CD: 175223695Sdfr sprintf(buf, "%s%d:", dev->d_dev->dv_name, dev->d_unit); 176223695Sdfr break; 177223695Sdfr 178223695Sdfr case DEVT_DISK: 179243243Sae return (disk_fmtdev(vdev)); 180223695Sdfr 181223695Sdfr case DEVT_NET: 182223695Sdfr case DEVT_ZFS: 183223695Sdfr sprintf(buf, "%s%d:", dev->d_dev->dv_name, dev->d_unit); 184223695Sdfr break; 185223695Sdfr } 186223695Sdfr return(buf); 187223695Sdfr} 188223695Sdfr 189223695Sdfr 190223695Sdfr/* 191223695Sdfr * Set currdev to suit the value being supplied in (value) 192223695Sdfr */ 193223695Sdfrint 194223695Sdfruserboot_setcurrdev(struct env_var *ev, int flags, const void *value) 195223695Sdfr{ 196223695Sdfr struct disk_devdesc *ncurr; 197223695Sdfr int rv; 198223695Sdfr 199223695Sdfr if ((rv = userboot_parsedev(&ncurr, value, NULL)) != 0) 200223695Sdfr return(rv); 201223695Sdfr free(ncurr); 202223695Sdfr env_setenv(ev->ev_name, flags | EV_NOHOOK, value, NULL, NULL); 203223695Sdfr return(0); 204223695Sdfr} 205