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