xref: /titanic_51/usr/src/boot/sys/boot/efi/loader/devicename.c (revision b8767a1ded579aee7e1f6cbeb35b956692bb6fa6)
1 /*
2  * Copyright (c) 1998 Michael Smith <msmith@freebsd.org>
3  * Copyright (c) 2006 Marcel Moolenaar
4  * All rights reserved.
5  *
6  * Redistribution and use in source and binary forms, with or without
7  * modification, are permitted provided that the following conditions
8  * are met:
9  * 1. Redistributions of source code must retain the above copyright
10  *    notice, this list of conditions and the following disclaimer.
11  * 2. Redistributions in binary form must reproduce the above copyright
12  *    notice, this list of conditions and the following disclaimer in the
13  *    documentation and/or other materials provided with the distribution.
14  *
15  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
16  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
17  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
18  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
19  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
20  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
21  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
22  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
23  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
24  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
25  * SUCH DAMAGE.
26  */
27 
28 #include <sys/cdefs.h>
29 
30 #include <stand.h>
31 #include <string.h>
32 #include <sys/disklabel.h>
33 #include <sys/param.h>
34 #include <bootstrap.h>
35 #include <disk.h>
36 #include <libzfs.h>
37 
38 #include <efi.h>
39 #include <efilib.h>
40 
41 #include "loader_efi.h"
42 
43 static int efi_parsedev(struct devdesc **, const char *, const char **);
44 
45 /*
46  * Point (dev) at an allocated device specifier for the device matching the
47  * path in (devspec). If it contains an explicit device specification,
48  * use that.  If not, use the default device.
49  */
50 int
51 efi_getdev(void **vdev, const char *devspec, const char **path)
52 {
53 	struct devdesc **dev = (struct devdesc **)vdev;
54 	int rv;
55 
56 	/*
57 	 * If it looks like this is just a path and no device, then
58 	 * use the current device instead.
59 	 */
60 	if (devspec == NULL || *devspec == '/' || !strchr(devspec, ':')) {
61 		rv = efi_parsedev(dev, getenv("currdev"), NULL);
62 		if (rv == 0 && path != NULL)
63 			*path = devspec;
64 		return (rv);
65 	}
66 
67 	/* Parse the device name off the beginning of the devspec. */
68 	return (efi_parsedev(dev, devspec, path));
69 }
70 
71 /*
72  * Point (dev) at an allocated device specifier matching the string version
73  * at the beginning of (devspec).  Return a pointer to the remaining
74  * text in (path).
75  *
76  * In all cases, the beginning of (devspec) is compared to the names
77  * of known devices in the device switch, and then any following text
78  * is parsed according to the rules applied to the device type.
79  *
80  * For disk-type devices, the syntax is:
81  *
82  * fs<unit>:
83  */
84 static int
85 efi_parsedev(struct devdesc **dev, const char *devspec, const char **path)
86 {
87 	struct devdesc *idev;
88 	struct devsw *dv;
89 	int i, unit, err;
90 	char *cp;
91 	const char *np;
92 
93 	/* minimum length check */
94 	if (strlen(devspec) < 2)
95 		return (EINVAL);
96 
97 	/* look for a device that matches */
98 	for (i = 0; devsw[i] != NULL; i++) {
99 		dv = devsw[i];
100 		if (strncmp(devspec, dv->dv_name, strlen(dv->dv_name)) == 0)
101 			break;
102 	}
103 	if (devsw[i] == NULL)
104 		return (ENOENT);
105 
106 	np = devspec + strlen(dv->dv_name);
107 	idev = NULL;
108 	err = 0;
109 
110 	switch (dv->dv_type) {
111 	case DEVT_NONE:
112 		break;
113 
114 	case DEVT_DISK:
115 		idev = malloc(sizeof (struct disk_devdesc));
116 		if (idev == NULL)
117 			return (ENOMEM);
118 
119 		err = disk_parsedev((struct disk_devdesc *)idev, np, path);
120 		if (err != 0)
121 			goto fail;
122 		break;
123 
124 	case DEVT_ZFS:
125 		idev = malloc(sizeof (struct zfs_devdesc));
126 		if (idev == NULL)
127 			return (ENOMEM);
128 
129 		err = zfs_parsedev((struct zfs_devdesc *)idev, np, path);
130 		if (err != 0)
131 			goto fail;
132 		break;
133 
134 	default:
135 		idev = malloc(sizeof (struct devdesc));
136 		if (idev == NULL)
137 			return (ENOMEM);
138 
139 		unit = 0;
140 		cp = (char *)np;
141 
142 		if (*np != '\0' && *np != ':') {
143 			/* get unit number if present */
144 			errno = 0;
145 			unit = strtol(np, &cp, 0);
146 			if (errno != 0 || cp == np) {
147 				err = EUNIT;
148 				goto fail;
149 			}
150 		}
151 		if (*cp != '\0' && *cp != ':') {
152 			err = EINVAL;
153 			goto fail;
154 		}
155 
156 		idev->d_unit = unit;
157 		if (path != NULL)
158 			*path = (*cp == '\0') ? cp : cp + 1;
159 		break;
160 	}
161 
162 	idev->d_dev = dv;
163 	idev->d_type = dv->dv_type;
164 
165 	if (dev != NULL)
166 		*dev = idev;
167 	else
168 		free(idev);
169 	return (0);
170 
171 fail:
172 	free(idev);
173 	return (err);
174 }
175 
176 char *
177 efi_fmtdev(void *vdev)
178 {
179 	struct devdesc *dev = (struct devdesc *)vdev;
180 	static char buf[SPECNAMELEN + 1];
181 
182 	switch (dev->d_type) {
183 	case DEVT_NONE:
184 		strlcpy(buf, "(no device)", sizeof (buf));
185 		break;
186 
187 	case DEVT_DISK:
188 		return (disk_fmtdev(vdev));
189 
190 	case DEVT_ZFS:
191 		return (zfs_fmtdev(dev));
192 
193 	default:
194 		snprintf(buf, sizeof (buf), "%s%d:", dev->d_dev->dv_name,
195 		    dev->d_unit);
196 		break;
197 	}
198 
199 	return (buf);
200 }
201 
202 /*
203  * Set currdev to suit the value being supplied in (value)
204  */
205 int
206 efi_setcurrdev(struct env_var *ev, int flags, const void *value)
207 {
208 	struct devdesc *ncurr;
209 	int rv;
210 
211 	rv = efi_parsedev(&ncurr, value, NULL);
212 	if (rv != 0)
213 		return (rv);
214 
215 	free(ncurr);
216 	env_setenv(ev->ev_name, flags | EV_NOHOOK, value, NULL, NULL);
217 	return (0);
218 }
219