xref: /freebsd/stand/efi/libefi/devpath.c (revision b9e19b07)
1ca987d46SWarner Losh /*-
2ca987d46SWarner Losh  * Copyright (c) 2016 John Baldwin <jhb@FreeBSD.org>
3ca987d46SWarner Losh  *
4ca987d46SWarner Losh  * Redistribution and use in source and binary forms, with or without
5ca987d46SWarner Losh  * modification, are permitted provided that the following conditions
6ca987d46SWarner Losh  * are met:
7ca987d46SWarner Losh  * 1. Redistributions of source code must retain the above copyright
8ca987d46SWarner Losh  *    notice, this list of conditions and the following disclaimer.
9ca987d46SWarner Losh  * 2. Redistributions in binary form must reproduce the above copyright
10ca987d46SWarner Losh  *    notice, this list of conditions and the following disclaimer in the
11ca987d46SWarner Losh  *    documentation and/or other materials provided with the distribution.
12ca987d46SWarner Losh  *
13ca987d46SWarner Losh  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
14ca987d46SWarner Losh  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
15ca987d46SWarner Losh  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
16ca987d46SWarner Losh  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
17ca987d46SWarner Losh  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
18ca987d46SWarner Losh  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
19ca987d46SWarner Losh  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
20ca987d46SWarner Losh  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
21ca987d46SWarner Losh  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
22ca987d46SWarner Losh  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
23ca987d46SWarner Losh  * SUCH DAMAGE.
24ca987d46SWarner Losh  */
25ca987d46SWarner Losh 
26ca987d46SWarner Losh #include <sys/cdefs.h>
27ca987d46SWarner Losh __FBSDID("$FreeBSD$");
28ca987d46SWarner Losh 
29ca987d46SWarner Losh #include <efi.h>
30ca987d46SWarner Losh #include <efilib.h>
3116b07b25SWarner Losh #include <efichar.h>
32ca987d46SWarner Losh 
33ca987d46SWarner Losh static EFI_GUID ImageDevicePathGUID =
34ca987d46SWarner Losh     EFI_LOADED_IMAGE_DEVICE_PATH_PROTOCOL_GUID;
35ca987d46SWarner Losh static EFI_GUID DevicePathGUID = DEVICE_PATH_PROTOCOL;
36ca987d46SWarner Losh static EFI_GUID DevicePathToTextGUID = EFI_DEVICE_PATH_TO_TEXT_PROTOCOL_GUID;
3716b07b25SWarner Losh static EFI_DEVICE_PATH_TO_TEXT_PROTOCOL *toTextProtocol;
3816b07b25SWarner Losh static EFI_GUID DevicePathFromTextGUID = EFI_DEVICE_PATH_FROM_TEXT_PROTOCOL_GUID;
3916b07b25SWarner Losh static EFI_DEVICE_PATH_FROM_TEXT_PROTOCOL *fromTextProtocol;
40ca987d46SWarner Losh 
41ca987d46SWarner Losh EFI_DEVICE_PATH *
42ca987d46SWarner Losh efi_lookup_image_devpath(EFI_HANDLE handle)
43ca987d46SWarner Losh {
44ca987d46SWarner Losh 	EFI_DEVICE_PATH *devpath;
45ca987d46SWarner Losh 	EFI_STATUS status;
46ca987d46SWarner Losh 
47ca987d46SWarner Losh 	status = BS->HandleProtocol(handle, &ImageDevicePathGUID,
48ca987d46SWarner Losh 	    (VOID **)&devpath);
49ca987d46SWarner Losh 	if (EFI_ERROR(status))
50ca987d46SWarner Losh 		devpath = NULL;
51ca987d46SWarner Losh 	return (devpath);
52ca987d46SWarner Losh }
53ca987d46SWarner Losh 
54ca987d46SWarner Losh EFI_DEVICE_PATH *
55ca987d46SWarner Losh efi_lookup_devpath(EFI_HANDLE handle)
56ca987d46SWarner Losh {
57ca987d46SWarner Losh 	EFI_DEVICE_PATH *devpath;
58ca987d46SWarner Losh 	EFI_STATUS status;
59ca987d46SWarner Losh 
60ca987d46SWarner Losh 	status = BS->HandleProtocol(handle, &DevicePathGUID, (VOID **)&devpath);
61ca987d46SWarner Losh 	if (EFI_ERROR(status))
62ca987d46SWarner Losh 		devpath = NULL;
63ca987d46SWarner Losh 	return (devpath);
64ca987d46SWarner Losh }
65ca987d46SWarner Losh 
66ca987d46SWarner Losh CHAR16 *
67ca987d46SWarner Losh efi_devpath_name(EFI_DEVICE_PATH *devpath)
68ca987d46SWarner Losh {
69ca987d46SWarner Losh 	EFI_STATUS status;
70ca987d46SWarner Losh 
71ca987d46SWarner Losh 	if (devpath == NULL)
72ca987d46SWarner Losh 		return (NULL);
7316b07b25SWarner Losh 	if (toTextProtocol == NULL) {
74ca987d46SWarner Losh 		status = BS->LocateProtocol(&DevicePathToTextGUID, NULL,
7516b07b25SWarner Losh 		    (VOID **)&toTextProtocol);
76ca987d46SWarner Losh 		if (EFI_ERROR(status))
7716b07b25SWarner Losh 			toTextProtocol = NULL;
78ca987d46SWarner Losh 	}
7916b07b25SWarner Losh 	if (toTextProtocol == NULL)
80ca987d46SWarner Losh 		return (NULL);
81ca987d46SWarner Losh 
8216b07b25SWarner Losh 	return (toTextProtocol->ConvertDevicePathToText(devpath, TRUE, TRUE));
83ca987d46SWarner Losh }
84ca987d46SWarner Losh 
85ca987d46SWarner Losh void
86ca987d46SWarner Losh efi_free_devpath_name(CHAR16 *text)
87ca987d46SWarner Losh {
88ca987d46SWarner Losh 
89ca987d46SWarner Losh 	BS->FreePool(text);
90ca987d46SWarner Losh }
91ca987d46SWarner Losh 
92ca987d46SWarner Losh EFI_DEVICE_PATH *
9316b07b25SWarner Losh efi_name_to_devpath(const char *path)
9416b07b25SWarner Losh {
9516b07b25SWarner Losh 	EFI_DEVICE_PATH *devpath;
9616b07b25SWarner Losh 	CHAR16 *uv;
9716b07b25SWarner Losh 	size_t ul;
9816b07b25SWarner Losh 
9916b07b25SWarner Losh 	uv = NULL;
10016b07b25SWarner Losh 	if (utf8_to_ucs2(path, &uv, &ul) != 0)
10116b07b25SWarner Losh 		return (NULL);
10216b07b25SWarner Losh 	devpath = efi_name_to_devpath16(uv);
10316b07b25SWarner Losh 	free(uv);
10416b07b25SWarner Losh 	return (devpath);
10516b07b25SWarner Losh }
10616b07b25SWarner Losh 
10716b07b25SWarner Losh EFI_DEVICE_PATH *
10816b07b25SWarner Losh efi_name_to_devpath16(CHAR16 *path)
10916b07b25SWarner Losh {
11016b07b25SWarner Losh 	EFI_STATUS status;
11116b07b25SWarner Losh 
11216b07b25SWarner Losh 	if (path == NULL)
11316b07b25SWarner Losh 		return (NULL);
11416b07b25SWarner Losh 	if (fromTextProtocol == NULL) {
11516b07b25SWarner Losh 		status = BS->LocateProtocol(&DevicePathFromTextGUID, NULL,
11616b07b25SWarner Losh 		    (VOID **)&fromTextProtocol);
11716b07b25SWarner Losh 		if (EFI_ERROR(status))
11816b07b25SWarner Losh 			fromTextProtocol = NULL;
11916b07b25SWarner Losh 	}
12016b07b25SWarner Losh 	if (fromTextProtocol == NULL)
12116b07b25SWarner Losh 		return (NULL);
12216b07b25SWarner Losh 
12316b07b25SWarner Losh 	return (fromTextProtocol->ConvertTextToDevicePath(path));
12416b07b25SWarner Losh }
12516b07b25SWarner Losh 
12616b07b25SWarner Losh void efi_devpath_free(EFI_DEVICE_PATH *devpath)
12716b07b25SWarner Losh {
12816b07b25SWarner Losh 
12916b07b25SWarner Losh 	BS->FreePool(devpath);
13016b07b25SWarner Losh }
13116b07b25SWarner Losh 
13216b07b25SWarner Losh EFI_DEVICE_PATH *
133ca987d46SWarner Losh efi_devpath_last_node(EFI_DEVICE_PATH *devpath)
134ca987d46SWarner Losh {
135ca987d46SWarner Losh 
136ca987d46SWarner Losh 	if (IsDevicePathEnd(devpath))
137ca987d46SWarner Losh 		return (NULL);
138ca987d46SWarner Losh 	while (!IsDevicePathEnd(NextDevicePathNode(devpath)))
139ca987d46SWarner Losh 		devpath = NextDevicePathNode(devpath);
140ca987d46SWarner Losh 	return (devpath);
141ca987d46SWarner Losh }
142ca987d46SWarner Losh 
143ca987d46SWarner Losh EFI_DEVICE_PATH *
144ca987d46SWarner Losh efi_devpath_trim(EFI_DEVICE_PATH *devpath)
145ca987d46SWarner Losh {
146ca987d46SWarner Losh 	EFI_DEVICE_PATH *node, *copy;
147ca987d46SWarner Losh 	size_t prefix, len;
148ca987d46SWarner Losh 
149ca987d46SWarner Losh 	if ((node = efi_devpath_last_node(devpath)) == NULL)
150ca987d46SWarner Losh 		return (NULL);
151ca987d46SWarner Losh 	prefix = (UINT8 *)node - (UINT8 *)devpath;
152ca987d46SWarner Losh 	if (prefix == 0)
153ca987d46SWarner Losh 		return (NULL);
154ca987d46SWarner Losh 	len = prefix + DevicePathNodeLength(NextDevicePathNode(node));
155ca987d46SWarner Losh 	copy = malloc(len);
156ca987d46SWarner Losh 	if (copy != NULL) {
157ca987d46SWarner Losh 		memcpy(copy, devpath, prefix);
158ca987d46SWarner Losh 		node = (EFI_DEVICE_PATH *)((UINT8 *)copy + prefix);
159ca987d46SWarner Losh 		SetDevicePathEndNode(node);
160ca987d46SWarner Losh 	}
161ca987d46SWarner Losh 	return (copy);
162ca987d46SWarner Losh }
163ca987d46SWarner Losh 
164ca987d46SWarner Losh EFI_HANDLE
165ca987d46SWarner Losh efi_devpath_handle(EFI_DEVICE_PATH *devpath)
166ca987d46SWarner Losh {
167ca987d46SWarner Losh 	EFI_STATUS status;
168ca987d46SWarner Losh 	EFI_HANDLE h;
169ca987d46SWarner Losh 
170ca987d46SWarner Losh 	/*
171ca987d46SWarner Losh 	 * There isn't a standard way to locate a handle for a given
172ca987d46SWarner Losh 	 * device path.  However, querying the EFI_DEVICE_PATH protocol
173ca987d46SWarner Losh 	 * for a given device path should give us a handle for the
174ca987d46SWarner Losh 	 * closest node in the path to the end that is valid.
175ca987d46SWarner Losh 	 */
176ca987d46SWarner Losh 	status = BS->LocateDevicePath(&DevicePathGUID, &devpath, &h);
177ca987d46SWarner Losh 	if (EFI_ERROR(status))
178ca987d46SWarner Losh 		return (NULL);
179ca987d46SWarner Losh 	return (h);
180ca987d46SWarner Losh }
181ca987d46SWarner Losh 
182ca987d46SWarner Losh bool
18313850b36SWarner Losh efi_devpath_match_node(EFI_DEVICE_PATH *devpath1, EFI_DEVICE_PATH *devpath2)
184ca987d46SWarner Losh {
185ca987d46SWarner Losh 	size_t len;
186ca987d46SWarner Losh 
187ca987d46SWarner Losh 	if (devpath1 == NULL || devpath2 == NULL)
188ca987d46SWarner Losh 		return (false);
189ca987d46SWarner Losh 	if (DevicePathType(devpath1) != DevicePathType(devpath2) ||
190ca987d46SWarner Losh 	    DevicePathSubType(devpath1) != DevicePathSubType(devpath2))
191ca987d46SWarner Losh 		return (false);
192ca987d46SWarner Losh 	len = DevicePathNodeLength(devpath1);
193ca987d46SWarner Losh 	if (len != DevicePathNodeLength(devpath2))
194ca987d46SWarner Losh 		return (false);
195ca987d46SWarner Losh 	if (memcmp(devpath1, devpath2, len) != 0)
196ca987d46SWarner Losh 		return (false);
19713850b36SWarner Losh 	return (true);
19813850b36SWarner Losh }
199ca987d46SWarner Losh 
20013850b36SWarner Losh bool
20113850b36SWarner Losh efi_devpath_match(EFI_DEVICE_PATH *devpath1, EFI_DEVICE_PATH *devpath2)
20213850b36SWarner Losh {
20313850b36SWarner Losh 
20413850b36SWarner Losh 	if (devpath1 == NULL || devpath2 == NULL)
20513850b36SWarner Losh 		return (false);
20613850b36SWarner Losh 
20713850b36SWarner Losh 	while (true) {
20813850b36SWarner Losh 		if (!efi_devpath_match_node(devpath1, devpath2))
20913850b36SWarner Losh 			return false;
210ca987d46SWarner Losh 		if (IsDevicePathEnd(devpath1))
211ca987d46SWarner Losh 			break;
212ca987d46SWarner Losh 		devpath1 = NextDevicePathNode(devpath1);
213ca987d46SWarner Losh 		devpath2 = NextDevicePathNode(devpath2);
214ca987d46SWarner Losh 	}
215ca987d46SWarner Losh 	return (true);
216ca987d46SWarner Losh }
217ca987d46SWarner Losh 
218ca987d46SWarner Losh bool
219ca987d46SWarner Losh efi_devpath_is_prefix(EFI_DEVICE_PATH *prefix, EFI_DEVICE_PATH *path)
220ca987d46SWarner Losh {
221ca987d46SWarner Losh 	size_t len;
222ca987d46SWarner Losh 
223ca987d46SWarner Losh 	if (prefix == NULL || path == NULL)
224ca987d46SWarner Losh 		return (false);
225ca987d46SWarner Losh 
226ca987d46SWarner Losh 	while (1) {
227ca987d46SWarner Losh 		if (IsDevicePathEnd(prefix))
228ca987d46SWarner Losh 			break;
229ca987d46SWarner Losh 
230ca987d46SWarner Losh 		if (DevicePathType(prefix) != DevicePathType(path) ||
231ca987d46SWarner Losh 		    DevicePathSubType(prefix) != DevicePathSubType(path))
232ca987d46SWarner Losh 			return (false);
233ca987d46SWarner Losh 
234ca987d46SWarner Losh 		len = DevicePathNodeLength(prefix);
235ca987d46SWarner Losh 		if (len != DevicePathNodeLength(path))
236ca987d46SWarner Losh 			return (false);
237ca987d46SWarner Losh 
238ca987d46SWarner Losh 		if (memcmp(prefix, path, len) != 0)
239ca987d46SWarner Losh 			return (false);
240ca987d46SWarner Losh 
241ca987d46SWarner Losh 		prefix = NextDevicePathNode(prefix);
242ca987d46SWarner Losh 		path = NextDevicePathNode(path);
243ca987d46SWarner Losh 	}
244ca987d46SWarner Losh 	return (true);
245ca987d46SWarner Losh }
246ee4e1d58SWarner Losh 
247ee4e1d58SWarner Losh /*
248ee4e1d58SWarner Losh  * Skip over the 'prefix' part of path and return the part of the path
249ee4e1d58SWarner Losh  * that starts with the first node that's a MEDIA_DEVICE_PATH.
250ee4e1d58SWarner Losh  */
251ee4e1d58SWarner Losh EFI_DEVICE_PATH *
252ee4e1d58SWarner Losh efi_devpath_to_media_path(EFI_DEVICE_PATH *path)
253ee4e1d58SWarner Losh {
254ee4e1d58SWarner Losh 
255ee4e1d58SWarner Losh 	while (!IsDevicePathEnd(path)) {
256ee4e1d58SWarner Losh 		if (DevicePathType(path) == MEDIA_DEVICE_PATH)
257ee4e1d58SWarner Losh 			return (path);
258ee4e1d58SWarner Losh 		path = NextDevicePathNode(path);
259ee4e1d58SWarner Losh 	}
260ee4e1d58SWarner Losh 	return (NULL);
261ee4e1d58SWarner Losh }
262c6c2a73cSWarner Losh 
263c6c2a73cSWarner Losh UINTN
264c6c2a73cSWarner Losh efi_devpath_length(EFI_DEVICE_PATH  *path)
265c6c2a73cSWarner Losh {
266c6c2a73cSWarner Losh 	EFI_DEVICE_PATH *start = path;
267c6c2a73cSWarner Losh 
268c6c2a73cSWarner Losh 	while (!IsDevicePathEnd(path))
269c6c2a73cSWarner Losh 		path = NextDevicePathNode(path);
270c6c2a73cSWarner Losh 	return ((UINTN)path - (UINTN)start) + DevicePathNodeLength(path);
271c6c2a73cSWarner Losh }
272b9e19b07SWarner Losh 
273b9e19b07SWarner Losh EFI_HANDLE
274b9e19b07SWarner Losh efi_devpath_to_handle(EFI_DEVICE_PATH *path, EFI_HANDLE *handles, unsigned nhandles)
275b9e19b07SWarner Losh {
276b9e19b07SWarner Losh 	unsigned i;
277b9e19b07SWarner Losh 	EFI_DEVICE_PATH *media, *devpath;
278b9e19b07SWarner Losh 	EFI_HANDLE h;
279b9e19b07SWarner Losh 
280b9e19b07SWarner Losh 	media = efi_devpath_to_media_path(path);
281b9e19b07SWarner Losh 	if (media == NULL)
282b9e19b07SWarner Losh 		return (NULL);
283b9e19b07SWarner Losh 	for (i = 0; i < nhandles; i++) {
284b9e19b07SWarner Losh 		h = handles[i];
285b9e19b07SWarner Losh 		devpath = efi_lookup_devpath(h);
286b9e19b07SWarner Losh 		if (devpath == NULL)
287b9e19b07SWarner Losh 			continue;
288b9e19b07SWarner Losh 		if (!efi_devpath_match_node(media, efi_devpath_to_media_path(devpath)))
289b9e19b07SWarner Losh 			continue;
290b9e19b07SWarner Losh 		return (h);
291b9e19b07SWarner Losh 	}
292b9e19b07SWarner Losh 	return (NULL);
293b9e19b07SWarner Losh }
294