devicename.c revision 294720
150764Smarkm/*-
2233294Sstas * Copyright (c) 1998 Michael Smith <msmith@freebsd.org>
3233294Sstas * Copyright (c) 2006 Marcel Moolenaar
4233294Sstas * All rights reserved.
550764Smarkm *
6233294Sstas * Redistribution and use in source and binary forms, with or without
7233294Sstas * modification, are permitted provided that the following conditions
8233294Sstas * are met:
950764Smarkm * 1. Redistributions of source code must retain the above copyright
10233294Sstas *    notice, this list of conditions and the following disclaimer.
11233294Sstas * 2. Redistributions in binary form must reproduce the above copyright
1250764Smarkm *    notice, this list of conditions and the following disclaimer in the
13233294Sstas *    documentation and/or other materials provided with the distribution.
14233294Sstas *
15233294Sstas * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
1650764Smarkm * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
17233294Sstas * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
18233294Sstas * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
19233294Sstas * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
2050764Smarkm * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
21233294Sstas * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
22233294Sstas * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
23233294Sstas * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
24233294Sstas * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
25233294Sstas * SUCH DAMAGE.
26233294Sstas */
27233294Sstas
28233294Sstas#include <sys/cdefs.h>
29233294Sstas__FBSDID("$FreeBSD: stable/10/sys/boot/efi/loader/devicename.c 294720 2016-01-25 10:50:11Z smh $");
30233294Sstas
31233294Sstas#include <stand.h>
3250764Smarkm#include <string.h>
3350764Smarkm#include <sys/disklabel.h>
34233294Sstas#include "bootstrap.h"
3550764Smarkm
3650764Smarkm#include <efi.h>
3750764Smarkm#include <efilib.h>
3850764Smarkm
3950764Smarkmstatic int efi_parsedev(struct devdesc **, const char *, const char **);
40233294Sstas
41233294Sstas/*
42233294Sstas * Point (dev) at an allocated device specifier for the device matching the
43233294Sstas * path in (devspec). If it contains an explicit device specification,
44233294Sstas * use that.  If not, use the default device.
45233294Sstas */
46233294Sstasint
4750764Smarkmefi_getdev(void **vdev, const char *devspec, const char **path)
4850764Smarkm{
4950764Smarkm	struct devdesc **dev = (struct devdesc **)vdev;
50233294Sstas	int rv;
5150764Smarkm
5250764Smarkm	/*
53233294Sstas	 * If it looks like this is just a path and no device, then
54233294Sstas	 * use the current device instead.
55233294Sstas	 */
56233294Sstas	if (devspec == NULL || *devspec == '/' || !strchr(devspec, ':')) {
57233294Sstas		rv = efi_parsedev(dev, getenv("currdev"), NULL);
58233294Sstas		if (rv == 0 && path != NULL)
59233294Sstas			*path = devspec;
60233294Sstas		return (rv);
61233294Sstas	}
62233294Sstas
63233294Sstas	/* Parse the device name off the beginning of the devspec. */
64233294Sstas	return (efi_parsedev(dev, devspec, path));
65233294Sstas}
66233294Sstas
67233294Sstas/*
68233294Sstas * Point (dev) at an allocated device specifier matching the string version
69233294Sstas * at the beginning of (devspec).  Return a pointer to the remaining
7050764Smarkm * text in (path).
7150764Smarkm *
7250764Smarkm * In all cases, the beginning of (devspec) is compared to the names
7350764Smarkm * of known devices in the device switch, and then any following text
7450764Smarkm * is parsed according to the rules applied to the device type.
7550764Smarkm *
7650764Smarkm * For disk-type devices, the syntax is:
7750764Smarkm *
7850764Smarkm * fs<unit>:
79233294Sstas */
80233294Sstasstatic int
81233294Sstasefi_parsedev(struct devdesc **dev, const char *devspec, const char **path)
8250764Smarkm{
8350764Smarkm	struct devdesc *idev;
8450764Smarkm	struct devsw *dv;
85127804Snectar	char *cp;
8650764Smarkm	const char *np;
87127804Snectar	int i, err;
8850764Smarkm
8950764Smarkm	/* minimum length check */
9050764Smarkm	if (strlen(devspec) < 2)
9150764Smarkm		return (EINVAL);
9250764Smarkm
9350764Smarkm	/* look for a device that matches */
9450764Smarkm	for (i = 0; devsw[i] != NULL; i++) {
9550764Smarkm		dv = devsw[i];
9650764Smarkm		if (!strncmp(devspec, dv->dv_name, strlen(dv->dv_name)))
9750764Smarkm			break;
98127804Snectar	}
99127804Snectar	if (devsw[i] == NULL)
10050764Smarkm		return (ENOENT);
10150764Smarkm
102233294Sstas	idev = malloc(sizeof(struct devdesc));
103233294Sstas	if (idev == NULL)
10450764Smarkm		return (ENOMEM);
10550764Smarkm
10650764Smarkm	idev->d_dev = dv;
10750764Smarkm	idev->d_type = dv->dv_type;
10850764Smarkm	idev->d_unit = -1;
10950764Smarkm
11050764Smarkm	err = 0;
11150764Smarkm	np = devspec + strlen(dv->dv_name);
112	if (*np != '\0' && *np != ':') {
113		idev->d_unit = strtol(np, &cp, 0);
114		if (cp == np) {
115			idev->d_unit = -1;
116			free(idev);
117			return (EUNIT);
118		}
119	}
120	if (*cp != '\0' && *cp != ':') {
121		free(idev);
122		return (EINVAL);
123	}
124
125	if (path != NULL)
126		*path = (*cp == 0) ? cp : cp + 1;
127	if (dev != NULL)
128		*dev = idev;
129	else
130		free(idev);
131	return (0);
132}
133
134char *
135efi_fmtdev(void *vdev)
136{
137	struct devdesc *dev = (struct devdesc *)vdev;
138	static char buf[32];	/* XXX device length constant? */
139
140	switch(dev->d_type) {
141	case DEVT_NONE:
142		strcpy(buf, "(no device)");
143		break;
144
145	default:
146		sprintf(buf, "%s%d:", dev->d_dev->dv_name, dev->d_unit);
147		break;
148	}
149
150	return (buf);
151}
152
153/*
154 * Set currdev to suit the value being supplied in (value)
155 */
156int
157efi_setcurrdev(struct env_var *ev, int flags, const void *value)
158{
159	struct devdesc *ncurr;
160	int rv;
161
162	rv = efi_parsedev(&ncurr, value, NULL);
163	if (rv != 0)
164		return (rv);
165
166	free(ncurr);
167	env_setenv(ev->ev_name, flags | EV_NOHOOK, value, NULL, NULL);
168	return (0);
169}
170