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